Lely core libraries  1.9.2
nmt.c
Go to the documentation of this file.
1 
24 #include "co.h"
25 #include <lely/util/diag.h>
26 #ifndef LELY_NO_CO_MASTER
27 #include <lely/can/buf.h>
28 #include <lely/co/csdo.h>
29 #include <lely/util/time.h>
30 #endif
31 #include <lely/co/dev.h>
32 #ifndef LELY_NO_CO_EMCY
33 #include <lely/co/emcy.h>
34 #endif
35 #include <lely/co/nmt.h>
36 #include <lely/co/obj.h>
37 #ifndef LELY_NO_CO_RPDO
38 #include <lely/co/rpdo.h>
39 #endif
40 #include <lely/co/sdo.h>
41 #ifndef LELY_NO_CO_TPDO
42 #include <lely/co/tpdo.h>
43 #endif
44 #include <lely/co/val.h>
45 #ifndef LELY_NO_CO_MASTER
46 #include "nmt_boot.h"
47 #include "nmt_cfg.h"
48 #endif
49 #include "nmt_hb.h"
50 #include "nmt_srv.h"
51 
52 #include <assert.h>
53 #include <stdlib.h>
54 
55 struct __co_nmt_state;
57 typedef const struct __co_nmt_state co_nmt_state_t;
58 
59 #ifndef LELY_NO_CO_MASTER
60 struct co_nmt_slave {
72  co_unsigned32_t assignment;
74  co_unsigned8_t est;
76  co_unsigned8_t rst;
78  char es;
80  int booted;
88  void *cfg_data;
90  co_unsigned16_t gt;
92  co_unsigned8_t ltf;
94  co_unsigned8_t rtr;
99  int ng_state;
100 };
101 #endif
102 
104 struct __co_nmt {
110  co_unsigned8_t id;
112  void *dcf_node;
114  void *dcf_comm;
118  struct co_nmt_srv srv;
120  co_unsigned32_t startup;
121 #ifndef LELY_NO_CO_MASTER
122  int master;
124 #endif
130  void *cs_data;
133 #ifndef LELY_NO_CO_MASTER
137  void *ng_data;
138 #endif
139 
144  co_unsigned8_t st;
146  co_unsigned16_t gt;
148  co_unsigned8_t ltf;
153  int lg_state;
157  void *lg_data;
159  co_unsigned16_t ms;
163  co_unsigned8_t nhb;
167  void *hb_data;
171  void *st_data;
172 #ifndef LELY_NO_CO_MASTER
173  struct can_buf buf;
176  struct timespec inhibit;
179 #ifndef LELY_NO_CO_LSS
183  void *lss_data;
184 #endif
185 
189  int halt;
196  int timeout;
200  void *boot_data;
204  void *cfg_data;
208  void *dn_data;
212  void *up_data;
213 #endif
217  void *sync_data;
218 };
219 
225 static co_unsigned32_t co_100c_dn_ind(
226  co_sub_t *sub, struct co_sdo_req *req, void *data);
227 
233 static co_unsigned32_t co_100d_dn_ind(
234  co_sub_t *sub, struct co_sdo_req *req, void *data);
235 
242 static co_unsigned32_t co_1016_dn_ind(
243  co_sub_t *sub, struct co_sdo_req *req, void *data);
244 
251 static co_unsigned32_t co_1017_dn_ind(
252  co_sub_t *sub, struct co_sdo_req *req, void *data);
253 
254 #ifndef LELY_NO_CO_MASTER
255 
261 static co_unsigned32_t co_1f25_dn_ind(
262  co_sub_t *sub, struct co_sdo_req *req, void *data);
263 #endif
264 
271 static co_unsigned32_t co_1f80_dn_ind(
272  co_sub_t *sub, struct co_sdo_req *req, void *data);
273 
274 #ifndef LELY_NO_CO_MASTER
275 
281 static co_unsigned32_t co_1f82_dn_ind(
282  co_sub_t *sub, struct co_sdo_req *req, void *data);
283 #endif
284 
286 static int co_nmt_recv_000(const struct can_msg *msg, void *data);
287 
295 static int co_nmt_recv_700(const struct can_msg *msg, void *data);
296 
297 #ifndef LELY_NO_CO_MASTER
298 static int co_nmt_ng_timer(const struct timespec *tp, void *data);
300 #endif
301 
307 static int co_nmt_ec_timer(const struct timespec *tp, void *data);
308 
309 #ifndef LELY_NO_CO_MASTER
310 
315 static int co_nmt_cs_timer(const struct timespec *tp, void *data);
316 #endif
317 
325 static void co_nmt_st_ind(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st);
326 
327 #ifndef LELY_NO_CO_MASTER
328 static void default_ng_ind(co_nmt_t *nmt, co_unsigned8_t id, int state,
330  int reason, void *data);
331 #endif
332 
334 static void default_lg_ind(co_nmt_t *nmt, int state, void *data);
335 
337 static void default_hb_ind(co_nmt_t *nmt, co_unsigned8_t id, int state,
338  int reason, void *data);
339 
341 static void default_st_ind(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st,
342  void *data);
343 
344 #ifndef LELY_NO_CO_MASTER
345 
347 static void co_nmt_dn_ind(const co_csdo_t *sdo, co_unsigned16_t idx,
348  co_unsigned8_t subidx, size_t size, size_t nbyte, void *data);
349 
351 static void co_nmt_up_ind(const co_csdo_t *sdo, co_unsigned16_t idx,
352  co_unsigned8_t subidx, size_t size, size_t nbyte, void *data);
353 
354 #endif
355 
360 static void co_nmt_enter(co_nmt_t *nmt, co_nmt_state_t *next);
361 
371 static inline void co_nmt_emit_cs(co_nmt_t *nmt, co_unsigned8_t cs);
372 
373 #ifndef LELY_NO_CO_MASTER
374 
383 static inline void co_nmt_emit_boot(
384  co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es);
385 #endif
386 
390  co_nmt_state_t *(*on_enter)(co_nmt_t *nmt);
402  co_nmt_state_t *(*on_cs)(co_nmt_t *nmt, co_unsigned8_t cs);
403 #ifndef LELY_NO_CO_MASTER
404 
416  co_nmt_state_t *(*on_boot)(co_nmt_t *nmt, co_unsigned8_t id,
417  co_unsigned8_t st, char es);
418 #endif
419  void (*on_leave)(co_nmt_t *nmt);
421 };
422 
423 #define LELY_CO_DEFINE_STATE(name, ...) \
424  static co_nmt_state_t *const name = &(co_nmt_state_t){ __VA_ARGS__ };
425 
426 #ifndef LELY_NO_CO_MASTER
429  co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es);
430 #endif
431 
433 static co_nmt_state_t *co_nmt_init_on_cs(co_nmt_t *nmt, co_unsigned8_t cs);
434 
436 // clang-format off
437 LELY_CO_DEFINE_STATE(co_nmt_init_state,
438  .on_cs = &co_nmt_init_on_cs
439 )
440 // clang-format on
441 
444 
446 // clang-format off
447 LELY_CO_DEFINE_STATE(co_nmt_reset_node_state,
448  .on_enter = &co_nmt_reset_node_on_enter
449 )
450 // clang-format on
451 
454 
460  co_nmt_t *nmt, co_unsigned8_t cs);
461 
463 // clang-format off
464 LELY_CO_DEFINE_STATE(co_nmt_reset_comm_state,
465  .on_enter = &co_nmt_reset_comm_on_enter,
466  .on_cs = &co_nmt_reset_comm_on_cs
467 )
468 // clang-format on
469 
472 
474 static co_nmt_state_t *co_nmt_bootup_on_cs(co_nmt_t *nmt, co_unsigned8_t cs);
475 
477 // clang-format off
478 LELY_CO_DEFINE_STATE(co_nmt_bootup_state,
479  .on_enter = &co_nmt_bootup_on_enter,
480  .on_cs = &co_nmt_bootup_on_cs
481 )
482 // clang-format on
483 
486 
491 static co_nmt_state_t *co_nmt_preop_on_cs(co_nmt_t *nmt, co_unsigned8_t cs);
492 
493 #ifndef LELY_NO_CO_MASTER
494 
499  co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es);
500 #endif
501 
503 // clang-format off
504 #ifdef LELY_NO_CO_MASTER
505 LELY_CO_DEFINE_STATE(co_nmt_preop_state,
506  .on_enter = &co_nmt_preop_on_enter,
507  .on_cs = &co_nmt_preop_on_cs
508 )
509 #else
510 LELY_CO_DEFINE_STATE(co_nmt_preop_state,
511  .on_enter = &co_nmt_preop_on_enter,
512  .on_cs = &co_nmt_preop_on_cs,
513  .on_boot = &co_nmt_preop_on_boot
514 )
515 #endif
516 // clang-format on
517 
520 
522 static co_nmt_state_t *co_nmt_start_on_cs(co_nmt_t *nmt, co_unsigned8_t cs);
523 
525 // clang-format off
526 #ifdef LELY_NO_CO_MASTER
527 LELY_CO_DEFINE_STATE(co_nmt_start_state,
528  .on_enter = &co_nmt_start_on_enter,
529  .on_cs = &co_nmt_start_on_cs
530 )
531 #else
532 LELY_CO_DEFINE_STATE(co_nmt_start_state,
533  .on_enter = &co_nmt_start_on_enter,
534  .on_cs = &co_nmt_start_on_cs,
535  .on_boot = &co_nmt_default_on_boot
536 )
537 #endif
538 // clang-format on
539 
542 
544 static co_nmt_state_t *co_nmt_stop_on_cs(co_nmt_t *nmt, co_unsigned8_t cs);
545 
547 // clang-format off
548 #ifdef LELY_NO_CO_MASTER
549 LELY_CO_DEFINE_STATE(co_nmt_stop_state,
550  .on_enter = &co_nmt_stop_on_enter,
551  .on_cs = &co_nmt_stop_on_cs
552 )
553 #else
554 LELY_CO_DEFINE_STATE(co_nmt_stop_state,
555  .on_enter = &co_nmt_stop_on_enter,
556  .on_cs = &co_nmt_stop_on_cs,
557  .on_boot = &co_nmt_default_on_boot
558 )
559 #endif
560 // clang-format on
561 
562 #undef LELY_CO_DEFINE_STATE
563 
566 
567 #ifndef LELY_NO_CO_MASTER
570 #endif
571 
574 
576 static void co_nmt_ec_init(co_nmt_t *nmt);
577 
579 static void co_nmt_ec_fini(co_nmt_t *nmt);
580 
589 static int co_nmt_ec_update(co_nmt_t *nmt);
590 
599 static int co_nmt_ec_send_res(co_nmt_t *nmt, co_unsigned8_t st);
600 
602 static void co_nmt_hb_init(co_nmt_t *nmt);
603 
605 static void co_nmt_hb_fini(co_nmt_t *nmt);
606 
607 #ifndef LELY_NO_CO_MASTER
608 
610 static void co_nmt_slaves_init(co_nmt_t *nmt);
611 
613 static void co_nmt_slaves_fini(co_nmt_t *nmt);
614 
621 static int co_nmt_slaves_boot(co_nmt_t *nmt);
622 
623 #endif
624 
626 #define CO_NMT_PREOP_SRV \
627  (CO_NMT_STOP_SRV | CO_NMT_SRV_SDO | CO_NMT_SRV_SYNC | CO_NMT_SRV_TIME \
628  | CO_NMT_SRV_EMCY)
629 
631 #define CO_NMT_START_SRV (CO_NMT_PREOP_SRV | CO_NMT_SRV_PDO)
632 
634 #define CO_NMT_STOP_SRV CO_NMT_SRV_LSS
635 
636 co_unsigned32_t
637 co_dev_cfg_hb(co_dev_t *dev, co_unsigned8_t id, co_unsigned16_t ms)
638 {
639  assert(dev);
640 
641  co_obj_t *obj_1016 = co_dev_find_obj(dev, 0x1016);
642  if (__unlikely(!obj_1016))
643  return CO_SDO_AC_NO_OBJ;
644 
645  co_unsigned8_t n = co_obj_get_val_u8(obj_1016, 0x00);
646  co_unsigned8_t i = 0;
647  // If the node-ID is valid, find an existing heartbeat consumer with the
648  // same ID.
649  if (id && id <= CO_NUM_NODES) {
650  for (i = 1; i <= n; i++) {
651  co_unsigned32_t val_i = co_obj_get_val_u32(obj_1016, i);
652  co_unsigned8_t id_i = (val_i >> 16) & 0xff;
653  if (id_i == id)
654  break;
655  }
656  }
657  // If the node-ID is invalid or no heartbeat consumer exists, find an
658  // unused consumer.
659  if (!i || i > n) {
660  for (i = 1; i <= n; i++) {
661  co_unsigned32_t val_i = co_obj_get_val_u32(obj_1016, i);
662  co_unsigned8_t id_i = (val_i >> 16) & 0xff;
663  if (!id_i || id_i > CO_NUM_NODES)
664  break;
665  }
666  }
667 
668  if (__unlikely(!i || i > n))
669  return CO_SDO_AC_NO_SUB;
670  co_sub_t *sub = co_obj_find_sub(obj_1016, i);
671  if (__unlikely(!sub))
672  return CO_SDO_AC_NO_SUB;
673 
674  co_unsigned32_t val = ((co_unsigned32_t)id << 16) | ms;
675  return co_sub_dn_ind_val(sub, CO_DEFTYPE_UNSIGNED32, &val);
676 }
677 
678 const char *
680 {
681  switch (es) {
682  case 'A': return "The CANopen device is not listed in object 1F81.";
683  case 'B':
684  return "No response received for upload request of object 1000.";
685  case 'C':
686  return "Value of object 1000 from CANopen device is different to value in object 1F84 (Device type).";
687  case 'D':
688  return "Value of object 1018 sub-index 01 from CANopen device is different to value in object 1F85 (Vendor-ID).";
689  case 'E':
690  return "Heartbeat event. No heartbeat message received from CANopen device.";
691  case 'F':
692  return "Node guarding event. No confirmation for guarding request received from CANopen device.";
693  case 'G':
694  return "Objects for program download are not configured or inconsistent.";
695  case 'H':
696  return "Software update is required, but not allowed because of configuration or current status.";
697  case 'I':
698  return "Software update is required, but program download failed.";
699  case 'J': return "Configuration download failed.";
700  case 'K':
701  return "Heartbeat event during start error control service. No heartbeat message received from CANopen device during start error control service.";
702  case 'L': return "NMT slave was initially operational.";
703  case 'M':
704  return "Value of object 1018 sub-index 02 from CANopen device is different to value in object 1F86 (Product code).";
705  case 'N':
706  return "Value of object 1018 sub-index 03 from CANopen device is different to value in object 1F87 (Revision number).";
707  case 'O':
708  return "Value of object 1018 sub-index 04 from CANopen device is different to value in object 1F88 (Serial number).";
709  default: return "Unknown error status";
710  }
711 }
712 
713 void *
714 __co_nmt_alloc(void)
715 {
716  void *ptr = malloc(sizeof(struct __co_nmt));
717  if (__unlikely(!ptr))
718  set_errc(errno2c(errno));
719  return ptr;
720 }
721 
722 void
723 __co_nmt_free(void *ptr)
724 {
725  free(ptr);
726 }
727 
728 struct __co_nmt *
729 __co_nmt_init(struct __co_nmt *nmt, can_net_t *net, co_dev_t *dev)
730 {
731  assert(nmt);
732  assert(net);
733  assert(dev);
734 
735  int errc = 0;
736 
737  nmt->net = net;
738  nmt->dev = dev;
739 
740  nmt->id = co_dev_get_id(nmt->dev);
741 
742  // Store a concise DCF containing the application parameters.
743  // clang-format off
744  if (__unlikely(co_dev_write_dcf(nmt->dev, 0x2000, 0x9fff,
745  &nmt->dcf_node) == -1)) {
746  // clang-format on
747  errc = get_errc();
748  goto error_write_dcf_node;
749  }
750 
751  // Store a concise DCF containing the communication parameters.
752  // clang-format off
753  if (__unlikely(co_dev_write_dcf(nmt->dev, 0x1000, 0x1fff,
754  &nmt->dcf_comm) == -1)) {
755  // clang-format on
756  errc = get_errc();
757  goto error_write_dcf_comm;
758  }
759 
760  nmt->state = NULL;
761 
762  co_nmt_srv_init(&nmt->srv, nmt);
763 
764  nmt->startup = 0;
765 #ifndef LELY_NO_CO_MASTER
766  nmt->master = 0;
767 #endif
768 
769  // Create the CAN frame receiver for NMT messages.
770  nmt->recv_000 = can_recv_create();
771  if (__unlikely(!nmt->recv_000)) {
772  errc = get_errc();
773  goto error_create_recv_000;
774  }
776 
777  nmt->cs_ind = NULL;
778  nmt->cs_data = NULL;
779 
780  nmt->recv_700 = NULL;
781 
782 #ifndef LELY_NO_CO_MASTER
783  nmt->ng_ind = &default_ng_ind;
784  nmt->ng_data = NULL;
785 #endif
786 
787  nmt->ec_timer = NULL;
788 
789  nmt->st = CO_NMT_ST_BOOTUP;
790  nmt->gt = 0;
791  nmt->ltf = 0;
792 
794  nmt->lg_ind = &default_lg_ind;
795  nmt->lg_data = NULL;
796 
797  nmt->ms = 0;
798 
799  nmt->hbs = NULL;
800  nmt->nhb = 0;
801  nmt->hb_ind = &default_hb_ind;
802  nmt->hb_data = NULL;
803 
804  nmt->st_ind = &default_st_ind;
805  nmt->st_data = NULL;
806 
807 #ifndef LELY_NO_CO_MASTER
808  nmt->buf = (struct can_buf)CAN_BUF_INIT;
809  can_net_get_time(nmt->net, &nmt->inhibit);
810  nmt->cs_timer = NULL;
811 
812 #ifndef LELY_NO_CO_LSS
813  nmt->lss_req = NULL;
814  nmt->lss_data = NULL;
815 #endif
816 
817  nmt->halt = 0;
818 
819  for (co_unsigned8_t id = 1; id <= CO_NUM_NODES; id++) {
820  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
821  slave->nmt = nmt;
822 
823  slave->recv = NULL;
824  slave->timer = NULL;
825 
826  slave->assignment = 0;
827  slave->est = 0;
828  slave->rst = 0;
829  slave->es = 0;
830 
831  slave->booted = 0;
832  slave->boot = NULL;
833 
834  slave->cfg = NULL;
835  slave->cfg_con = NULL;
836  slave->cfg_data = NULL;
837 
838  slave->gt = 0;
839  slave->ltf = 0;
840  slave->rtr = 0;
841  slave->ng_state = CO_NMT_EC_RESOLVED;
842  }
843 
845 
846  nmt->boot_ind = NULL;
847  nmt->boot_data = NULL;
848  nmt->cfg_ind = NULL;
849  nmt->cfg_data = NULL;
850  nmt->dn_ind = NULL;
851  nmt->dn_data = NULL;
852  nmt->up_ind = NULL;
853  nmt->up_data = NULL;
854 #endif
855  nmt->sync_ind = NULL;
856  nmt->sync_data = NULL;
857 
858  // Set the download indication function for the guard time.
859  co_obj_t *obj_100c = co_dev_find_obj(nmt->dev, 0x100c);
860  if (obj_100c)
861  co_obj_set_dn_ind(obj_100c, &co_100c_dn_ind, nmt);
862 
863  // Set the download indication function for the life time factor.
864  co_obj_t *obj_100d = co_dev_find_obj(nmt->dev, 0x100d);
865  if (obj_100d)
866  co_obj_set_dn_ind(obj_100d, &co_100d_dn_ind, nmt);
867 
868  // Set the download indication function for the consumer heartbeat time.
869  co_obj_t *obj_1016 = co_dev_find_obj(nmt->dev, 0x1016);
870  if (obj_1016)
871  co_obj_set_dn_ind(obj_1016, &co_1016_dn_ind, nmt);
872 
873  // Set the download indication function for the producer heartbeat time.
874  co_obj_t *obj_1017 = co_dev_find_obj(nmt->dev, 0x1017);
875  if (obj_1017)
876  co_obj_set_dn_ind(obj_1017, &co_1017_dn_ind, nmt);
877 
878 #ifndef LELY_NO_CO_MASTER
879  // Set the download indication function for the configuration request
880  // value.
881  co_obj_t *obj_1f25 = co_dev_find_obj(nmt->dev, 0x1f25);
882  if (obj_1f25)
883  co_obj_set_dn_ind(obj_1f25, &co_1f25_dn_ind, nmt);
884 #endif
885 
886  // Set the download indication function for the NMT startup value.
887  co_obj_t *obj_1f80 = co_dev_find_obj(nmt->dev, 0x1f80);
888  if (obj_1f80)
889  co_obj_set_dn_ind(obj_1f80, &co_1f80_dn_ind, nmt);
890 
891 #ifndef LELY_NO_CO_MASTER
892  // Set the download indication function for the request NMT value.
893  co_obj_t *obj_1f82 = co_dev_find_obj(nmt->dev, 0x1f82);
894  if (obj_1f82)
895  co_obj_set_dn_ind(obj_1f82, &co_1f82_dn_ind, nmt);
896 #endif
897 
899  return nmt;
900 
901 #ifndef LELY_NO_CO_MASTER
902  if (obj_1f82)
903  co_obj_set_dn_ind(obj_1f82, NULL, NULL);
904 #endif
905  if (obj_1f80)
906  co_obj_set_dn_ind(obj_1f80, NULL, NULL);
907 #ifndef LELY_NO_CO_MASTER
908  if (obj_1f25)
909  co_obj_set_dn_ind(obj_1f25, NULL, NULL);
910 #endif
911  if (obj_1017)
912  co_obj_set_dn_ind(obj_1017, NULL, NULL);
913  if (obj_1016)
914  co_obj_set_dn_ind(obj_1016, NULL, NULL);
915  if (obj_100d)
916  co_obj_set_dn_ind(obj_100d, NULL, NULL);
917  if (obj_100c)
918  co_obj_set_dn_ind(obj_100c, NULL, NULL);
920 error_create_recv_000:
923 error_write_dcf_comm:
925 error_write_dcf_node:
926  set_errc(errc);
927  return NULL;
928 }
929 
930 void
931 __co_nmt_fini(struct __co_nmt *nmt)
932 {
933  assert(nmt);
934 
935 #ifndef LELY_NO_CO_MASTER
936  // Remove the download indication function for the request NMT value.
937  co_obj_t *obj_1f82 = co_dev_find_obj(nmt->dev, 0x1f82);
938  if (obj_1f82)
939  co_obj_set_dn_ind(obj_1f82, NULL, NULL);
940 #endif
941 
942  // Remove the download indication function for the NMT startup value.
943  co_obj_t *obj_1f80 = co_dev_find_obj(nmt->dev, 0x1f80);
944  if (obj_1f80)
945  co_obj_set_dn_ind(obj_1f80, NULL, NULL);
946 
947 #ifndef LELY_NO_CO_MASTER
948  // Remove the download indication function for the configuration request
949  // value.
950  co_obj_t *obj_1f25 = co_dev_find_obj(nmt->dev, 0x1f25);
951  if (obj_1f25)
952  co_obj_set_dn_ind(obj_1f25, NULL, NULL);
953 #endif
954 
955  // Remove the download indication function for the producer heartbeat
956  // time.
957  co_obj_t *obj_1017 = co_dev_find_obj(nmt->dev, 0x1017);
958  if (obj_1017)
959  co_obj_set_dn_ind(obj_1017, NULL, NULL);
960 
961  // Remove the download indication function for the consumer heartbeat
962  // time.
963  co_obj_t *obj_1016 = co_dev_find_obj(nmt->dev, 0x1016);
964  if (obj_1016)
965  co_obj_set_dn_ind(obj_1016, NULL, NULL);
966 
967  // Remove the download indication function for the life time factor.
968  co_obj_t *obj_100d = co_dev_find_obj(nmt->dev, 0x100d);
969  if (obj_100d)
970  co_obj_set_dn_ind(obj_100d, NULL, NULL);
971 
972  // Remove the download indication function for the guard time.
973  co_obj_t *obj_100c = co_dev_find_obj(nmt->dev, 0x100c);
974  if (obj_100c)
975  co_obj_set_dn_ind(obj_100c, NULL, NULL);
976 
977 #ifndef LELY_NO_CO_MASTER
979 #endif
980 
982 
984 
987 
988 #ifndef LELY_NO_CO_MASTER
990  can_buf_fini(&nmt->buf);
991 #endif
992 
994 
996 
999 }
1000 
1001 co_nmt_t *
1003 {
1004  int errc = 0;
1005 
1006  co_nmt_t *nmt = __co_nmt_alloc();
1007  if (__unlikely(!nmt)) {
1008  errc = get_errc();
1009  goto error_alloc_nmt;
1010  }
1011 
1012  if (__unlikely(!__co_nmt_init(nmt, net, dev))) {
1013  errc = get_errc();
1014  goto error_init_nmt;
1015  }
1016 
1017  return nmt;
1018 
1019 error_init_nmt:
1020  __co_nmt_free(nmt);
1021 error_alloc_nmt:
1022  set_errc(errc);
1023  return NULL;
1024 }
1025 
1026 void
1028 {
1029  if (nmt) {
1030  __co_nmt_fini(nmt);
1031  __co_nmt_free(nmt);
1032  }
1033 }
1034 
1035 can_net_t *
1037 {
1038  assert(nmt);
1039 
1040  return nmt->net;
1041 }
1042 
1043 co_dev_t *
1045 {
1046  assert(nmt);
1047 
1048  return nmt->dev;
1049 }
1050 
1051 void
1052 co_nmt_get_cs_ind(const co_nmt_t *nmt, co_nmt_cs_ind_t **pind, void **pdata)
1053 {
1054  assert(nmt);
1055 
1056  if (pind)
1057  *pind = nmt->cs_ind;
1058  if (pdata)
1059  *pdata = nmt->cs_data;
1060 }
1061 
1062 void
1064 {
1065  assert(nmt);
1066 
1067  nmt->cs_ind = ind;
1068  nmt->cs_data = data;
1069 }
1070 
1071 #ifndef LELY_NO_CO_MASTER
1072 
1073 void
1074 co_nmt_get_ng_ind(const co_nmt_t *nmt, co_nmt_ng_ind_t **pind, void **pdata)
1075 {
1076  assert(nmt);
1077 
1078  if (pind)
1079  *pind = nmt->ng_ind;
1080  if (pdata)
1081  *pdata = nmt->ng_data;
1082 }
1083 
1084 void
1086 {
1087  assert(nmt);
1088 
1089  nmt->ng_ind = ind ? ind : &default_ng_ind;
1090  nmt->ng_data = ind ? data : NULL;
1091 }
1092 
1093 void
1094 co_nmt_on_ng(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason)
1095 {
1096  assert(nmt);
1097  (void)reason;
1098 
1099  if (__unlikely(!id || id > CO_NUM_NODES))
1100  return;
1101 
1102  if (co_nmt_is_master(nmt) && state == CO_NMT_EC_OCCURRED)
1103  co_nmt_node_err_ind(nmt, id);
1104 }
1105 
1106 #endif // !LELY_NO_CO_MASTER
1107 
1108 void
1109 co_nmt_get_lg_ind(const co_nmt_t *nmt, co_nmt_lg_ind_t **pind, void **pdata)
1110 {
1111  assert(nmt);
1112 
1113  if (pind)
1114  *pind = nmt->lg_ind;
1115  if (pdata)
1116  *pdata = nmt->lg_data;
1117 }
1118 
1119 void
1121 {
1122  assert(nmt);
1123 
1124  nmt->lg_ind = ind ? ind : &default_lg_ind;
1125  nmt->lg_data = ind ? data : NULL;
1126 }
1127 
1128 void
1130 {
1131  assert(nmt);
1132 
1133  if (state == CO_NMT_EC_OCCURRED)
1134  co_nmt_on_err(nmt, 0x8130, 0x10, NULL);
1135 }
1136 
1137 void
1138 co_nmt_get_hb_ind(const co_nmt_t *nmt, co_nmt_hb_ind_t **pind, void **pdata)
1139 {
1140  assert(nmt);
1141 
1142  if (pind)
1143  *pind = nmt->hb_ind;
1144  if (pdata)
1145  *pdata = nmt->hb_data;
1146 }
1147 
1148 void
1150 {
1151  assert(nmt);
1152 
1153  nmt->hb_ind = ind ? ind : &default_hb_ind;
1154  nmt->hb_data = ind ? data : NULL;
1155 }
1156 
1157 void
1158 co_nmt_on_hb(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason)
1159 {
1160  assert(nmt);
1161 
1162  if (__unlikely(!id || id > CO_NUM_NODES))
1163  return;
1164 
1165  if (state == CO_NMT_EC_OCCURRED && reason == CO_NMT_EC_TIMEOUT) {
1166 #ifndef LELY_NO_CO_MASTER
1167  if (co_nmt_is_master(nmt)) {
1168  co_nmt_node_err_ind(nmt, id);
1169  return;
1170  }
1171 #endif
1172  co_nmt_on_err(nmt, 0x8130, 0x10, NULL);
1173  }
1174 }
1175 
1176 void
1177 co_nmt_get_st_ind(const co_nmt_t *nmt, co_nmt_st_ind_t **pind, void **pdata)
1178 {
1179  assert(nmt);
1180 
1181  if (pind)
1182  *pind = nmt->st_ind;
1183  if (pdata)
1184  *pdata = nmt->st_data;
1185 }
1186 
1187 void
1189 {
1190  assert(nmt);
1191 
1192  nmt->st_ind = ind ? ind : &default_st_ind;
1193  nmt->st_data = ind ? data : NULL;
1194 }
1195 
1196 void
1197 co_nmt_on_st(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st)
1198 {
1199  assert(nmt);
1200 
1201  if (__unlikely(!id || id > CO_NUM_NODES))
1202  return;
1203 
1204 #ifdef LELY_NO_CO_MASTER
1205  (void)nmt;
1206  (void)st;
1207 #else
1208  if (co_nmt_is_master(nmt) && st == CO_NMT_ST_BOOTUP) {
1209  int errc = get_errc();
1210  co_nmt_boot_req(nmt, id, nmt->timeout);
1211  set_errc(errc);
1212  }
1213 #endif
1214 }
1215 
1216 #ifndef LELY_NO_CO_MASTER
1217 
1218 #ifndef LELY_NO_CO_LSS
1219 
1220 void
1221 co_nmt_get_lss_req(const co_nmt_t *nmt, co_nmt_lss_req_t **pind, void **pdata)
1222 {
1223  assert(nmt);
1224 
1225  if (pind)
1226  *pind = nmt->lss_req;
1227  if (pdata)
1228  *pdata = nmt->lss_data;
1229 }
1230 
1231 void
1233 {
1234  assert(nmt);
1235 
1236  nmt->lss_req = ind;
1237  nmt->lss_data = data;
1238 }
1239 
1240 #endif
1241 
1242 void
1243 co_nmt_get_boot_ind(const co_nmt_t *nmt, co_nmt_boot_ind_t **pind, void **pdata)
1244 {
1245  assert(nmt);
1246 
1247  if (pind)
1248  *pind = nmt->boot_ind;
1249  if (pdata)
1250  *pdata = nmt->boot_data;
1251 }
1252 
1253 void
1255 {
1256  assert(nmt);
1257 
1258  nmt->boot_ind = ind;
1259  nmt->boot_data = data;
1260 }
1261 
1262 void
1263 co_nmt_get_cfg_ind(const co_nmt_t *nmt, co_nmt_cfg_ind_t **pind, void **pdata)
1264 {
1265  assert(nmt);
1266 
1267  if (pind)
1268  *pind = nmt->cfg_ind;
1269  if (pdata)
1270  *pdata = nmt->cfg_data;
1271 }
1272 
1273 void
1275 {
1276  assert(nmt);
1277 
1278  nmt->cfg_ind = ind;
1279  nmt->cfg_data = data;
1280 }
1281 
1282 void
1283 co_nmt_get_dn_ind(const co_nmt_t *nmt, co_nmt_sdo_ind_t **pind, void **pdata)
1284 {
1285  assert(nmt);
1286 
1287  if (pind)
1288  *pind = nmt->dn_ind;
1289  if (pdata)
1290  *pdata = nmt->dn_data;
1291 }
1292 
1293 void
1295 {
1296  assert(nmt);
1297 
1298  nmt->dn_ind = ind;
1299  nmt->dn_data = data;
1300 }
1301 
1302 void
1303 co_nmt_get_up_ind(const co_nmt_t *nmt, co_nmt_sdo_ind_t **pind, void **pdata)
1304 {
1305  assert(nmt);
1306 
1307  if (pind)
1308  *pind = nmt->up_ind;
1309  if (pdata)
1310  *pdata = nmt->up_data;
1311 }
1312 
1313 void
1315 {
1316  assert(nmt);
1317 
1318  nmt->up_ind = ind;
1319  nmt->up_data = data;
1320 }
1321 
1322 #endif // !LELY_NO_CO_MASTER
1323 
1324 void
1325 co_nmt_get_sync_ind(const co_nmt_t *nmt, co_nmt_sync_ind_t **pind, void **pdata)
1326 {
1327  assert(nmt);
1328 
1329  if (pind)
1330  *pind = nmt->sync_ind;
1331  if (pdata)
1332  *pdata = nmt->sync_data;
1333 }
1334 
1335 void
1337 {
1338  assert(nmt);
1339 
1340  nmt->sync_ind = ind;
1341  nmt->sync_data = data;
1342 }
1343 
1344 void
1345 co_nmt_on_sync(co_nmt_t *nmt, co_unsigned8_t cnt)
1346 {
1347  assert(nmt);
1348 
1349  // Handle TPDOs before RPDOs. This prevents a possible race condition if
1350  // the same object is mapped to both an RPDO and a TPDO. In accordance
1351  // with CiA 301 v4.2.0 we transmit the value from the previous
1352  // synchronous window before updating it with a received PDO.
1353 #ifndef LELY_NO_CO_TPDO
1354  for (co_unsigned16_t i = 0; i < nmt->srv.ntpdo; i++) {
1355  if (nmt->srv.tpdos[i])
1356  co_tpdo_sync(nmt->srv.tpdos[i], cnt);
1357  }
1358 #endif
1359 #ifndef LELY_NO_CO_RPDO
1360  for (co_unsigned16_t i = 0; i < nmt->srv.nrpdo; i++) {
1361  if (nmt->srv.rpdos[i])
1362  co_rpdo_sync(nmt->srv.rpdos[i], cnt);
1363  }
1364 #endif
1365 
1366  if (nmt->sync_ind)
1367  nmt->sync_ind(nmt, cnt, nmt->sync_data);
1368 }
1369 
1370 void
1371 co_nmt_on_err(co_nmt_t *nmt, co_unsigned16_t eec, co_unsigned8_t er,
1372  const uint8_t msef[5])
1373 {
1374  assert(nmt);
1375 
1376  if (eec) {
1377 #ifdef LELY_NO_CO_EMCY
1378  (void)er;
1379  (void)msef;
1380 #else
1381  if (nmt->srv.emcy)
1382  co_emcy_push(nmt->srv.emcy, eec, er, msef);
1383 #endif
1384  // In case of a communication error (0x81xx), invoke the
1385  // behavior specified by 1029:01.
1386  if ((eec & 0xff00) == 0x8100)
1388  }
1389 }
1390 
1391 co_unsigned8_t
1393 {
1394  assert(nmt);
1395 
1396  return nmt->id;
1397 }
1398 
1399 int
1400 co_nmt_set_id(co_nmt_t *nmt, co_unsigned8_t id)
1401 {
1402  assert(nmt);
1403 
1404  if (__unlikely(!id || (id > CO_NUM_NODES && id != 0xff))) {
1406  return -1;
1407  }
1408 
1409  nmt->id = id;
1410 
1411  return 0;
1412 }
1413 
1414 co_unsigned8_t
1416 {
1417  assert(nmt);
1418 
1419  return nmt->st & ~CO_NMT_ST_TOGGLE;
1420 }
1421 
1422 int
1424 {
1425 #ifdef LELY_NO_CO_MASTER
1426  (void)nmt;
1427 
1428  return 0;
1429 #else
1430  assert(nmt);
1431 
1432  return nmt->master;
1433 #endif
1434 }
1435 
1436 #ifndef LELY_NO_CO_MASTER
1437 
1438 int
1440 {
1441  assert(nmt);
1442 
1443  return nmt->timeout;
1444 }
1445 
1446 void
1448 {
1449  assert(nmt);
1450 
1451  nmt->timeout = timeout;
1452 }
1453 
1454 int
1455 co_nmt_cs_req(co_nmt_t *nmt, co_unsigned8_t cs, co_unsigned8_t id)
1456 {
1457  assert(nmt);
1458 
1459  if (__unlikely(!nmt->master)) {
1461  return -1;
1462  }
1463 
1464  switch (cs) {
1465  case CO_NMT_CS_START:
1466  case CO_NMT_CS_STOP:
1467  case CO_NMT_CS_ENTER_PREOP:
1468  case CO_NMT_CS_RESET_NODE:
1469  case CO_NMT_CS_RESET_COMM: break;
1470  default: set_errnum(ERRNUM_INVAL); return -1;
1471  }
1472 
1473  if (__unlikely(id > CO_NUM_NODES)) {
1475  return -1;
1476  }
1477 
1478  if (id == co_dev_get_id(nmt->dev))
1479  return co_nmt_cs_ind(nmt, cs);
1480 
1481  trace("NMT: sending command specifier %d to node %d", cs, id);
1482 
1483  struct can_msg msg = CAN_MSG_INIT;
1484  msg.id = CO_NMT_CS_CANID;
1485  msg.len = 2;
1486  msg.data[0] = cs;
1487  msg.data[1] = id;
1488 
1489  // Add the frame to the buffer.
1490  if (__unlikely(!can_buf_write(&nmt->buf, &msg, 1))) {
1491  if (__unlikely(!can_buf_reserve(&nmt->buf, 1)))
1492  return -1;
1493  can_buf_write(&nmt->buf, &msg, 1);
1494  }
1495 
1496  // Send the frame by triggering the inhibit timer.
1497  return co_nmt_cs_timer(NULL, nmt);
1498 }
1499 
1500 #ifndef LELY_NO_CO_LSS
1501 int
1503 {
1504  assert(nmt);
1505 
1506  if (__unlikely(!nmt->master || nmt->state != co_nmt_reset_comm_state)) {
1508  return -1;
1509  }
1510 
1512 
1513  return 0;
1514 }
1515 #endif
1516 
1517 int
1518 co_nmt_boot_req(co_nmt_t *nmt, co_unsigned8_t id, int timeout)
1519 {
1520  assert(nmt);
1521 
1522  int errc = 0;
1523 
1524  if (__unlikely(!nmt->master)) {
1525  errc = errnum2c(ERRNUM_PERM);
1526  goto error_param;
1527  }
1528 
1529  // clang-format off
1530  if (__unlikely(!id || id > CO_NUM_NODES
1531  || id == co_dev_get_id(nmt->dev))) {
1532  // clang-format on
1533  errc = errnum2c(ERRNUM_INVAL);
1534  goto error_param;
1535  }
1536  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
1537 
1538  if (__unlikely(slave->boot)) {
1539  errc = errnum2c(ERRNUM_INPROGRESS);
1540  goto error_param;
1541  }
1542 
1543  trace("NMT: booting slave %d", id);
1544 
1545  slave->boot = co_nmt_boot_create(nmt->net, nmt->dev, nmt);
1546  if (__unlikely(!slave->boot)) {
1547  errc = get_errc();
1548  goto error_create_boot;
1549  }
1550 
1551  // clang-format off
1552  if (__unlikely(co_nmt_boot_boot_req(slave->boot, id, timeout,
1553  &co_nmt_dn_ind, &co_nmt_up_ind, nmt) == -1)) {
1554  // clang-format on
1555  errc = get_errc();
1556  goto error_boot_req;
1557  }
1558 
1559  return 0;
1560 
1561 error_boot_req:
1562  co_nmt_boot_destroy(slave->boot);
1563  slave->boot = NULL;
1564 error_create_boot:
1565 error_param:
1566  set_errc(errc);
1567  return -1;
1568 }
1569 
1570 int
1571 co_nmt_is_booting(const co_nmt_t *nmt, co_unsigned8_t id)
1572 {
1573  assert(nmt);
1574 
1575  if (__unlikely(!nmt->master))
1576  return 0;
1577 
1578  // clang-format off
1579  if (__unlikely(!id || id > CO_NUM_NODES
1580  || id == co_dev_get_id(nmt->dev)))
1581  // clang-format on
1582  return 0;
1583 
1584  return !!nmt->slaves[id - 1].boot;
1585 }
1586 
1587 int
1588 co_nmt_cfg_req(co_nmt_t *nmt, co_unsigned8_t id, int timeout,
1589  co_nmt_cfg_con_t *con, void *data)
1590 {
1591  assert(nmt);
1592 
1593  int errc = 0;
1594 
1595  if (__unlikely(!nmt->master)) {
1596  errc = errnum2c(ERRNUM_PERM);
1597  goto error_param;
1598  }
1599 
1600  // clang-format off
1601  if (__unlikely(!id || id > CO_NUM_NODES
1602  || id == co_dev_get_id(nmt->dev))) {
1603  // clang-format on
1604  errc = errnum2c(ERRNUM_INVAL);
1605  goto error_param;
1606  }
1607  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
1608 
1609  if (__unlikely(slave->cfg)) {
1610  errc = errnum2c(ERRNUM_INPROGRESS);
1611  goto error_param;
1612  }
1613 
1614  trace("NMT: starting update configuration process for node %d", id);
1615 
1616  slave->cfg = co_nmt_cfg_create(nmt->net, nmt->dev, nmt);
1617  if (__unlikely(!slave->cfg)) {
1618  errc = get_errc();
1619  goto error_create_cfg;
1620  }
1621  slave->cfg_con = con;
1622  slave->cfg_data = data;
1623 
1624  // clang-format off
1625  if (__unlikely(co_nmt_cfg_cfg_req(slave->cfg, id, timeout,
1626  &co_nmt_dn_ind, &co_nmt_up_ind, nmt) == -1)) {
1627  // clang-format on
1628  errc = get_errc();
1629  goto error_cfg_req;
1630  }
1631 
1632  return 0;
1633 
1634 error_cfg_req:
1635  co_nmt_cfg_destroy(slave->cfg);
1636  slave->cfg = NULL;
1637 error_create_cfg:
1638 error_param:
1639  set_errc(errc);
1640  return -1;
1641 }
1642 
1643 int
1644 co_nmt_cfg_res(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned32_t ac)
1645 {
1646  assert(nmt);
1647 
1648  if (__unlikely(!nmt->master)) {
1650  return -1;
1651  }
1652 
1653  if (__unlikely(!id || id > CO_NUM_NODES || !nmt->slaves[id - 1].cfg)) {
1655  return -1;
1656  }
1657 
1658  return co_nmt_cfg_cfg_res(nmt->slaves[id - 1].cfg, ac);
1659 }
1660 
1661 int
1662 co_nmt_ng_req(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned16_t gt,
1663  co_unsigned8_t ltf)
1664 {
1665  assert(nmt);
1666 
1667  if (__unlikely(!nmt->master)) {
1669  return -1;
1670  }
1671 
1672  // clang-format off
1673  if (__unlikely(!id || id > CO_NUM_NODES
1674  || id == co_dev_get_id(nmt->dev))) {
1675  // clang-format on
1677  return -1;
1678  }
1679  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
1680 
1681  if (!gt || !ltf) {
1682  can_timer_destroy(slave->timer);
1683  slave->timer = 0;
1684 
1685  slave->gt = 0;
1686  slave->ltf = 0;
1687  slave->rtr = 0;
1688  } else {
1689  if (!slave->timer) {
1690  slave->timer = can_timer_create();
1691  if (__unlikely(!slave->timer))
1692  return -1;
1694  slave->timer, &co_nmt_ng_timer, slave);
1695  }
1696 
1697  slave->gt = gt;
1698  slave->ltf = ltf;
1699  slave->rtr = 0;
1700 
1701  can_timer_timeout(slave->timer, nmt->net, slave->gt);
1702  }
1703 
1704  return 0;
1705 }
1706 
1707 #endif // !LELY_NO_CO_MASTER
1708 
1709 int
1710 co_nmt_cs_ind(co_nmt_t *nmt, co_unsigned8_t cs)
1711 {
1712  assert(nmt);
1713 
1714  switch (cs) {
1715  case CO_NMT_CS_START:
1716  case CO_NMT_CS_STOP:
1717  case CO_NMT_CS_ENTER_PREOP:
1718  case CO_NMT_CS_RESET_NODE:
1719  case CO_NMT_CS_RESET_COMM:
1720  trace("NMT: received command specifier %d", cs);
1721  co_nmt_emit_cs(nmt, cs);
1722  return 0;
1723  default: set_errnum(ERRNUM_INVAL); return -1;
1724  }
1725 }
1726 
1727 void
1729 {
1730  assert(nmt);
1731 
1732  diag(DIAG_INFO, 0, "NMT: communication error indicated");
1733  switch (co_dev_get_val_u8(nmt->dev, 0x1029, 0x01)) {
1734  case 0:
1737  break;
1738  case 2: co_nmt_cs_ind(nmt, CO_NMT_CS_STOP); break;
1739  }
1740 }
1741 
1742 #ifndef LELY_NO_CO_MASTER
1743 int
1744 co_nmt_node_err_ind(co_nmt_t *nmt, co_unsigned8_t id)
1745 {
1746  assert(nmt);
1747 
1748  if (!nmt->master) {
1750  return -1;
1751  }
1752 
1753  if (__unlikely(!id || id > CO_NUM_NODES)) {
1755  return -1;
1756  }
1757 
1758  co_unsigned32_t assignment = co_dev_get_val_u32(nmt->dev, 0x1f81, id);
1759  // Ignore the error event if the slave is no longer in the network list.
1760  if (!(assignment & 0x01))
1761  return 0;
1762  int mandatory = !!(assignment & 0x08);
1763 
1764  diag(DIAG_INFO, 0, "NMT: error indicated for %s slave %d",
1765  mandatory ? "mandatory" : "optional", id);
1766 
1767  if (mandatory && (nmt->startup & 0x40)) {
1768  // If the slave is mandatory and bit 6 of the NMT startup value
1769  // is set, stop all nodes, including the master.
1771  return co_nmt_cs_ind(nmt, CO_NMT_CS_STOP);
1772  } else if (mandatory && (nmt->startup & 0x10)) {
1773  // If the slave is mandatory and bit 4 of the NMT startup value
1774  // is set, reset all nodes, including the master.
1777  } else {
1778  // If the slave is not mandatory, or bits 4 and 6 of the NMT
1779  // startup value are zero, reset the node individually.
1781  return 0;
1782  }
1783 }
1784 #endif
1785 
1786 co_rpdo_t *
1787 co_nmt_get_rpdo(const co_nmt_t *nmt, co_unsigned16_t n)
1788 {
1789  assert(nmt);
1790 
1791  if (__unlikely(!n || n > nmt->srv.nrpdo))
1792  return NULL;
1793 
1794  return nmt->srv.rpdos[n - 1];
1795 }
1796 
1797 co_tpdo_t *
1798 co_nmt_get_tpdo(const co_nmt_t *nmt, co_unsigned16_t n)
1799 {
1800  assert(nmt);
1801 
1802  if (__unlikely(!n || n > nmt->srv.ntpdo))
1803  return NULL;
1804 
1805  return nmt->srv.tpdos[n - 1];
1806 }
1807 
1808 co_ssdo_t *
1809 co_nmt_get_ssdo(const co_nmt_t *nmt, co_unsigned8_t n)
1810 {
1811  assert(nmt);
1812 
1813  if (__unlikely(!n || n > nmt->srv.nssdo))
1814  return NULL;
1815 
1816  return nmt->srv.ssdos[n - 1];
1817 }
1818 
1819 co_csdo_t *
1820 co_nmt_get_csdo(const co_nmt_t *nmt, co_unsigned8_t n)
1821 {
1822  assert(nmt);
1823 
1824  if (__unlikely(!n || n > nmt->srv.ncsdo))
1825  return NULL;
1826 
1827  return nmt->srv.csdos[n - 1];
1828 }
1829 
1830 co_sync_t *
1832 {
1833  assert(nmt);
1834 
1835  return nmt->srv.sync;
1836 }
1837 
1838 co_time_t *
1840 {
1841  assert(nmt);
1842 
1843  return nmt->srv.time;
1844 }
1845 
1846 co_emcy_t *
1848 {
1849  assert(nmt);
1850 
1851  return nmt->srv.emcy;
1852 }
1853 
1854 co_lss_t *
1856 {
1857  assert(nmt);
1858 
1859  return nmt->srv.lss;
1860 }
1861 
1862 #ifndef LELY_NO_CO_MASTER
1863 
1864 void
1865 co_nmt_boot_con(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es)
1866 {
1867  assert(nmt);
1868  assert(nmt->master);
1869  assert(id && id <= CO_NUM_NODES);
1870 
1871  // Update the NMT slave state, including the assignment, in case it
1872  // changed during the 'boot slave' procedure.
1873  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
1874  slave->assignment = co_dev_get_val_u32(nmt->dev, 0x1f81, id);
1875  slave->est = st & ~CO_NMT_ST_TOGGLE;
1876  // If we did not (yet) receive a state but the error control service was
1877  // successfully started, assume the node is pre-operational.
1878  if (!slave->est && (!es || es == 'L'))
1879  slave->est = CO_NMT_ST_PREOP;
1880  slave->rst = st;
1881  slave->es = es;
1882  slave->booted = 1;
1883  co_nmt_boot_destroy(slave->boot);
1884  slave->boot = NULL;
1885 
1886  // Update object 1F82 (Request NMT) with the NMT state.
1887  co_sub_t *sub = co_dev_find_sub(nmt->dev, 0x1f82, id);
1888  if (sub)
1889  co_sub_set_val_u8(sub, st & ~CO_NMT_ST_TOGGLE);
1890 
1891  // If the slave booted successfully and can be started by the NMT
1892  // service, and if the master is allowed to start the nodes (bit 3 of
1893  // the NMT startup value) and has to start the slaves individually (bit
1894  // 1) or is in the operational state, send the NMT 'start' command to
1895  // the slave.
1896  // clang-format off
1897  if (!es && (slave->assignment & 0x05) == 0x05 && !(nmt->startup & 0x08)
1898  && (!(nmt->startup & 0x02)
1900  // clang-format on
1902 
1903  // If the error control service was successfully started, enable
1904  // heartbeat consumption or node guarding.
1905  if (!es || es == 'L') {
1906  co_obj_t *obj_1016 = co_dev_find_obj(nmt->dev, 0x1016);
1907  for (co_unsigned8_t i = 0; i < nmt->nhb; i++) {
1908  co_unsigned32_t val =
1909  co_obj_get_val_u32(obj_1016, i + 1);
1910  if (id != ((val >> 16) & 0xff))
1911  continue;
1912  co_nmt_hb_set_st(nmt->hbs[i], st);
1913  // Disable node guarding.
1914  slave->assignment &= 0xff;
1915  }
1916  // Enable node guarding if the guard time and lifetime factor
1917  // are non-zero.
1918  co_unsigned16_t gt = (slave->assignment >> 16) & 0xffff;
1919  co_unsigned8_t ltf = (slave->assignment >> 8) & 0xff;
1920  if (__unlikely(co_nmt_ng_req(nmt, id, gt, ltf) == -1))
1922  "unable to guard node %02X", id);
1923  }
1924 
1925  trace("NMT: slave %d finished booting with error status %c", id,
1926  es ? es : '0');
1927  if (nmt->boot_ind)
1928  nmt->boot_ind(nmt, id, st, es, nmt->boot_data);
1929 
1930  co_nmt_emit_boot(nmt, id, st, es);
1931 }
1932 
1933 void
1934 co_nmt_cfg_ind(co_nmt_t *nmt, co_unsigned8_t id, co_csdo_t *sdo)
1935 {
1936  assert(nmt);
1937  assert(nmt->master);
1938  assert(id && id <= CO_NUM_NODES);
1939 
1940  if (nmt->cfg_ind) {
1941  nmt->cfg_ind(nmt, id, sdo, nmt->cfg_data);
1942  } else {
1944  "skipping NMT update configuration process");
1945  co_nmt_cfg_res(nmt, id, 0);
1946  }
1947 }
1948 
1949 void
1950 co_nmt_cfg_con(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned32_t ac)
1951 {
1952  assert(nmt);
1953  assert(nmt->master);
1954  assert(id && id <= CO_NUM_NODES);
1955 
1956  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
1957  co_nmt_cfg_destroy(slave->cfg);
1958  slave->cfg = NULL;
1959 
1960  trace("NMT: update configuration process completed for slave %d", id);
1961  if (slave->cfg_con)
1962  slave->cfg_con(nmt, id, ac, slave->cfg_data);
1963 }
1964 
1965 #endif // !LELY_NO_CO_MASTER
1966 
1967 void
1968 co_nmt_hb_ind(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason,
1969  co_unsigned8_t st)
1970 {
1971  assert(nmt);
1972  assert(nmt->hb_ind);
1973 
1974  if (__unlikely(!id || id > CO_NUM_NODES))
1975  return;
1976 
1977  nmt->hb_ind(nmt, id, state, reason, nmt->hb_data);
1978 
1979  if (reason == CO_NMT_EC_STATE)
1980  co_nmt_st_ind(nmt, id, st);
1981 }
1982 
1983 static co_unsigned32_t
1984 co_100c_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
1985 {
1986  assert(sub);
1987  assert(co_obj_get_idx(co_sub_get_obj(sub)) == 0x100c);
1988  assert(req);
1989  co_nmt_t *nmt = data;
1990  assert(nmt);
1991 
1992  co_unsigned32_t ac = 0;
1993 
1994  co_unsigned16_t type = co_sub_get_type(sub);
1995  union co_val val;
1996  if (__unlikely(co_sdo_req_dn_val(req, type, &val, &ac) == -1))
1997  return ac;
1998 
1999  if (__unlikely(co_sub_get_subidx(sub))) {
2000  ac = CO_SDO_AC_NO_SUB;
2001  goto error;
2002  }
2003 
2004  assert(type == CO_DEFTYPE_UNSIGNED16);
2005  co_unsigned16_t gt = val.u16;
2006  co_unsigned16_t gt_old = co_sub_get_val_u16(sub);
2007  if (gt == gt_old)
2008  goto error;
2009 
2010  nmt->gt = gt;
2011 
2012  co_sub_dn(sub, &val);
2013  co_val_fini(type, &val);
2014 
2015  co_nmt_ec_update(nmt);
2016  return 0;
2017 
2018 error:
2019  co_val_fini(type, &val);
2020  return ac;
2021 }
2022 
2023 static co_unsigned32_t
2024 co_100d_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
2025 {
2026  assert(sub);
2027  assert(co_obj_get_idx(co_sub_get_obj(sub)) == 0x100d);
2028  assert(req);
2029  co_nmt_t *nmt = data;
2030  assert(nmt);
2031 
2032  co_unsigned32_t ac = 0;
2033 
2034  co_unsigned16_t type = co_sub_get_type(sub);
2035  union co_val val;
2036  if (__unlikely(co_sdo_req_dn_val(req, type, &val, &ac) == -1))
2037  return ac;
2038 
2039  if (__unlikely(co_sub_get_subidx(sub))) {
2040  ac = CO_SDO_AC_NO_SUB;
2041  goto error;
2042  }
2043 
2044  assert(type == CO_DEFTYPE_UNSIGNED8);
2045  co_unsigned8_t ltf = val.u8;
2046  co_unsigned8_t ltf_old = co_sub_get_val_u8(sub);
2047  if (ltf == ltf_old)
2048  return 0;
2049 
2050  nmt->ltf = ltf;
2051 
2052  co_sub_dn(sub, &val);
2053  co_val_fini(type, &val);
2054 
2055  co_nmt_ec_update(nmt);
2056  return 0;
2057 
2058 error:
2059  co_val_fini(type, &val);
2060  return ac;
2061 }
2062 
2063 static co_unsigned32_t
2064 co_1016_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
2065 {
2066  assert(sub);
2067  assert(co_obj_get_idx(co_sub_get_obj(sub)) == 0x1016);
2068  assert(req);
2069  co_nmt_t *nmt = data;
2070  assert(nmt);
2071 
2072  co_unsigned32_t ac = 0;
2073 
2074  co_unsigned16_t type = co_sub_get_type(sub);
2075  union co_val val;
2076  if (__unlikely(co_sdo_req_dn_val(req, type, &val, &ac) == -1))
2077  return ac;
2078 
2079  co_unsigned8_t subidx = co_sub_get_subidx(sub);
2080  if (__unlikely(!subidx)) {
2081  ac = CO_SDO_AC_NO_WRITE;
2082  goto error;
2083  }
2084  if (__unlikely(subidx > nmt->nhb)) {
2085  ac = CO_SDO_AC_NO_SUB;
2086  goto error;
2087  }
2088 
2089  assert(type == CO_DEFTYPE_UNSIGNED32);
2090  if (val.u32 == co_sub_get_val_u32(sub))
2091  goto error;
2092 
2093  co_unsigned8_t id = (val.u32 >> 16) & 0xff;
2094  co_unsigned16_t ms = val.u32 & 0xffff;
2095 
2096  // If the heartbeat consumer is active (valid node-ID and non-zero
2097  // heartbeat time), check the other entries for duplicate node-IDs.
2098  co_obj_t *obj_1016 = co_dev_find_obj(nmt->dev, 0x1016);
2099  if (id && id <= CO_NUM_NODES && ms) {
2100  for (co_unsigned8_t i = 1; i <= CO_NUM_NODES; i++) {
2101  // Skip the current entry.
2102  if (i == subidx)
2103  continue;
2104  co_unsigned32_t val_i = co_obj_get_val_u32(obj_1016, i);
2105  co_unsigned8_t id_i = (val_i >> 16) & 0xff;
2106  co_unsigned16_t ms_i = val_i & 0xffff;
2107  // It's not allowed to have two active heartbeat
2108  // consumers with the same node-ID.
2109  if (__unlikely(id_i == id && ms_i)) {
2110  ac = CO_SDO_AC_PARAM;
2111  goto error;
2112  }
2113  }
2114  }
2115 
2116  co_sub_dn(sub, &val);
2117  co_val_fini(type, &val);
2118 
2119  co_nmt_hb_set_1016(nmt->hbs[subidx - 1], id, ms);
2120  return 0;
2121 
2122 error:
2123  co_val_fini(type, &val);
2124  return ac;
2125 }
2126 
2127 static co_unsigned32_t
2128 co_1017_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
2129 {
2130  assert(sub);
2131  assert(co_obj_get_idx(co_sub_get_obj(sub)) == 0x1017);
2132  assert(req);
2133  co_nmt_t *nmt = data;
2134  assert(nmt);
2135 
2136  co_unsigned32_t ac = 0;
2137 
2138  co_unsigned16_t type = co_sub_get_type(sub);
2139  union co_val val;
2140  if (__unlikely(co_sdo_req_dn_val(req, type, &val, &ac) == -1))
2141  return ac;
2142 
2143  if (__unlikely(co_sub_get_subidx(sub))) {
2144  ac = CO_SDO_AC_NO_SUB;
2145  goto error;
2146  }
2147 
2148  assert(type == CO_DEFTYPE_UNSIGNED16);
2149  co_unsigned16_t ms = val.u16;
2150  co_unsigned16_t ms_old = co_sub_get_val_u16(sub);
2151  if (ms == ms_old)
2152  goto error;
2153 
2154  nmt->ms = ms;
2155 
2156  co_sub_dn(sub, &val);
2157  co_val_fini(type, &val);
2158 
2159  co_nmt_ec_update(nmt);
2160  return 0;
2161 
2162 error:
2163  co_val_fini(type, &val);
2164  return ac;
2165 }
2166 
2167 #ifndef LELY_NO_CO_MASTER
2168 static co_unsigned32_t
2169 co_1f25_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
2170 {
2171  assert(sub);
2172  assert(co_obj_get_idx(co_sub_get_obj(sub)) == 0x1f25);
2173  assert(req);
2174  co_nmt_t *nmt = data;
2175  assert(nmt);
2176 
2177  co_unsigned32_t ac = 0;
2178 
2179  co_unsigned16_t type = co_sub_get_type(sub);
2180  union co_val val;
2181  if (__unlikely(co_sdo_req_dn_val(req, type, &val, &ac) == -1))
2182  return ac;
2183 
2184  co_unsigned8_t subidx = co_sub_get_subidx(sub);
2185  if (__unlikely(!subidx)) {
2186  ac = CO_SDO_AC_NO_WRITE;
2187  goto error;
2188  }
2189 
2190  // Sub-index 80 indicates all nodes.
2191  co_unsigned8_t id = subidx == 0x80 ? 0 : subidx;
2192  // Abort with an error if the node-ID is unknown.
2193  // clang-format off
2194  if (__unlikely(id > CO_NUM_NODES
2195  || (id && !(nmt->slaves[id - 1].assignment & 0x01)))) {
2196  // clang-format on
2197  ac = CO_SDO_AC_PARAM_VAL;
2198  goto error;
2199  }
2200 
2201  // Check if the value 'conf' was downloaded.
2202  if (__unlikely(!nmt->master || val.u32 != UINT32_C(0x666e6f63))) {
2203  ac = CO_SDO_AC_DATA_CTL;
2204  goto error;
2205  }
2206 
2207  if (id) {
2208  // Check if the entry for this node is present in object 1F20
2209  // (Store DCF) or 1F22 (Concise DCF).
2210  // clang-format off
2211  if (__unlikely(!co_dev_get_val(nmt->dev, 0x1f20, id)
2212  && !co_dev_get_val(nmt->dev, 0x1f22, id))) {
2213  // clang-format on
2214  ac = CO_SDO_AC_NO_DATA;
2215  goto error;
2216  }
2217  // Abort if the slave is already being configured.
2218  if (__unlikely(nmt->slaves[id - 1].cfg)) {
2219  ac = CO_SDO_AC_DATA_DEV;
2220  goto error;
2221  }
2222  co_nmt_cfg_req(nmt, id, nmt->timeout, NULL, NULL);
2223  } else {
2224  // Check if object 1F20 (Store DCF) or 1F22 (Concise DCF)
2225  // exists.
2226  // clang-format off
2227  if (__unlikely(!co_dev_find_obj(nmt->dev, 0x1f20)
2228  && !co_dev_find_obj(nmt->dev, 0x1f22))) {
2229  // clang-format on
2230  ac = CO_SDO_AC_NO_DATA;
2231  goto error;
2232  }
2233  for (id = 1; id <= CO_NUM_NODES; id++) {
2234  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
2235  // Skip slaves that are not in the network list or are
2236  // already being configured.
2237  if (!(slave->assignment & 0x01) || slave->cfg)
2238  continue;
2239  co_nmt_cfg_req(nmt, id, nmt->timeout, NULL, NULL);
2240  }
2241  }
2242 
2243 error:
2244  co_val_fini(type, &val);
2245  return ac;
2246 }
2247 #endif // !LELY_NO_CO_MASTER
2248 
2249 static co_unsigned32_t
2250 co_1f80_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
2251 {
2252  assert(sub);
2253  assert(co_obj_get_idx(co_sub_get_obj(sub)) == 0x1f80);
2254  assert(req);
2255  (void)data;
2256 
2257  co_unsigned32_t ac = 0;
2258 
2259  co_unsigned16_t type = co_sub_get_type(sub);
2260  union co_val val;
2261  if (__unlikely(co_sdo_req_dn_val(req, type, &val, &ac) == -1))
2262  return ac;
2263 
2264  if (__unlikely(co_sub_get_subidx(sub))) {
2265  ac = CO_SDO_AC_NO_SUB;
2266  goto error;
2267  }
2268 
2269  assert(type == CO_DEFTYPE_UNSIGNED32);
2270  co_unsigned32_t startup = val.u32;
2271  co_unsigned32_t startup_old = co_sub_get_val_u32(sub);
2272  if (startup == startup_old)
2273  goto error;
2274 
2275  // Only bits 0..4 and 6 are supported.
2276  if (__unlikely((startup ^ startup_old) != 0x5f)) {
2277  ac = CO_SDO_AC_PARAM_VAL;
2278  goto error;
2279  }
2280 
2281  co_sub_dn(sub, &val);
2282 error:
2283  co_val_fini(type, &val);
2284  return ac;
2285 }
2286 
2287 #ifndef LELY_NO_CO_MASTER
2288 static co_unsigned32_t
2289 co_1f82_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
2290 {
2291  assert(sub);
2292  assert(co_obj_get_idx(co_sub_get_obj(sub)) == 0x1f82);
2293  assert(req);
2294  co_nmt_t *nmt = data;
2295  assert(nmt);
2296 
2297  co_unsigned32_t ac = 0;
2298 
2299  co_unsigned16_t type = co_sub_get_type(sub);
2300  union co_val val;
2301  if (__unlikely(co_sdo_req_dn_val(req, type, &val, &ac) == -1))
2302  return ac;
2303 
2304  co_unsigned8_t subidx = co_sub_get_subidx(sub);
2305  if (__unlikely(!subidx)) {
2306  ac = CO_SDO_AC_NO_WRITE;
2307  goto error;
2308  }
2309 
2310  // Sub-index 80 indicates all nodes.
2311  co_unsigned8_t id = subidx == 0x80 ? 0 : subidx;
2312  // Abort with an error if the node-ID is unknown.
2313  // clang-format off
2314  if (__unlikely(id > CO_NUM_NODES
2315  || (id && !(nmt->slaves[id - 1].assignment & 0x01)))) {
2316  // clang-format on
2317  ac = CO_SDO_AC_PARAM_VAL;
2318  goto error;
2319  }
2320 
2321  if (__unlikely(!nmt->master)) {
2322  ac = CO_SDO_AC_DATA_CTL;
2323  goto error;
2324  }
2325 
2326  assert(type == CO_DEFTYPE_UNSIGNED8);
2327  switch (val.u8) {
2328  case CO_NMT_ST_STOP: co_nmt_cs_req(nmt, CO_NMT_CS_STOP, id); break;
2329  case CO_NMT_ST_START: co_nmt_cs_req(nmt, CO_NMT_CS_START, id); break;
2330  case CO_NMT_ST_RESET_NODE:
2332  break;
2333  case CO_NMT_ST_RESET_COMM:
2335  break;
2336  case CO_NMT_ST_PREOP:
2338  break;
2339  default: ac = CO_SDO_AC_PARAM_VAL; break;
2340  }
2341 
2342 error:
2343  co_val_fini(type, &val);
2344  return ac;
2345 }
2346 #endif // !LELY_NO_CO_MASTER
2347 
2348 static int
2349 co_nmt_recv_000(const struct can_msg *msg, void *data)
2350 {
2351  assert(msg);
2352  co_nmt_t *nmt = data;
2353  assert(nmt);
2354 
2355 #ifndef LELY_NO_CO_MASTER
2356  // Ignore NMT commands if we're the master.
2357  if (nmt->master)
2358  return 0;
2359 #endif
2360 
2361  if (__unlikely(msg->len < 2))
2362  return 0;
2363  co_unsigned8_t cs = msg->data[0];
2364  co_unsigned8_t id = msg->data[1];
2365 
2366  // Ignore NMT commands to other nodes.
2367  if (id && id != co_dev_get_id(nmt->dev))
2368  return 0;
2369 
2370  co_nmt_emit_cs(nmt, cs);
2371 
2372  return 0;
2373 }
2374 
2375 static int
2376 co_nmt_recv_700(const struct can_msg *msg, void *data)
2377 {
2378  assert(msg);
2379  assert(msg->id > 0x700 && msg->id <= 0x77f);
2380  co_nmt_t *nmt = data;
2381  assert(nmt);
2382 
2383  if (msg->flags & CAN_FLAG_RTR) {
2384  assert(nmt->gt && nmt->ltf);
2385  assert(nmt->lg_ind);
2386 
2387  // Respond with the state and flip the toggle bit.
2388  co_nmt_ec_send_res(nmt, nmt->st);
2389  nmt->st ^= CO_NMT_ST_TOGGLE;
2390 
2391  // Reset the life guarding timer.
2392  can_timer_timeout(nmt->ec_timer, nmt->net, nmt->gt * nmt->ltf);
2393 
2394  if (nmt->lg_state == CO_NMT_EC_OCCURRED) {
2395  diag(DIAG_INFO, 0, "NMT: life guarding event resolved");
2396  // Notify the user of the resolution of a life guarding
2397  // error.
2399  nmt->lg_ind(nmt, nmt->lg_state, nmt->lg_data);
2400  }
2401 #ifndef LELY_NO_CO_MASTER
2402  } else {
2403  assert(nmt->master);
2404  assert(nmt->ng_ind);
2405 
2406  co_unsigned8_t id = (msg->id - 0x700) & 0x7f;
2407  if (__unlikely(!id))
2408  return 0;
2409  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
2410 
2411  // Ignore messages from booting slaves.
2412  if (slave->boot)
2413  return 0;
2414 
2415  if (__unlikely(msg->len < 1))
2416  return 0;
2417  co_unsigned8_t st = msg->data[0];
2418 
2419  if (st == CO_NMT_ST_BOOTUP) {
2420  // The expected state after a boot-up event is
2421  // pre-operational.
2422  slave->est = CO_NMT_ST_PREOP;
2423  // Inform the application of the boot-up event.
2424  co_nmt_st_ind(nmt, id, st);
2425  return 0;
2426  }
2427 
2428  // Ignore messages if node guarding is disabled.
2429  if (!slave->gt || !slave->ltf)
2430  return 0;
2431 
2432  // Check the toggle bit and ignore the message if it does not
2433  // match.
2434  if (__unlikely(!((st ^ slave->rst) & CO_NMT_ST_TOGGLE)))
2435  return 0;
2436  slave->rst ^= CO_NMT_ST_TOGGLE;
2437 
2438  // Notify the application of the resolution of a node guarding
2439  // timeout.
2440  if (slave->rtr >= slave->ltf) {
2441  diag(DIAG_INFO, 0,
2442  "NMT: node guarding time out resolved for node %d",
2443  id);
2446  }
2447  slave->rtr = 0;
2448 
2449  // Notify the application of the occurrence or resolution of an
2450  // unexpected state change.
2451  if (slave->est != (st & ~CO_NMT_ST_TOGGLE)
2452  && slave->ng_state == CO_NMT_EC_RESOLVED) {
2453  diag(DIAG_INFO, 0,
2454  "NMT: node guarding state change occurred for node %d",
2455  id);
2456  slave->ng_state = CO_NMT_EC_OCCURRED;
2457  nmt->ng_ind(nmt, id, slave->ng_state, CO_NMT_EC_STATE,
2458  nmt->ng_data);
2459  } else if (slave->est == (st & ~CO_NMT_ST_TOGGLE)
2460  && slave->ng_state == CO_NMT_EC_OCCURRED) {
2461  diag(DIAG_INFO, 0,
2462  "NMT: node guarding state change resolved for node %d",
2463  id);
2464  slave->ng_state = CO_NMT_EC_RESOLVED;
2465  nmt->ng_ind(nmt, id, slave->ng_state, CO_NMT_EC_STATE,
2466  nmt->ng_data);
2467  }
2468 
2469  // Notify the application of the occurrence of a state change.
2470  if (st != slave->rst)
2471  co_nmt_st_ind(nmt, id, st);
2472 #endif
2473  }
2474 
2475  return 0;
2476 }
2477 
2478 #ifndef LELY_NO_CO_MASTER
2479 static int
2480 co_nmt_ng_timer(const struct timespec *tp, void *data)
2481 {
2482  (void)tp;
2483  struct co_nmt_slave *slave = data;
2484  assert(slave);
2485  assert(slave->gt && slave->ltf);
2486  co_nmt_t *nmt = slave->nmt;
2487  assert(nmt);
2488  assert(nmt->master);
2489  assert(nmt->ng_ind);
2490  co_unsigned8_t id = slave - nmt->slaves + 1;
2491  assert(id && id <= CO_NUM_NODES);
2492 
2493  // Reset the timer for the next RTR.
2494  can_timer_timeout(slave->timer, nmt->net, slave->gt);
2495 
2496  // Do not send node guarding RTRs to slaves that have not finished
2497  // booting.
2498  if (!slave->booted)
2499  return 0;
2500 
2501  // Notify the application once of the occurrence of a node guarding
2502  // timeout.
2503  // clang-format off
2504  if (__unlikely(slave->rtr <= slave->ltf
2505  && ++slave->rtr == slave->ltf)) {
2506  // clang-format on
2507  diag(DIAG_INFO, 0,
2508  "NMT: node guarding time out occurred for node %d",
2509  id);
2511  nmt->ng_data);
2512  return 0;
2513  }
2514 
2515  struct can_msg msg = CAN_MSG_INIT;
2516  msg.id = CO_NMT_EC_CANID(id);
2517  msg.flags |= CAN_FLAG_RTR;
2518 
2519  return can_net_send(nmt->net, &msg);
2520 }
2521 #endif
2522 
2523 static int
2524 co_nmt_ec_timer(const struct timespec *tp, void *data)
2525 {
2526  (void)tp;
2527  co_nmt_t *nmt = data;
2528  assert(nmt);
2529  assert(nmt->lg_ind);
2530 
2531  if (nmt->ms) {
2532  // Send the state of the NMT service (excluding the toggle bit).
2533  co_nmt_ec_send_res(nmt, nmt->st & ~CO_NMT_ST_TOGGLE);
2534  } else if (nmt->gt && nmt->ltf) {
2535  // Notify the user of the occurrence of a life guarding error.
2536  diag(DIAG_INFO, 0, "NMT: life guarding event occurred");
2538  nmt->lg_ind(nmt, nmt->lg_state, nmt->lg_data);
2539  }
2540 
2541  return 0;
2542 }
2543 
2544 #ifndef LELY_NO_CO_MASTER
2545 int
2546 co_nmt_cs_timer(const struct timespec *tp, void *data)
2547 {
2548  (void)tp;
2549  co_nmt_t *nmt = data;
2550  assert(nmt);
2551  assert(nmt->master);
2552 
2553  co_unsigned16_t inhibit = co_dev_get_val_u16(nmt->dev, 0x102a, 0x00);
2554 
2555  if (inhibit) {
2556  if (nmt->cs_timer) {
2557  can_timer_stop(nmt->cs_timer);
2558  } else {
2559  nmt->cs_timer = can_timer_create();
2560  if (__unlikely(!nmt->cs_timer))
2561  return -1;
2563  nmt->cs_timer, &co_nmt_cs_timer, nmt);
2564  }
2565  } else if (nmt->cs_timer) {
2567  nmt->cs_timer = NULL;
2568  }
2569 
2570  struct timespec now = { 0, 0 };
2571  can_net_get_time(nmt->net, &now);
2572 
2573  struct can_msg msg;
2574  while (can_buf_peek(&nmt->buf, &msg, 1)) {
2575  assert(msg.id == CO_NMT_CS_CANID);
2576  assert(msg.len == 2);
2577  // Wait until the inhibit time has elapsed.
2578  if (inhibit && timespec_cmp(&now, &nmt->inhibit) < 0) {
2579  can_timer_start(nmt->cs_timer, nmt->net, &nmt->inhibit,
2580  NULL);
2581  return 0;
2582  }
2583  // Try to send the frame.
2584  if (__unlikely(can_net_send(nmt->net, &msg) == -1))
2585  return -1;
2586  can_buf_read(&nmt->buf, NULL, 1);
2587  // Update the expected state of the node(s).
2588  co_unsigned8_t st = 0;
2589  switch (msg.data[0]) {
2590  case CO_NMT_CS_START: st = CO_NMT_ST_START; break;
2591  case CO_NMT_CS_STOP: st = CO_NMT_ST_STOP; break;
2592  case CO_NMT_CS_ENTER_PREOP: st = CO_NMT_ST_PREOP; break;
2593  }
2594  co_unsigned8_t id = msg.data[1];
2595  assert(id <= CO_NUM_NODES);
2596  if (id) {
2597  if (nmt->slaves[id - 1].est)
2598  nmt->slaves[id - 1].est = st;
2599  } else {
2600  for (id = 1; id <= CO_NUM_NODES; id++) {
2601  if (nmt->slaves[id - 1].est)
2602  nmt->slaves[id - 1].est = st;
2603  }
2604  }
2605  // Update the inhibit time.
2606  can_net_get_time(nmt->net, &now);
2607  nmt->inhibit = now;
2608  timespec_add_usec(&nmt->inhibit, inhibit * 100);
2609  }
2610 
2611  return 0;
2612 }
2613 #endif // !LELY_NO_CO_MASTER
2614 
2615 static void
2616 co_nmt_st_ind(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st)
2617 {
2618  assert(nmt);
2619  assert(nmt->st_ind);
2620 
2621  if (__unlikely(!id || id > CO_NUM_NODES))
2622  return;
2623 
2624 #ifndef LELY_NO_CO_MASTER
2625  if (nmt->master) {
2626  nmt->slaves[id - 1].rst = st;
2627 
2628  // Update object 1F82 (Request NMT) with the NMT state.
2629  co_sub_t *sub = co_dev_find_sub(nmt->dev, 0x1f82, id);
2630  if (sub)
2631  co_sub_set_val_u8(sub, st & ~CO_NMT_ST_TOGGLE);
2632  }
2633 #endif
2634 
2635  nmt->st_ind(nmt, id, st & ~CO_NMT_ST_TOGGLE, nmt->st_data);
2636 }
2637 
2638 #ifndef LELY_NO_CO_MASTER
2639 static void
2640 default_ng_ind(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason,
2641  void *data)
2642 {
2643  (void)data;
2644 
2645  co_nmt_on_ng(nmt, id, state, reason);
2646 }
2647 #endif
2648 
2649 static void
2650 default_lg_ind(co_nmt_t *nmt, int state, void *data)
2651 {
2652  (void)data;
2653 
2654  co_nmt_on_lg(nmt, state);
2655 }
2656 
2657 static void
2658 default_hb_ind(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason,
2659  void *data)
2660 {
2661  (void)data;
2662 
2663  co_nmt_on_hb(nmt, id, state, reason);
2664 }
2665 
2666 static void
2667 default_st_ind(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, void *data)
2668 {
2669  (void)data;
2670 
2671  co_nmt_on_st(nmt, id, st);
2672 }
2673 
2674 #ifndef LELY_NO_CO_MASTER
2675 
2676 static void
2677 co_nmt_dn_ind(const co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx,
2678  size_t size, size_t nbyte, void *data)
2679 {
2680  co_nmt_t *nmt = data;
2681  assert(nmt);
2682 
2683  if (nmt->dn_ind)
2684  nmt->dn_ind(nmt, co_csdo_get_num(sdo), idx, subidx, size, nbyte,
2685  nmt->dn_data);
2686 }
2687 
2688 static void
2689 co_nmt_up_ind(const co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx,
2690  size_t size, size_t nbyte, void *data)
2691 {
2692  co_nmt_t *nmt = data;
2693  assert(nmt);
2694 
2695  if (nmt->up_ind)
2696  nmt->up_ind(nmt, co_csdo_get_num(sdo), idx, subidx, size, nbyte,
2697  nmt->up_data);
2698 }
2699 
2700 #endif
2701 
2702 static void
2704 {
2705  assert(nmt);
2706 
2707  while (next) {
2708  co_nmt_state_t *prev = nmt->state;
2709  nmt->state = next;
2710 
2711  if (prev && prev->on_leave)
2712  prev->on_leave(nmt);
2713 
2714  next = next->on_enter ? next->on_enter(nmt) : NULL;
2715  }
2716 }
2717 
2718 static inline void
2719 co_nmt_emit_cs(co_nmt_t *nmt, co_unsigned8_t cs)
2720 {
2721  assert(nmt);
2722  assert(nmt->state);
2723  assert(nmt->state->on_cs);
2724 
2725  co_nmt_enter(nmt, nmt->state->on_cs(nmt, cs));
2726 }
2727 
2728 #ifndef LELY_NO_CO_MASTER
2729 
2730 static inline void
2731 co_nmt_emit_boot(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es)
2732 {
2733  assert(nmt);
2734  assert(nmt->state);
2735  assert(nmt->state->on_boot);
2736 
2737  co_nmt_enter(nmt, nmt->state->on_boot(nmt, id, st, es));
2738 }
2739 
2740 static co_nmt_state_t *
2742  co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es)
2743 {
2744  (void)nmt;
2745  (void)id;
2746  (void)st;
2747  (void)es;
2748 
2749  return NULL;
2750 }
2751 
2752 #endif // !LELY_NO_CO_MASTER
2753 
2754 static co_nmt_state_t *
2755 co_nmt_init_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
2756 {
2757  (void)nmt;
2758 
2759  switch (cs) {
2761  default: return NULL;
2762  }
2763 }
2764 
2765 static co_nmt_state_t *
2767 {
2768  assert(nmt);
2769 
2770  diag(DIAG_INFO, 0, "NMT: entering reset application state");
2771 
2772 #ifndef LELY_NO_CO_MASTER
2773  // Disable NMT slave management.
2774  co_nmt_slaves_fini(nmt);
2775  nmt->halt = 0;
2776 #endif
2777 
2778  // Disable all services.
2779  co_nmt_srv_set(&nmt->srv, nmt, 0);
2780 
2781  // Disable heartbeat consumption.
2782  co_nmt_hb_fini(nmt);
2783 
2784  // Disable error control services.
2785  co_nmt_ec_fini(nmt);
2786 
2787  // Stop receiving NMT commands.
2788  can_recv_stop(nmt->recv_000);
2789 
2790  // Reset application parameters.
2791  // clang-format off
2792  if (__unlikely(co_dev_read_dcf(nmt->dev, NULL, NULL, &nmt->dcf_node)
2793  == -1))
2794  // clang-format on
2796  "unable to reset application parameters");
2797 
2798  nmt->st = CO_NMT_ST_RESET_NODE;
2799  co_nmt_st_ind(nmt, co_dev_get_id(nmt->dev), 0);
2800 
2801  if (nmt->cs_ind)
2802  nmt->cs_ind(nmt, CO_NMT_CS_RESET_NODE, nmt->cs_data);
2803 
2804  return co_nmt_reset_comm_state;
2805 }
2806 
2807 static co_nmt_state_t *
2809 {
2810  assert(nmt);
2811 
2812  diag(DIAG_INFO, 0, "NMT: entering reset communication state");
2813 
2814 #ifndef LELY_NO_CO_MASTER
2815  // Disable NMT slave management.
2816  co_nmt_slaves_fini(nmt);
2817  nmt->halt = 0;
2818 #endif
2819 
2820  // Disable all services.
2821  co_nmt_srv_set(&nmt->srv, nmt, 0);
2822 
2823  // Disable heartbeat consumption.
2824  co_nmt_hb_fini(nmt);
2825 
2826  // Disable error control services.
2827  co_nmt_ec_fini(nmt);
2828 
2829  // Stop receiving NMT commands.
2830  can_recv_stop(nmt->recv_000);
2831 
2832  // Reset communication parameters.
2833  // clang-format off
2834  if (__unlikely(co_dev_read_dcf(nmt->dev, NULL, NULL, &nmt->dcf_comm)
2835  == -1))
2836  // clang-format on
2838  "unable to reset communication parameters");
2839 
2840  // Update the node-ID if necessary.
2841  if (nmt->id != co_dev_get_id(nmt->dev)) {
2842  co_dev_set_id(nmt->dev, nmt->id);
2844  // clang-format off
2845  if (__unlikely(co_dev_write_dcf(nmt->dev, 0x1000, 0x1fff,
2846  &nmt->dcf_comm) == -1))
2847  // clang-format on
2849  "unable to store communication parameters");
2850  }
2851 
2852  // Load the NMT startup value.
2853  nmt->startup = co_dev_get_val_u32(nmt->dev, 0x1f80, 0x00);
2854 #ifndef LELY_NO_CO_MASTER
2855  // Bit 0 of the NMT startup value determines whether we are a master or
2856  // a slave.
2857  nmt->master = !!(nmt->startup & 0x01);
2858 #endif
2859  diag(DIAG_INFO, 0, "NMT: running as %s",
2860  co_nmt_is_master(nmt) ? "master" : "slave");
2861 
2862  nmt->st = CO_NMT_ST_RESET_COMM;
2863  co_nmt_st_ind(nmt, co_dev_get_id(nmt->dev), 0);
2864 
2865  // Start receiving NMT commands.
2866  if (!co_nmt_is_master(nmt))
2867  can_recv_start(nmt->recv_000, nmt->net, CO_NMT_CS_CANID, 0);
2868 
2869  // Enable LSS.
2870  co_nmt_srv_set(&nmt->srv, nmt, CO_NMT_SRV_LSS);
2871 
2872  if (nmt->cs_ind)
2873  nmt->cs_ind(nmt, CO_NMT_CS_RESET_COMM, nmt->cs_data);
2874 
2875 #if !defined(LELY_NO_CO_MASTER) && !defined(LELY_NO_CO_LSS)
2876  // If LSS is required, invoked the user-defined callback function and
2877  // wait for the process to complete.
2878  if (nmt->master && nmt->lss_req) {
2879  nmt->lss_req(nmt, co_nmt_get_lss(nmt), nmt->lss_data);
2880  return NULL;
2881  }
2882 #endif
2883 
2884  return co_nmt_bootup_state;
2885 }
2886 
2887 static co_nmt_state_t *
2888 co_nmt_reset_comm_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
2889 {
2890  (void)nmt;
2891 
2892  switch (cs) {
2895  default: return NULL;
2896  }
2897 }
2898 
2899 static co_nmt_state_t *
2901 {
2902  assert(nmt);
2903 
2904  // Don't enter the 'pre-operational' state if the node-ID is invalid.
2905  if (co_dev_get_id(nmt->dev) == 0xff) {
2906  diag(DIAG_INFO, 0, "NMT: unconfigured node-ID");
2907  return NULL;
2908  }
2909 
2910  // Enable error control services.
2911  co_nmt_ec_init(nmt);
2912 
2913  // Enable heartbeat consumption.
2914  co_nmt_hb_init(nmt);
2915 
2916  // Send the boot-up signal to notify the master we exist.
2918 
2919  return co_nmt_preop_state;
2920 }
2921 
2922 static co_nmt_state_t *
2923 co_nmt_bootup_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
2924 {
2925  (void)nmt;
2926 
2927  switch (cs) {
2930  default: return NULL;
2931  }
2932 }
2933 
2934 static co_nmt_state_t *
2936 {
2937  assert(nmt);
2938 
2939  diag(DIAG_INFO, 0, "NMT: entering pre-operational state");
2940 
2941 #ifndef LELY_NO_CO_MASTER
2942  // Disable NMT slave management.
2943  co_nmt_slaves_fini(nmt);
2944  nmt->halt = 0;
2945 #endif
2946 
2947  // Enable all services except PDO.
2948  co_nmt_srv_set(&nmt->srv, nmt, CO_NMT_PREOP_SRV);
2949 
2950  nmt->st = CO_NMT_ST_PREOP | (nmt->st & CO_NMT_ST_TOGGLE);
2951  co_nmt_st_ind(nmt, co_dev_get_id(nmt->dev), nmt->st);
2952 
2953  if (nmt->cs_ind)
2954  nmt->cs_ind(nmt, CO_NMT_CS_ENTER_PREOP, nmt->cs_data);
2955 
2956  return co_nmt_startup(nmt);
2957 }
2958 
2959 static co_nmt_state_t *
2960 co_nmt_preop_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
2961 {
2962  (void)nmt;
2963 
2964  switch (cs) {
2965  case CO_NMT_CS_START: return co_nmt_start_state;
2966  case CO_NMT_CS_STOP: return co_nmt_stop_state;
2969  default: return NULL;
2970  }
2971 }
2972 
2973 #ifndef LELY_NO_CO_MASTER
2974 static co_nmt_state_t *
2976  co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es)
2977 {
2978  assert(nmt);
2979  assert(nmt->master);
2980  assert(id && id <= CO_NUM_NODES);
2981  (void)st;
2982 
2983  // If the 'boot slave' process failed for a mandatory slave, halt the
2984  // network boot-up procedure.
2985  if ((nmt->slaves[id - 1].assignment & 0x09) == 0x09 && es && es != 'L')
2986  nmt->halt = 1;
2987 
2988  // Wait for any mandatory slaves that have not yet finished booting.
2989  int wait = nmt->halt;
2990  for (co_unsigned8_t id = 1; !wait && id <= CO_NUM_NODES; id++)
2991  wait = (nmt->slaves[id - 1].assignment & 0x09) == 0x09
2992  && nmt->slaves[id - 1].boot;
2993  if (!wait) {
2994  trace("NMT: all mandatory slaves started successfully");
2995  return co_nmt_startup_slave(nmt);
2996  }
2997  return NULL;
2998 }
2999 #endif
3000 
3001 static co_nmt_state_t *
3003 {
3004  assert(nmt);
3005 
3006  diag(DIAG_INFO, 0, "NMT: entering operational state");
3007 
3008  // Enable all services.
3009  co_nmt_srv_set(&nmt->srv, nmt, CO_NMT_START_SRV);
3010 
3011  nmt->st = CO_NMT_ST_START | (nmt->st & CO_NMT_ST_TOGGLE);
3012  co_nmt_st_ind(nmt, co_dev_get_id(nmt->dev), nmt->st);
3013 
3014 #ifndef LELY_NO_CO_MASTER
3015  // If we're the master and bit 3 of the NMT startup value is 0 and bit 1
3016  // is 1, send the NMT start remote node command to all nodes (see Fig. 2
3017  // in CiA 302-2 version 4.1.0).
3018  if (nmt->master && (nmt->startup & 0x0a) == 0x02) {
3019  // Check if all slaves booted successfully.
3020  int boot = 1;
3021  for (co_unsigned8_t id = 1; boot && id <= CO_NUM_NODES; id++) {
3022  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
3023  // Skip those slaves that are not in the network list.
3024  if (!(slave->assignment & 0x01))
3025  continue;
3026  // Check if the slave finished booting successfully and
3027  // can be started by the master.
3028  boot = slave->booted && (!slave->es || slave->es == 'L')
3029  && !(slave->assignment & 0x04);
3030  }
3031  if (boot) {
3032  // Start all NMT slaves at once.
3034  } else {
3035  for (co_unsigned8_t id = 1; id <= CO_NUM_NODES; id++) {
3036  struct co_nmt_slave *slave =
3037  &nmt->slaves[id - 1];
3038  // Skip those slaves that are not in the network
3039  // list (bit 0), or that we are not allowed to
3040  // boot (bit 2).
3041  if ((slave->assignment & 0x05) != 0x05)
3042  continue;
3043  // Only start slaves that have finished booting
3044  // successfully and are not already (expected to
3045  // be) operational.
3046  if (slave->booted
3047  && (!slave->es || slave->es == 'L')
3048  && slave->est != CO_NMT_ST_START)
3050  }
3051  }
3052  }
3053 #endif
3054 
3055  if (nmt->cs_ind)
3057 
3058  return NULL;
3059 }
3060 
3061 static co_nmt_state_t *
3062 co_nmt_start_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
3063 {
3064  (void)nmt;
3065 
3066  switch (cs) {
3067  case CO_NMT_CS_STOP: return co_nmt_stop_state;
3071  default: return NULL;
3072  }
3073 }
3074 
3075 static co_nmt_state_t *
3077 {
3078  assert(nmt);
3079 
3080  diag(DIAG_INFO, 0, "NMT: entering stopped state");
3081 
3082  // Disable all services (except LSS).
3084 
3087 
3088  if (nmt->cs_ind)
3090 
3091  return NULL;
3092 }
3093 
3094 static co_nmt_state_t *
3095 co_nmt_stop_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
3096 {
3097  (void)nmt;
3098 
3099  switch (cs) {
3100  case CO_NMT_CS_START: return co_nmt_start_state;
3104  default: return NULL;
3105  }
3106 }
3107 
3108 static co_nmt_state_t *
3110 {
3111  assert(nmt);
3112 
3113 #ifndef LELY_NO_CO_MASTER
3114  if (nmt->master)
3115  return co_nmt_startup_master(nmt);
3116 #endif
3117  return co_nmt_startup_slave(nmt);
3118 }
3119 
3120 #ifndef LELY_NO_CO_MASTER
3121 static co_nmt_state_t *
3123 {
3124  assert(nmt);
3125  assert(nmt->master);
3126 
3127  // Enable NMT slave management.
3129 
3130  // Check if any node has the keep-alive bit set.
3131  int keep = 0;
3132  for (co_unsigned8_t id = 1; !keep && id <= CO_NUM_NODES; id++)
3133  keep = (nmt->slaves[id - 1].assignment & 0x11) == 0x11;
3134 
3135  // Send the NMT 'reset communication' command to all slaves with
3136  // the keep-alive bit _not_ set. This includes slaves which are not in
3137  // the network list.
3138  if (keep) {
3139  for (co_unsigned8_t id = 1; id <= CO_NUM_NODES; id++) {
3140  // Do not reset the master itself.
3141  if (id == co_dev_get_id(nmt->dev))
3142  continue;
3143  if ((nmt->slaves[id - 1].assignment & 0x11) != 0x11)
3145  }
3146  } else {
3148  }
3149 
3150  // Start the 'boot slave' processes.
3151  switch (co_nmt_slaves_boot(nmt)) {
3152  case -1:
3153  // Halt the network boot-up procedure if the 'boot slave'
3154  // process failed for a mandatory slave.
3155  nmt->halt = 1;
3156  return NULL;
3157  case 0: return co_nmt_startup_slave(nmt);
3158  default:
3159  // Wait for all mandatory slaves to finish booting.
3160  trace("NMT: waiting for mandatory slaves to start");
3161  return NULL;
3162  }
3163 }
3164 #endif
3165 
3166 static co_nmt_state_t *
3168 {
3169  assert(nmt);
3170 
3171  // Enter the operational state automatically if bit 2 of the NMT startup
3172  // value is 0.
3173  return (nmt->startup & 0x04) ? NULL : co_nmt_start_state;
3174 }
3175 
3176 static void
3178 {
3179  assert(nmt);
3180 
3181  // Enable life guarding or heartbeat production.
3182  nmt->gt = co_dev_get_val_u16(nmt->dev, 0x100c, 0x00);
3183  nmt->ltf = co_dev_get_val_u8(nmt->dev, 0x100d, 0x00);
3184  nmt->ms = co_dev_get_val_u16(nmt->dev, 0x1017, 0x00);
3185 
3187 
3188  if (__unlikely(co_nmt_ec_update(nmt) == -1))
3189  diag(DIAG_ERROR, get_errc(), "unable to start %s",
3190  nmt->ms ? "heartbeat production"
3191  : "life guarding");
3192 }
3193 
3194 static void
3196 {
3197  assert(nmt);
3198 
3199  // Disable life guarding and heartbeat production.
3200  nmt->gt = 0;
3201  nmt->ltf = 0;
3202  nmt->ms = 0;
3203 
3205 
3207 }
3208 
3209 static int
3211 {
3212  assert(nmt);
3213 
3214  // Heartbeat production has precedence over life guarding.
3215  int lt = nmt->ms ? 0 : nmt->gt * nmt->ltf;
3216 #ifndef LELY_NO_CO_MASTER
3217  // Disable life guarding for the master.
3218  if (nmt->master)
3219  lt = 0;
3220 #endif
3221 
3222  if (lt) {
3223  if (!nmt->recv_700) {
3225  if (__unlikely(!nmt->recv_700))
3226  return -1;
3228  }
3229  // Start the CAN frame receiver for node guarding RTRs.
3232  CAN_FLAG_RTR);
3233  } else if (nmt->recv_700) {
3235  nmt->recv_700 = NULL;
3236  }
3237 
3238  if (nmt->ms || lt) {
3239  if (!nmt->ec_timer) {
3241  if (__unlikely(!nmt->ec_timer))
3242  return -1;
3245  }
3246  // Start the CAN timer for heartbeat production or life
3247  // guarding.
3248  int ms = nmt->ms ? nmt->ms : lt;
3249  struct timespec interval = { ms / 1000, (ms % 1000) * 1000000 };
3250  can_timer_start(nmt->ec_timer, nmt->net, NULL, &interval);
3251  } else if (nmt->ec_timer) {
3253  nmt->ec_timer = NULL;
3254  }
3255 
3256  return 0;
3257 }
3258 
3259 static int
3260 co_nmt_ec_send_res(co_nmt_t *nmt, co_unsigned8_t st)
3261 {
3262  assert(nmt);
3263 
3264  struct can_msg msg = CAN_MSG_INIT;
3265  msg.id = CO_NMT_EC_CANID(co_dev_get_id(nmt->dev));
3266  msg.len = 1;
3267  msg.data[0] = st;
3268 
3269  return can_net_send(nmt->net, &msg);
3270 }
3271 
3272 static void
3274 {
3275  assert(nmt);
3276 
3277  // Create and initialize the heartbeat consumers.
3278  assert(!nmt->hbs);
3279  assert(!nmt->nhb);
3280  co_obj_t *obj_1016 = co_dev_find_obj(nmt->dev, 0x1016);
3281  if (obj_1016) {
3282  nmt->nhb = co_obj_get_val_u8(obj_1016, 0x00);
3283  nmt->hbs = calloc(nmt->nhb, sizeof(*nmt->hbs));
3284  if (__unlikely(!nmt->hbs && nmt->nhb)) {
3285  nmt->nhb = 0;
3286  set_errc(errno2c(errno));
3288  "unable to create heartbeat consumers");
3289  }
3290  }
3291 
3292  for (co_unsigned8_t i = 0; i < nmt->nhb; i++) {
3293  nmt->hbs[i] = co_nmt_hb_create(nmt->net, nmt);
3294  if (__unlikely(!nmt->hbs[i])) {
3296  "unable to create heartbeat consumer 0x%02X",
3297  (co_unsigned8_t)(i + 1));
3298  continue;
3299  }
3300 
3301  co_unsigned32_t val = co_obj_get_val_u32(obj_1016, i + 1);
3302  co_unsigned8_t id = (val >> 16) & 0xff;
3303  co_unsigned16_t ms = val & 0xffff;
3304  co_nmt_hb_set_1016(nmt->hbs[i], id, ms);
3305  }
3306 }
3307 
3308 static void
3310 {
3311  assert(nmt);
3312 
3313  // Destroy all heartbeat consumers.
3314  for (size_t i = 0; i < nmt->nhb; i++)
3315  co_nmt_hb_destroy(nmt->hbs[i]);
3316  free(nmt->hbs);
3317  nmt->hbs = NULL;
3318  nmt->nhb = 0;
3319 }
3320 
3321 #ifndef LELY_NO_CO_MASTER
3322 
3323 static void
3325 {
3326  assert(nmt);
3327  assert(nmt->master);
3328 
3329  co_nmt_slaves_fini(nmt);
3330 
3331  for (co_unsigned8_t id = 1; id <= CO_NUM_NODES; id++) {
3332  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
3333  slave->recv = can_recv_create();
3334  if (__unlikely(!slave->recv)) {
3336  "unable to create CAN frame receiver");
3337  continue;
3338  }
3340  // Start listening for boot-up notifications.
3341  can_recv_start(slave->recv, nmt->net, CO_NMT_EC_CANID(id), 0);
3342  }
3343 
3344  co_obj_t *obj_1f81 = co_dev_find_obj(nmt->dev, 0x1f81);
3345  if (__unlikely(!obj_1f81))
3346  return;
3347 
3348  co_unsigned8_t n = co_obj_get_val_u8(obj_1f81, 0x00);
3349  for (co_unsigned8_t i = 0; i < MIN(n, CO_NUM_NODES); i++)
3350  nmt->slaves[i].assignment = co_obj_get_val_u32(obj_1f81, i + 1);
3351 }
3352 
3353 static void
3355 {
3356  assert(nmt);
3357 
3358  for (co_unsigned8_t id = 1; id <= CO_NUM_NODES; id++) {
3359  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
3360 
3361  can_recv_destroy(slave->recv);
3362  slave->recv = NULL;
3363  can_timer_destroy(slave->timer);
3364  slave->timer = NULL;
3365 
3366  slave->assignment = 0;
3367  slave->est = 0;
3368  slave->rst = 0;
3369  slave->es = 0;
3370 
3371  slave->booted = 0;
3372  co_nmt_boot_destroy(slave->boot);
3373  slave->boot = NULL;
3374 
3375  co_nmt_cfg_destroy(slave->cfg);
3376  slave->cfg = NULL;
3377  slave->cfg_con = NULL;
3378  slave->cfg_data = NULL;
3379 
3380  slave->gt = 0;
3381  slave->ltf = 0;
3382  slave->rtr = 0;
3383  slave->ng_state = CO_NMT_EC_RESOLVED;
3384  }
3385 }
3386 
3387 static int
3389 {
3390  assert(nmt);
3391  assert(nmt->master);
3392 
3393  int res = 0;
3394  for (co_unsigned8_t id = 1; id <= CO_NUM_NODES; id++) {
3395  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
3396  // Skip those slaves that are not in the network list (bit 0).
3397  if ((slave->assignment & 0x01) != 0x01)
3398  continue;
3399  int mandatory = !!(slave->assignment & 0x08);
3400  // Wait for all mandatory slaves to finish booting.
3401  if (!res && mandatory)
3402  res = 1;
3403  // Nodes with the keep-alive bit _not_ set are booted when we
3404  // receive their boot-up signal.
3405  if (!(slave->assignment & 0x10))
3406  continue;
3407  // Halt the network boot-up procedure if the 'boot slave'
3408  // process failed for a mandatory slave with the keep-alive bit
3409  // set.
3410  // clang-format off
3411  if (__unlikely(co_nmt_boot_req(nmt, id, nmt->timeout) == -1
3412  && mandatory))
3413  // clang-format on
3414  res = -1;
3415  }
3416  return res;
3417 }
3418 
3419 #endif // !LELY_NO_CO_MASTER
void co_nmt_set_hb_ind(co_nmt_t *nmt, co_nmt_hb_ind_t *ind, void *data)
Sets the indication function invoked when a heartbeat event occurs.
Definition: nmt.c:1149
char es
The error status of the &#39;boot slave&#39; process.
Definition: nmt.c:78
void co_nmt_set_lg_ind(co_nmt_t *nmt, co_nmt_lg_ind_t *ind, void *data)
Sets the indication function invoked when a life guarding event occurs.
Definition: nmt.c:1120
co_tpdo_t * co_nmt_get_tpdo(const co_nmt_t *nmt, co_unsigned16_t n)
Returns a pointer to a Transmit-PDO service.
Definition: nmt.c:1798
A CANopen SDO upload/download request.
Definition: sdo.h:178
#define CO_NMT_ST_RESET_NODE
The NMT sub-state &#39;reset application&#39;.
Definition: nmt.h:64
co_unsigned8_t est
The expected state of the slave (excluding the toggle bit).
Definition: nmt.c:74
co_nmt_st_ind_t * st_ind
A pointer to the state change event indication function.
Definition: nmt.c:169
A CAN or CAN FD format frame.
Definition: msg.h:88
void can_buf_fini(struct can_buf *buf)
Finalizes a CAN frame buffer.
Definition: buf.c:67
#define CO_SDO_AC_DATA_CTL
SDO abort code: Data cannot be transferred or stored to the application because of local control...
Definition: sdo.h:159
void co_nmt_set_sync_ind(co_nmt_t *nmt, co_nmt_sync_ind_t *ind, void *data)
Sets the indication function invoked by co_nmt_on_sync() after all PDOs have been transmitted/process...
Definition: nmt.c:1336
co_nmt_sdo_ind_t * up_ind
A pointer to the SDO upload progress indication function.
Definition: nmt.c:210
struct can_buf buf
A pointer to the CAN frame buffer for NMT messages.
Definition: nmt.c:174
A CANopen LSS master/slave service.
Definition: lss.c:43
void co_obj_set_dn_ind(co_obj_t *obj, co_sub_dn_ind_t *ind, void *data)
Sets the download indication function for a CANopen object.
Definition: obj.c:336
uint_least32_t id
The identifier (11 or 29 bits, depending on the CAN_FLAG_IDE flag).
Definition: msg.h:90
co_unsigned32_t co_sub_dn_ind_val(co_sub_t *sub, co_unsigned16_t type, const void *val)
Invokes the download indication function of a CANopen sub-object, registered with co_sub_set_dn_ind()...
Definition: obj.c:810
static co_unsigned32_t co_1f82_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
The download indication function for (all sub-objects of) CANopen object 1F80 (Request NMT)...
Definition: nmt.c:2289
#define CO_NMT_CS_STOP
The NMT command specifier &#39;stop&#39;.
Definition: nmt.h:43
A CAN network interface.
Definition: net.c:37
void co_nmt_cfg_destroy(co_nmt_cfg_t *cfg)
Destroys a CANopen NMT &#39;configuration request&#39;.
Definition: nmt_cfg.c:246
int co_dev_write_dcf(const co_dev_t *dev, co_unsigned16_t min, co_unsigned16_t max, void **ptr)
Loads the values of a range of objects in the object dictionary of a CANopen device, and writes them to a memory buffer, in the concise DCF format.
Definition: dev.c:776
void co_nmt_lss_req_t(co_nmt_t *nmt, co_lss_t *lss, void *data)
The type of a CANopen LSS request function, invoked by an NMT master before booting the slaves (see F...
Definition: nmt.h:188
A CANopen NMT &#39;boot slave&#39; service.
Definition: nmt_boot.c:67
int co_nmt_ng_req(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned16_t gt, co_unsigned8_t ltf)
Request the node guarding service for the specified node, even if it is not in the network list...
Definition: nmt.c:1662
An NMT error control event was resolved.
Definition: nmt.h:82
#define CO_NMT_ST_START
The NMT state &#39;operational&#39;.
Definition: nmt.h:61
uint_least8_t len
The number of bytes in data (or the requested number of bytes in case of a remote frame)...
Definition: msg.h:101
void(* on_leave)(co_nmt_t *nmt)
A pointer to the function invoked when the current state is left.
Definition: nmt.c:420
A CANopen sub-object.
Definition: obj.h:54
co_nmt_state_t *(* on_enter)(co_nmt_t *nmt)
A pointer to the function invoked when a new state is entered.
Definition: nmt.c:390
co_unsigned8_t rtr
The number of unanswered node guarding RTRs.
Definition: nmt.c:94
#define CO_NMT_PREOP_SRV
The services enabled in the NMT &#39;pre-operational&#39; state.
Definition: nmt.c:626
A CANopen Receive-PDO.
Definition: rpdo.c:40
void co_nmt_destroy(co_nmt_t *nmt)
Destroys a CANopen NMT master/slave service.
Definition: nmt.c:1027
void * cs_data
A pointer to user-specified data for cs_ind.
Definition: nmt.c:130
int co_dev_read_dcf(co_dev_t *dev, co_unsigned16_t *pmin, co_unsigned16_t *pmax, void *const *ptr)
Reads the values of a range of objects from a memory buffer, in the concise DCF format, and stores them in the object dictionary of a CANopen device.
Definition: dev.c:682
static void default_lg_ind(co_nmt_t *nmt, int state, void *data)
The default life guarding event handler.
Definition: nmt.c:2650
An NMT error control event occurred.
Definition: nmt.h:80
This header file is part of the CANopen library; it contains the Transmit-PDO declarations.
A CAN frame buffer.
Definition: buf.h:47
void * sync_data
A pointer to user-specified data for sync_ind.
Definition: nmt.c:217
#define CO_SDO_AC_NO_DATA
SDO abort code: No data available.
Definition: sdo.h:175
void can_timer_timeout(can_timer_t *timer, can_net_t *net, int timeout)
Starts a CAN timer and registers it with a network interface.
Definition: net.c:484
co_emcy_t * co_nmt_get_emcy(const co_nmt_t *nmt)
Returns a pointer to the EMCY producer/consumer service.
Definition: nmt.c:1847
A CANopen Server-SDO.
Definition: ssdo.c:43
void co_nmt_set_st_ind(co_nmt_t *nmt, co_nmt_st_ind_t *ind, void *data)
Sets the indication function invoked when a state change is detected.
Definition: nmt.c:1188
#define CO_NMT_STOP_SRV
The services enabled in the NMT &#39;stopped&#39; state.
Definition: nmt.c:634
void co_nmt_boot_con(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es)
The CANopen NMT &#39;boot slave&#39; confirmation function, invoked when the &#39;boot slave&#39; process completes...
Definition: nmt.c:1865
void * cfg_data
A pointer to user-specified data for cfg_con.
Definition: nmt.c:88
int errnum2c(errnum_t errnum)
Transforms a platform-independent error number to a native error code.
Definition: errnum.c:825
#define CO_DEFTYPE_DOMAIN
The data type (and object index) of an arbitrary large block of data.
Definition: type.h:77
void co_nmt_comm_err_ind(co_nmt_t *nmt)
Indicates the occurrence of a communication error and invokes the specified error behavior (object 10...
Definition: nmt.c:1728
co_nmt_cfg_con_t * cfg_con
A pointer to the NMT &#39;configuration request&#39; confirmation function.
Definition: nmt.c:86
void co_nmt_boot_destroy(co_nmt_boot_t *boot)
Destroys a CANopen NMT &#39;boot slave&#39; service.
Definition: nmt_boot.c:866
A CANopen Transmit-PDO.
Definition: tpdo.c:41
#define CO_NMT_ST_TOGGLE
The mask to get/set the toggle bit from an NMT state.
Definition: nmt.h:73
void co_nmt_srv_set(struct co_nmt_srv *srv, co_nmt_t *nmt, int set)
Enables/disables the specified CANopen services.
Definition: nmt_srv.c:152
#define CAN_MSG_INIT
The static initializer for can_msg.
Definition: msg.h:114
size_t can_buf_peek(struct can_buf *buf, struct can_msg *ptr, size_t n)
Reads, but does not remove, frames from a CAN frame buffer.
Definition: buf.h:242
int co_nmt_cfg_res(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned32_t ac)
Indicates the result of the &#39;update configuration&#39; step of an NMT &#39;request configuration&#39; request for...
Definition: nmt.c:1644
void * ng_data
A pointer to user-specified data for ng_ind.
Definition: nmt.c:137
#define CAN_BUF_INIT
The static initializer for struct can_buf.
Definition: buf.h:81
static void co_nmt_ec_fini(co_nmt_t *nmt)
Finalizes the error control services.
Definition: nmt.c:3195
void set_errnum(errnum_t errnum)
Sets the current (thread-specific) platform-independent error number to errnum.
Definition: errnum.h:375
void diag(enum diag_severity severity, int errc, const char *format,...)
Emits a diagnostic message.
Definition: diag.c:156
can_recv_t * recv_700
A pointer to the CAN frame receiver for NMT error control messages.
Definition: nmt.c:132
void co_nmt_get_lss_req(const co_nmt_t *nmt, co_nmt_lss_req_t **pind, void **pdata)
Retrieves the request function invoked to perform LSS when booting an NMT master. ...
Definition: nmt.c:1221
void co_nmt_on_hb(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason)
Implements the default behavior when a heartbeat event occurs (see sections 7.2.8.2.2.3 and 7.2.8.3.2.2 in CiA 301 version 4.2.0).
Definition: nmt.c:1158
static co_nmt_state_t * co_nmt_bootup_on_enter(co_nmt_t *nmt)
The entry function of the &#39;boot-up&#39; state.
Definition: nmt.c:2900
#define CO_SDO_AC_PARAM_VAL
SDO abort code: Invalid value for parameter (download only).
Definition: sdo.h:135
A union of the CANopen static data types.
Definition: val.h:163
A CANopen NMT service manager.
Definition: nmt_srv.h:30
struct timespec inhibit
The time at which the next NMT message may be sent.
Definition: nmt.c:176
void can_net_get_time(const can_net_t *net, struct timespec *tp)
Retrieves the current time of a CAN network interface.
Definition: net.c:204
int co_nmt_boot_boot_req(co_nmt_boot_t *boot, co_unsigned8_t id, int timeout, co_csdo_ind_t *dn_ind, co_csdo_ind_t *up_ind, void *data)
Starts a CANopen NMT &#39;boot slave&#39; service.
Definition: nmt_boot.c:875
void co_nmt_cfg_ind(co_nmt_t *nmt, co_unsigned8_t id, co_csdo_t *sdo)
The CANopen NMT &#39;update configuration&#39; indication function, invoked when a configuration request is r...
Definition: nmt.c:1934
void co_nmt_get_dn_ind(const co_nmt_t *nmt, co_nmt_sdo_ind_t **pind, void **pdata)
Retrieves the indication function used to notify the user of the progress of the current SDO download...
Definition: nmt.c:1283
static co_nmt_state_t * co_nmt_startup(co_nmt_t *nmt)
The NMT startup procedure (see Fig. 1 & 2 in CiA 302-2 version 4.1.0).
Definition: nmt.c:3109
co_unsigned8_t co_nmt_get_st(const co_nmt_t *nmt)
Returns the current state of a CANopen NMT service (one of CO_NMT_ST_BOOTUP, CO_NMT_ST_STOP, CO_NMT_ST_START, CO_NMT_ST_RESET_NODE, CO_NMT_ST_RESET_COMM or CO_NMT_ST_PREOP).
Definition: nmt.c:1415
static void co_nmt_slaves_init(co_nmt_t *nmt)
Initializes NMT slave management.
Definition: nmt.c:3324
static co_unsigned32_t co_1017_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
The download indication function for CANopen object 1017 (Producer heartbeat time).
Definition: nmt.c:2128
co_unsigned8_t co_dev_get_id(const co_dev_t *dev)
Returns the node-ID of a CANopen device.
Definition: dev.c:198
#define MIN(a, b)
Returns the minimum of a and b.
Definition: util.h:57
int timeout
The default SDO timeout (in milliseconds) used during the NMT &#39;boot slave&#39; and &#39;check configuration&#39; ...
Definition: nmt.c:196
static void co_nmt_st_ind(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st)
The indication function for state change events.
Definition: nmt.c:2616
static int co_nmt_ec_update(co_nmt_t *nmt)
Updates and (de)activates the life guarding or heartbeat production services.
Definition: nmt.c:3210
co_unsigned32_t startup
The NMT startup value (object 1F80).
Definition: nmt.c:120
This is the internal header file of the NMT &#39;boot slave&#39; declarations.
void can_timer_start(can_timer_t *timer, can_net_t *net, const struct timespec *start, const struct timespec *interval)
Starts a CAN timer and registers it with a network interface.
Definition: net.c:437
void * dcf_comm
The concise DCF of the communication parameters.
Definition: nmt.c:114
co_rpdo_t ** rpdos
An array of pointers to the Receive-PDO services.
Definition: nmt_srv.h:40
This header file is part of the CANopen library; it contains the Client-SDO declarations.
void co_nmt_set_ng_ind(co_nmt_t *nmt, co_nmt_ng_ind_t *ind, void *data)
Sets the indication function invoked when a node guarding event occurs.
Definition: nmt.c:1085
static co_nmt_state_t * co_nmt_start_on_enter(co_nmt_t *nmt)
The entry function of the &#39;operational&#39; state.
Definition: nmt.c:3002
This header file is part of the CANopen library; it contains the Service Data Object (SDO) declaratio...
static int co_nmt_slaves_boot(co_nmt_t *nmt)
Starts the NMT &#39;boot slave&#39; processes.
Definition: nmt.c:3388
co_unsigned16_t gt
The guard time (in milliseconds).
Definition: nmt.c:90
can_net_t * net
A pointer to a CAN network interface.
Definition: nmt.c:106
static co_nmt_state_t *const co_nmt_stop_state
The NMT &#39;stopped&#39; state.
Definition: nmt.c:558
void co_nmt_get_cs_ind(const co_nmt_t *nmt, co_nmt_cs_ind_t **pind, void **pdata)
Retrieves the indication function invoked when an NMT command is received.
Definition: nmt.c:1052
co_nmt_state_t *(* on_cs)(co_nmt_t *nmt, co_unsigned8_t cs)
A pointer to the transition function invoked when an NMT command is received.
Definition: nmt.c:402
void * boot_data
A pointer to user-specified data for boot_ind.
Definition: nmt.c:200
void co_nmt_hb_set_st(co_nmt_hb_t *hb, co_unsigned8_t st)
Sets the expected state of a remote NMT node.
Definition: nmt_hb.c:186
co_nmt_hb_t * co_nmt_hb_create(can_net_t *net, co_nmt_t *nmt)
Creates a new CANopen NMT heartbeat consumer service.
Definition: nmt_hb.c:135
static int co_nmt_ec_timer(const struct timespec *tp, void *data)
The CAN timer callback function for life guarding or heartbeat production.
Definition: nmt.c:2524
This header file is part of the CANopen library; it contains the CANopen value declarations.
void set_errc(int errc)
Sets the current (thread-specific) native error code to errc.
Definition: errnum.c:957
can_recv_t * recv
A pointer to the CAN frame receiver for the boot-up event and node guarding messages.
Definition: nmt.c:68
int co_tpdo_sync(co_tpdo_t *pdo, co_unsigned8_t cnt)
Triggers the transmission of a synchronous PDO.
Definition: tpdo.c:436
static void co_nmt_dn_ind(const co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx, size_t size, size_t nbyte, void *data)
The SDO download progress indication function.
Definition: nmt.c:2677
co_unsigned8_t co_nmt_get_id(const co_nmt_t *nmt)
Returns the pending node-ID.
Definition: nmt.c:1392
int master
A flag specifying whether the NMT service is a master or a slave.
Definition: nmt.c:123
void co_nmt_set_lss_req(co_nmt_t *nmt, co_nmt_lss_req_t *ind, void *data)
Sets the request function invoked to perform LSS when booting an NMT master.
Definition: nmt.c:1232
void co_nmt_get_boot_ind(const co_nmt_t *nmt, co_nmt_boot_ind_t **pind, void **pdata)
Retrieves the indication function invoked when a CANopen NMT &#39;boot slave&#39; process completes...
Definition: nmt.c:1243
static co_nmt_state_t * co_nmt_preop_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
The &#39;NMT command received&#39; transition function of the &#39;pre-operational&#39; state.
Definition: nmt.c:2960
co_nmt_lss_req_t * lss_req
A pointer to the LSS request function.
Definition: nmt.c:181
co_unsigned8_t ncsdo
The number of Client-SDO services.
Definition: nmt_srv.h:54
void can_timer_set_func(can_timer_t *timer, can_timer_func_t *func, void *data)
Sets the callback function invoked when a CAN timer is triggered.
Definition: net.c:428
can_recv_t * can_recv_create(void)
Creates a new CAN frame receiver.
Definition: net.c:537
int co_nmt_node_err_ind(co_nmt_t *nmt, co_unsigned8_t id)
Indicates the occurrence of an error event and triggers the error handling process (see Fig...
Definition: nmt.c:1744
void co_nmt_set_cs_ind(co_nmt_t *nmt, co_nmt_cs_ind_t *ind, void *data)
Sets the indication function invoked when an NMT command is received.
Definition: nmt.c:1063
const void * co_dev_get_val(const co_dev_t *dev, co_unsigned16_t idx, co_unsigned8_t subidx)
Returns a pointer to the current value of a CANopen sub-object.
Definition: dev.c:529
#define CO_DEFTYPE_UNSIGNED32
The data type (and object index) of a 32-bit unsigned integer.
Definition: type.h:50
void timespec_add_usec(struct timespec *tp, uint_least64_t usec)
Adds usec microseconds to the time at tp.
Definition: time.h:138
co_nmt_sync_ind_t * sync_ind
A pointer to the SYNC indication function.
Definition: nmt.c:215
void co_nmt_on_lg(co_nmt_t *nmt, int state)
Implements the default behavior when a life guarding event occurs (see section 7.2.8.2.2.2 in CiA 301 version 4.2.0).
Definition: nmt.c:1129
uint_least8_t data[CAN_MSG_MAX_LEN]
The frame payload (in case of a data frame).
Definition: msg.h:103
co_csdo_t ** csdos
An array of pointers to the Client-SDO services.
Definition: nmt_srv.h:52
int co_emcy_push(co_emcy_t *emcy, co_unsigned16_t eec, co_unsigned8_t er, const uint8_t msef[5])
Pushes a CANopen EMCY message to the stack and broadcasts it if the EMCY producer service is active...
Definition: emcy.c:380
co_tpdo_t ** tpdos
An array of pointers to the Transmit-PDO services.
Definition: nmt_srv.h:44
struct co_nmt_srv srv
The NMT service manager.
Definition: nmt.c:118
can_timer_t * ec_timer
A pointer to the CAN timer for life guarding or heartbeat production.
Definition: nmt.c:142
int co_nmt_cs_ind(co_nmt_t *nmt, co_unsigned8_t cs)
Processes an NMT command from the master or the application.
Definition: nmt.c:1710
static int co_nmt_ec_send_res(co_nmt_t *nmt, co_unsigned8_t st)
Sends an NMT error control response message.
Definition: nmt.c:3260
static co_nmt_state_t * co_nmt_preop_on_enter(co_nmt_t *nmt)
The entry function of the &#39;pre-operational&#39; state.
Definition: nmt.c:2935
This header file is part of the utilities library; it contains the time function declarations.
void can_timer_stop(can_timer_t *timer)
Stops a CAN timer and unregisters it with a network interface.
Definition: net.c:468
co_unsigned8_t nssdo
The number of Server-SDO services.
Definition: nmt_srv.h:50
co_unsigned16_t nrpdo
The number of Receive-PDO services.
Definition: nmt_srv.h:42
A CANopen NMT state.
Definition: nmt.c:388
co_nmt_state_t * state
The current state.
Definition: nmt.c:116
static co_nmt_state_t *const co_nmt_preop_state
The NMT &#39;pre-operational&#39; state.
Definition: nmt.c:514
This header file is part of the CANopen library; it contains the emergency (EMCY) object declarations...
void co_nmt_srv_init(struct co_nmt_srv *srv, co_nmt_t *nmt)
Initializes a CANopen NMT service manager.
Definition: nmt_srv.c:117
void * hb_data
A pointer to user-specified data for hb_ind.
Definition: nmt.c:167
int co_nmt_set_id(co_nmt_t *nmt, co_unsigned8_t id)
Sets the pending node-ID.
Definition: nmt.c:1400
#define CO_DEFTYPE_UNSIGNED16
The data type (and object index) of a 16-bit unsigned integer.
Definition: type.h:47
static co_nmt_state_t * co_nmt_startup_master(co_nmt_t *nmt)
The NMT master startup procedure.
Definition: nmt.c:3122
void can_recv_destroy(can_recv_t *recv)
Destroys a CAN frame receiver.
Definition: net.c:562
co_unsigned8_t co_csdo_get_num(const co_csdo_t *sdo)
Returns the SDO number of a Client-SDO.
Definition: csdo.c:920
This is the internal header file of the CANopen library.
void co_nmt_get_st_ind(const co_nmt_t *nmt, co_nmt_st_ind_t **pind, void **pdata)
Retrieves the indication function invoked when a state change is detected.
Definition: nmt.c:1177
void co_nmt_st_ind_t(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, void *data)
The type of a CANopen NMT state change indication function, invoked when a state change is detected b...
Definition: nmt.h:176
size_t can_buf_reserve(struct can_buf *buf, size_t n)
Resizes a CAN frame buffer, if necessary, to make room for at least n additional frames.
Definition: buf.c:111
int co_nmt_cs_req(co_nmt_t *nmt, co_unsigned8_t cs, co_unsigned8_t id)
Submits an NMT request to a slave.
Definition: nmt.c:1455
void co_nmt_get_ng_ind(const co_nmt_t *nmt, co_nmt_ng_ind_t **pind, void **pdata)
Retrieves the indication function invoked when a node guarding event occurs.
Definition: nmt.c:1074
#define CO_NMT_CS_RESET_NODE
The NMT command specifier &#39;reset node&#39;.
Definition: nmt.h:49
co_lss_t * co_nmt_get_lss(const co_nmt_t *nmt)
Returns a pointer to the LSS master/slave service.
Definition: nmt.c:1855
void co_nmt_boot_ind_t(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es, void *data)
The type of a CANopen NMT &#39;boot slave&#39; indication function, invoked when the &#39;boot slave&#39; process com...
Definition: nmt.h:200
const char * co_nmt_es2str(char es)
Returns a pointer to a string describing an NMT boot error status.
Definition: nmt.c:679
Operation in progress.
Definition: errnum.h:125
This header file is part of the CAN library; it contains the CAN frame buffer declarations.
#define CO_NMT_ST_STOP
The NMT state &#39;stopped&#39;.
Definition: nmt.h:58
#define CO_NMT_ST_PREOP
The NMT state &#39;pre-operational&#39;.
Definition: nmt.h:70
co_unsigned8_t rst
The received state of the slave (including the toggle bit).
Definition: nmt.c:76
#define CO_NMT_ST_BOOTUP
The NMT state &#39;boot-up&#39;.
Definition: nmt.h:55
co_dev_t * co_nmt_get_dev(const co_nmt_t *nmt)
Returns a pointer to the CANopen device of an NMT master/slave service.
Definition: nmt.c:1044
void co_nmt_on_st(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st)
Implements the default behavior when a state change is detected by the node guarding or heartbeat pro...
Definition: nmt.c:1197
int get_errc(void)
Returns the last (thread-specific) native error code set by a system call or library function...
Definition: errnum.c:947
A CAN timer.
Definition: net.c:63
static void default_hb_ind(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason, void *data)
The default heartbeat event handler.
Definition: nmt.c:2658
static void default_st_ind(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, void *data)
The default state change event handler.
Definition: nmt.c:2667
void co_nmt_cs_ind_t(co_nmt_t *nmt, co_unsigned8_t cs, void *data)
The type of a CANopen NMT command indication function, invoked when an NMT command is received (and a...
Definition: nmt.h:110
co_ssdo_t ** ssdos
An array of pointers to the Server-SDO services.
Definition: nmt_srv.h:48
int co_nmt_cfg_req(co_nmt_t *nmt, co_unsigned8_t id, int timeout, co_nmt_cfg_con_t *con, void *data)
Issues the NMT &#39;configuration request&#39; for the specified node.
Definition: nmt.c:1588
A CANopen Client-SDO.
Definition: csdo.c:45
void co_nmt_on_ng(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason)
Implements the default behavior when a node guarding event occurs (see sections 7.2.8.2.2.1 and 7.2.8.3.2.1 in CiA 301 version 4.2.0).
Definition: nmt.c:1094
#define CO_NMT_CS_ENTER_PREOP
The NMT command specifier &#39;enter pre-operational&#39;.
Definition: nmt.h:46
#define CO_NMT_EC_CANID(id)
The CAN identifier used for both node guarding and heartbeat monitoring.
Definition: nmt.h:76
void can_timer_destroy(can_timer_t *timer)
Destroys a CAN timer.
Definition: net.c:407
int co_sub_dn(co_sub_t *sub, void *val)
Downloads (moves) a value into a CANopen sub-object if the refuse-write-on-download flag (CO_OBJ_FLAG...
Definition: obj.c:832
co_rpdo_t * co_nmt_get_rpdo(const co_nmt_t *nmt, co_unsigned16_t n)
Returns a pointer to a Receive-PDO service.
Definition: nmt.c:1787
A CANopen NMT master/slave service.
Definition: nmt.c:104
#define CO_DEFTYPE_UNSIGNED8
The data type (and object index) of an 8-bit unsigned integer.
Definition: type.h:44
co_nmt_cfg_t * co_nmt_cfg_create(can_net_t *net, co_dev_t *dev, co_nmt_t *nmt)
Creates a new CANopen NMT &#39;configuration request&#39;.
Definition: nmt_cfg.c:221
This header file is part of the CANopen library; it contains the Receive-PDO declarations.
can_timer_t * timer
A pointer to the CAN timer for node guarding.
Definition: nmt.c:70
#define CO_NUM_NODES
The maximum number of nodes in a CANopen network.
Definition: dev.h:56
void co_nmt_set_timeout(co_nmt_t *nmt, int timeout)
Sets the default SDO timeout used during the NMT &#39;boot slave&#39; and &#39;check configuration&#39; processes...
Definition: nmt.c:1447
#define CO_NMT_ST_RESET_COMM
The NMT sub-state &#39;reset communication&#39;.
Definition: nmt.h:67
void co_nmt_get_cfg_ind(const co_nmt_t *nmt, co_nmt_cfg_ind_t **pind, void **pdata)
Retrieves the indication function invoked when a CANopen NMT &#39;configuration request&#39; is received...
Definition: nmt.c:1263
co_ssdo_t * co_nmt_get_ssdo(const co_nmt_t *nmt, co_unsigned8_t n)
Returns a pointer to a Server-SDO service.
Definition: nmt.c:1809
static co_nmt_state_t * co_nmt_init_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
The &#39;NMT command received&#39; transition function of the &#39;initializing&#39; state.
Definition: nmt.c:2755
An error.
Definition: diag.h:49
static co_nmt_state_t * co_nmt_reset_node_on_enter(co_nmt_t *nmt)
The entry function of the &#39;reset application&#39; state.
Definition: nmt.c:2766
Invalid argument.
Definition: errnum.h:129
static co_unsigned32_t co_1f25_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
The download indication function for (all sub-objects of) CANopen object 1F25 (Configuration request)...
Definition: nmt.c:2169
co_nmt_t * co_nmt_create(can_net_t *net, co_dev_t *dev)
Creates a new CANopen NMT master/slave service.
Definition: nmt.c:1002
int co_nmt_boot_req(co_nmt_t *nmt, co_unsigned8_t id, int timeout)
Requests the NMT &#39;boot slave&#39; process for the specified node.
Definition: nmt.c:1518
static co_unsigned32_t co_1f80_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
The download indication function for (all sub-objects of) CANopen object 1F80 (NMT startup)...
Definition: nmt.c:2250
int lg_state
Indicates whether a life guarding error occurred (CO_NMT_EC_OCCURRED or CO_NMT_EC_RESOLVED).
Definition: nmt.c:153
co_unsigned8_t id
The pending node-ID.
Definition: nmt.c:110
A CANopen TIME producer/consumer service.
Definition: time.c:41
void co_nmt_set_up_ind(co_nmt_t *nmt, co_nmt_sdo_ind_t *ind, void *data)
Sets the indication function used to notify the user of the progress of the current SDO upload reques...
Definition: nmt.c:1314
void co_nmt_sdo_ind_t(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned16_t idx, co_unsigned8_t subidx, size_t size, size_t nbyte, void *data)
The type of an SDO request progress indication function, invoked by a CANopen NMT master to notify th...
Definition: nmt.h:246
int errno2c(int errnum)
Transforms a standard C error number to a native error code.
Definition: errnum.c:43
This header file is part of the utilities library; it contains the diagnostic declarations.
#define CO_SDO_AC_NO_WRITE
SDO abort code: Attempt to write a read only object.
Definition: sdo.h:90
#define __unlikely(x)
Indicates to the compiler that the expression is most-likely false.
Definition: features.h:286
A CANopen NMT &#39;configuration request&#39; service.
Definition: nmt_cfg.c:40
void co_nmt_on_sync(co_nmt_t *nmt, co_unsigned8_t cnt)
Implements the default behavior after a SYNC object is received or transmitted.
Definition: nmt.c:1345
void co_nmt_get_lg_ind(const co_nmt_t *nmt, co_nmt_lg_ind_t **pind, void **pdata)
Retrieves the indication function invoked when a life guarding event occurs.
Definition: nmt.c:1109
An NMT error control timeout event.
Definition: nmt.h:87
co_nmt_cs_ind_t * cs_ind
A pointer to the NMT command indication function.
Definition: nmt.c:128
#define CO_NMT_START_SRV
The services enabled in the NMT &#39;operational&#39; state.
Definition: nmt.c:631
void co_nmt_srv_fini(struct co_nmt_srv *srv)
Finalizes a CANopen NMT service manager.
Definition: nmt_srv.c:144
void * st_data
A pointer to user-specified data for st_ind.
Definition: nmt.c:171
int co_rpdo_sync(co_rpdo_t *pdo, co_unsigned8_t cnt)
Triggers the actuation of a received synchronous PDO.
Definition: rpdo.c:416
void co_nmt_hb_ind_t(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason, void *data)
The type of a CANopen NMT heartbeat indication function, invoked when a heartbeat event occurs (see s...
Definition: nmt.h:155
int timespec_cmp(const void *p1, const void *p2)
Compares two times.
Definition: time.h:224
static void co_nmt_hb_init(co_nmt_t *nmt)
Initializes the heartbeat consumer services.
Definition: nmt.c:3273
A CANopen device.
Definition: dev.c:38
size_t can_buf_read(struct can_buf *buf, struct can_msg *ptr, size_t n)
Reads, and removes, frames from a CAN frame buffer.
Definition: buf.h:268
co_dev_t * dev
A pointer to a CANopen device.
Definition: nmt.c:108
co_nmt_ng_ind_t * ng_ind
A pointer to the node guarding event indication function.
Definition: nmt.c:135
static co_nmt_state_t * co_nmt_default_on_boot(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es)
The default &#39;boot slave completed&#39; transition function.
Definition: nmt.c:2741
static void co_nmt_enter(co_nmt_t *nmt, co_nmt_state_t *next)
Enters the specified state of an NMT master/slave service and invokes the exit and entry functions...
Definition: nmt.c:2703
#define CO_NMT_CS_CANID
The CAN identifier used for NMT commands.
Definition: nmt.h:37
A CANopen NMT heartbeat consumer.
Definition: nmt_hb.c:33
void co_nmt_on_err(co_nmt_t *nmt, co_unsigned16_t eec, co_unsigned8_t er, const uint8_t msef[5])
Implements the default error handling behavior by generating an EMCY message with co_emcy_push() and ...
Definition: nmt.c:1371
static co_nmt_state_t * co_nmt_reset_comm_on_enter(co_nmt_t *nmt)
The entry function of the &#39;reset communication&#39; state.
Definition: nmt.c:2808
co_unsigned32_t assignment
The NMT slave assignment (object 1F81).
Definition: nmt.c:72
void can_recv_set_func(can_recv_t *recv, can_recv_func_t *func, void *data)
Sets the callback function used to process CAN frames with a receiver.
Definition: net.c:582
uint_least8_t flags
The flags (any combination of CAN_FLAG_IDE, CAN_FLAG_RTR, CAN_FLAG_EDL, CAN_FLAG_BRS and CAN_FLAG_ESI...
Definition: msg.h:95
co_obj_t * co_sub_get_obj(const co_sub_t *sub)
Returns the a pointer to the CANopen object containing the specified sub-object.
Definition: obj.c:458
int ng_state
Indicates whether a node guarding error occurred (CO_NMT_EC_OCCURRED or CO_NMT_EC_RESOLVED).
Definition: nmt.c:99
This is the internal header file of the NMT &#39;configuration request&#39; declarations. ...
co_sync_t * co_nmt_get_sync(const co_nmt_t *nmt)
Returns a pointer to the SYNC producer/consumer service.
Definition: nmt.c:1831
void * lg_data
A pointer to user-specified data for lg_ind.
Definition: nmt.c:157
static co_nmt_state_t *const co_nmt_bootup_state
The NMT &#39;boot-up&#39; state.
Definition: nmt.c:481
can_timer_t * can_timer_create(void)
Creates a new CAN timer.
Definition: net.c:382
int booted
A flag specifying whether the &#39;boot slave&#39; process has ended.
Definition: nmt.c:80
A struct containing the state of an NMT slave.
Definition: nmt.c:61
static co_nmt_state_t * co_nmt_bootup_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
The &#39;NMT command received&#39; transition function of the &#39;boot-up&#39; state.
Definition: nmt.c:2923
static co_nmt_state_t *const co_nmt_reset_node_state
The NMT &#39;reset application&#39; state.
Definition: nmt.c:449
static co_nmt_state_t * co_nmt_reset_comm_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
The &#39;NMT command received&#39; transition function of the &#39;reset communication&#39; state.
Definition: nmt.c:2888
void can_recv_stop(can_recv_t *recv)
Stops a CAN frame receiver from processing frames and unregisters it with the network interface...
Definition: net.c:613
can_recv_t * recv_000
A pointer to the CAN frame receiver for NMT messages.
Definition: nmt.c:126
static co_unsigned32_t co_100d_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
The download indication function for CANopen object 100D (Life time factor).
Definition: nmt.c:2024
This header file is part of the CANopen library; it contains the device description declarations...
co_nmt_lg_ind_t * lg_ind
A pointer to the life guarding event indication function.
Definition: nmt.c:155
co_nmt_hb_ind_t * hb_ind
A pointer to the heartbeat event indication function.
Definition: nmt.c:165
A warning.
Definition: diag.h:47
void co_val_fini(co_unsigned16_t type, void *val)
Finalizes a value of the specified data type.
Definition: val.c:273
int co_nmt_is_master(const co_nmt_t *nmt)
Returns 1 if the specified CANopen NMT service is a master, and 0 if not.
Definition: nmt.c:1423
static int co_nmt_recv_000(const struct can_msg *msg, void *data)
The CAN receive callback function for NMT messages.
Definition: nmt.c:2349
int can_net_send(can_net_t *net, const struct can_msg *msg)
Sends a CAN frame from a network interface.
Definition: net.c:308
co_unsigned32_t co_dev_cfg_hb(co_dev_t *dev, co_unsigned8_t id, co_unsigned16_t ms)
Configures heartbeat consumption for the specified node by updating CANopen object 1016 (Consumer hea...
Definition: nmt.c:637
static co_nmt_state_t * co_nmt_startup_slave(co_nmt_t *nmt)
The NMT slave startup procedure.
Definition: nmt.c:3167
void * dn_data
A pointer to user-specified data for dn_ind.
Definition: nmt.c:208
void * cfg_data
A pointer to user-specified data for cfg_ind.
Definition: nmt.c:204
co_unsigned16_t gt
The guard time (in milliseconds).
Definition: nmt.c:146
static void co_nmt_hb_fini(co_nmt_t *nmt)
Finalizes the heartbeat consumer services.
Definition: nmt.c:3309
#define CO_SDO_AC_PARAM
SDO abort code: General parameter incompatibility reason.
Definition: sdo.h:105
void co_nmt_cfg_ind_t(co_nmt_t *nmt, co_unsigned8_t id, co_csdo_t *sdo, void *data)
The type of a CANopen NMT &#39;update configuration&#39; indication function, invoked when a configuration re...
Definition: nmt.h:214
void co_nmt_hb_ind(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason, co_unsigned8_t st)
The CANopen NMT heartbeat indication function, invoked when a heartbeat event occurs.
Definition: nmt.c:1968
int halt
A flag indicating if the startup procedure was halted because of a mandatory slave boot failure...
Definition: nmt.c:189
#define CO_SDO_AC_NO_SUB
SDO abort code: Sub-index does not exist.
Definition: sdo.h:132
static int co_nmt_ng_timer(const struct timespec *tp, void *data)
The CAN timer callback function for node guarding.
Definition: nmt.c:2480
void co_nmt_set_dn_ind(co_nmt_t *nmt, co_nmt_sdo_ind_t *ind, void *data)
Sets the indication function used to notify the user of the progress of the current SDO download requ...
Definition: nmt.c:1294
co_nmt_sdo_ind_t * dn_ind
A pointer to the SDO download progress indication function.
Definition: nmt.c:206
void * lss_data
A pointer to user-specified data for lss_req.
Definition: nmt.c:183
int co_nmt_is_booting(const co_nmt_t *nmt, co_unsigned8_t id)
Returns 1 if the NMT &#39;boot slave&#39; process is currently running for the specified node, and 0 if not.
Definition: nmt.c:1571
size_t can_buf_write(struct can_buf *buf, const struct can_msg *ptr, size_t n)
Writes frames to a CAN frame buffer.
Definition: buf.h:300
int co_sdo_req_dn_val(struct co_sdo_req *req, co_unsigned16_t type, void *val, co_unsigned32_t *pac)
Copies the next segment of the specified CANopen SDO download request to the internal buffer and...
Definition: sdo.c:165
int co_nmt_lss_con(co_nmt_t *nmt)
Confirms the completion of the process when booting an NMT master.
Definition: nmt.c:1502
struct co_nmt_slave slaves[CO_NUM_NODES]
An array containing the state of each NMT slave.
Definition: nmt.c:191
co_unsigned16_t ntpdo
The number of Transmit-PDO services.
Definition: nmt_srv.h:46
can_net_t * co_nmt_get_net(const co_nmt_t *nmt)
Returns a pointer to the CAN network of an NMT master/slave service.
Definition: nmt.c:1036
co_emcy_t * emcy
A pointer to the EMCY producer/consumer service.
Definition: nmt_srv.h:60
co_nmt_boot_t * boot
A pointer to the NMT &#39;boot slave&#39; service.
Definition: nmt.c:82
A CANopen EMCY producer/consumer service.
Definition: emcy.c:83
static void co_nmt_ec_init(co_nmt_t *nmt)
Initializes the error control services.
Definition: nmt.c:3177
void * up_data
A pointer to user-specified data for up_ind.
Definition: nmt.c:212
void co_nmt_cfg_con_t(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned32_t ac, void *data)
The type of a CANopen NMT &#39;configuration request&#39; confirmation callback function, invoked when a conf...
Definition: nmt.h:227
co_lss_t * lss
A pointer to the LSS master/slave service.
Definition: nmt_srv.h:62
static int co_nmt_recv_700(const struct can_msg *msg, void *data)
The CAN receive callback function for NMT error control (node guarding RTR) messages.
Definition: nmt.c:2376
static co_nmt_state_t * co_nmt_stop_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
The &#39;NMT command received&#39; transition function of the &#39;stopped&#39; state.
Definition: nmt.c:3095
co_time_t * time
A pointer to the TIME producer/consumer service.
Definition: nmt_srv.h:58
void co_nmt_set_cfg_ind(co_nmt_t *nmt, co_nmt_cfg_ind_t *ind, void *data)
Sets the indication function invoked when a CANopen NMT &#39;configuration request&#39; process is received...
Definition: nmt.c:1274
co_nmt_boot_ind_t * boot_ind
A pointer to the NMT &#39;boot slave&#39; indication function.
Definition: nmt.c:198
co_sync_t * sync
A pointer to the SYNC producer/consumer service.
Definition: nmt_srv.h:56
#define LELY_CO_NMT_TIMEOUT
The default SDO timeout (in milliseconds) for the NMT &#39;boot slave&#39; and &#39;check configuration&#39; processe...
Definition: nmt.h:33
can_timer_t * cs_timer
A pointer to the CAN timer for sending buffered NMT messages.
Definition: nmt.c:178
void co_nmt_hb_destroy(co_nmt_hb_t *hb)
Destroys a CANopen NMT heartbeat consumer service.
Definition: nmt_hb.c:160
void co_nmt_cfg_con(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned32_t ac)
The CANopen NMT &#39;configuration request&#39; confirmation function, invoked when a configuration request c...
Definition: nmt.c:1950
This header file is part of the CANopen library; it contains the network management (NMT) declaration...
int co_dev_set_id(co_dev_t *dev, co_unsigned8_t id)
Sets the node-ID of a CANopen device.
Definition: dev.c:206
#define CAN_FLAG_RTR
The Remote Transmission Request (RTR) flag (unavailable in CAN FD format frames). ...
Definition: msg.h:47
co_nmt_hb_t ** hbs
An array of pointers to the heartbeat consumers.
Definition: nmt.c:161
#define CO_SDO_AC_DATA_DEV
SDO abort code: Data cannot be transferred or stored to the application because of the present device...
Definition: sdo.h:165
co_unsigned8_t co_sub_get_subidx(const co_sub_t *sub)
Returns the sub-index of a CANopen sub-object.
Definition: obj.c:466
Operation not permitted.
Definition: errnum.h:205
static co_nmt_state_t * co_nmt_stop_on_enter(co_nmt_t *nmt)
The entry function of the &#39;stopped&#39; state.
Definition: nmt.c:3076
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib.h> and defines any missing functionality.
static co_nmt_state_t * co_nmt_start_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
The &#39;NMT command received&#39; transition function of the &#39;operational&#39; state.
Definition: nmt.c:3062
co_time_t * co_nmt_get_time(const co_nmt_t *nmt)
Returns a pointer to the TIME producer/consumer service.
Definition: nmt.c:1839
void co_nmt_lg_ind_t(co_nmt_t *nmt, int state, void *data)
The type of a CANopen NMT life guarding indication function, invoked when a life guarding event occur...
Definition: nmt.h:139
co_obj_t * co_dev_find_obj(const co_dev_t *dev, co_unsigned16_t idx)
Finds an object in the object dictionary of a CANopen device.
Definition: dev.c:279
#define CO_NMT_CS_START
The NMT command specifier &#39;start&#39;.
Definition: nmt.h:40
A CAN frame receiver.
Definition: net.c:99
static void co_nmt_emit_cs(co_nmt_t *nmt, co_unsigned8_t cs)
Invokes the &#39;NMT command received&#39; transition function of the current state of an NMT master/slave se...
Definition: nmt.c:2719
static void co_nmt_slaves_fini(co_nmt_t *nmt)
Finalizes NMT slave management.
Definition: nmt.c:3354
co_unsigned8_t ltf
The lifetime factor.
Definition: nmt.c:92
#define CO_NMT_SRV_LSS
The LSS master/slave service.
Definition: nmt_srv.h:81
co_unsigned16_t ms
The producer heartbeat time (in milliseconds).
Definition: nmt.c:159
co_nmt_cfg_t * cfg
A pointer to the NMT &#39;update configuration&#39; service.
Definition: nmt.c:84
co_nmt_state_t *(* on_boot)(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es)
A pointer to the transition function invoked when an &#39;boot slave&#39; process completes.
Definition: nmt.c:416
co_unsigned16_t co_sub_get_type(const co_sub_t *sub)
Returns the data type of a CANopen sub-object.
Definition: obj.c:508
co_sub_t * co_obj_find_sub(const co_obj_t *obj, co_unsigned8_t subidx)
Finds a sub-object in a CANopen object.
Definition: obj.c:207
static co_unsigned32_t co_100c_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
The download indication function for CANopen object 100C (Guard time).
Definition: nmt.c:1984
A CANopen SYNC producer/consumer service.
Definition: sync.c:39
co_nmt_boot_t * co_nmt_boot_create(can_net_t *net, co_dev_t *dev, co_nmt_t *nmt)
Creates a new CANopen NMT &#39;boot slave&#39; service.
Definition: nmt_boot.c:841
co_unsigned16_t co_obj_get_idx(const co_obj_t *obj)
Returns the index of a CANopen object.
Definition: obj.c:136
co_unsigned8_t nhb
The number of heartbeat consumers.
Definition: nmt.c:163
int co_nmt_cfg_cfg_res(co_nmt_cfg_t *cfg, co_unsigned32_t ac)
Indicates the result of the &#39;update configuration&#39; step of an NMT &#39;configuration request&#39;.
Definition: nmt_cfg.c:286
static void co_nmt_up_ind(const co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx, size_t size, size_t nbyte, void *data)
The SDO upload progress indication function.
Definition: nmt.c:2689
static void default_ng_ind(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason, void *data)
The default node guarding event handler.
Definition: nmt.c:2640
void co_nmt_get_sync_ind(const co_nmt_t *nmt, co_nmt_sync_ind_t **pind, void **pdata)
Retrieves the indication function invoked by co_nmt_on_sync() after all PDOs have been transmitted/pr...
Definition: nmt.c:1325
#define CO_NMT_CS_RESET_COMM
The NMT command specifier &#39;reset communication&#39;.
Definition: nmt.h:52
co_nmt_cfg_ind_t * cfg_ind
A pointer to the NMT &#39;configuration request&#39; indication function.
Definition: nmt.c:202
void co_nmt_get_hb_ind(const co_nmt_t *nmt, co_nmt_hb_ind_t **pind, void **pdata)
Retrieves the indication function invoked when a heartbeat event occurs.
Definition: nmt.c:1138
int co_nmt_cfg_cfg_req(co_nmt_cfg_t *cfg, co_unsigned8_t id, int timeout, co_csdo_ind_t *dn_ind, co_csdo_ind_t *up_ind, void *data)
Starts a CANopen NMT &#39;configuration request&#39;.
Definition: nmt_cfg.c:255
co_nmt_t * nmt
A pointer to the NMT master service.
Definition: nmt.c:63
A CANopen object.
Definition: obj.h:32
static co_unsigned32_t co_1016_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
The download indication function for CANopen object 1016 (Consumer heartbeat time).
Definition: nmt.c:2064
void * dcf_node
The concise DCF of the application parameters.
Definition: nmt.c:112
This header file is part of the CANopen library; it contains the object dictionary declarations...
static int co_nmt_cs_timer(const struct timespec *tp, void *data)
The CAN timer callback function for sending buffered NMT messages.
Definition: nmt.c:2546
void co_nmt_ng_ind_t(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason, void *data)
The type of a CANopen NMT node guarding indication function, invoked when a node guarding event occur...
Definition: nmt.h:126
void co_nmt_hb_set_1016(co_nmt_hb_t *hb, co_unsigned8_t id, co_unsigned16_t ms)
Processes the value of CANopen object 1016 (Consumer heartbeat time) for the specified heartbeat cons...
Definition: nmt_hb.c:169
void can_recv_start(can_recv_t *recv, can_net_t *net, uint_least32_t id, uint_least8_t flags)
Registers a CAN frame receiver with a network interface and starts processing frames.
Definition: net.c:591
Function not supported.
Definition: errnum.h:181
co_unsigned8_t st
The state of the NMT service (including the toggle bit).
Definition: nmt.c:144
This is the internal header file of the NMT service manager declarations.
void co_nmt_set_boot_ind(co_nmt_t *nmt, co_nmt_boot_ind_t *ind, void *data)
Sets the indication function invoked when a CANopen NMT &#39;boot slave&#39; process completes.
Definition: nmt.c:1254
void co_nmt_sync_ind_t(co_nmt_t *nmt, co_unsigned8_t cnt, void *data)
The type of a SYNC indication function, invoked by co_nmt_on_sync() after PDOs are transmitted/proces...
Definition: nmt.h:260
static void co_nmt_emit_boot(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es)
Invokes the &#39;boot slave completed&#39; transition function of the current state of an NMT master service...
Definition: nmt.c:2731
This is the internal header file of the NMT heartbeat consumer declarations.
static co_nmt_state_t *const co_nmt_init_state
The &#39;initializing&#39; state.
Definition: nmt.c:439
co_sub_t * co_dev_find_sub(const co_dev_t *dev, co_unsigned16_t idx, co_unsigned8_t subidx)
Finds a sub-object in the object dictionary of a CANopen device.
Definition: dev.c:290
#define CO_SDO_AC_NO_OBJ
SDO abort code: Object does not exist in the object dictionary.
Definition: sdo.h:93
static co_nmt_state_t *const co_nmt_start_state
The NMT &#39;operational&#39; state.
Definition: nmt.c:536
co_unsigned8_t ltf
The lifetime factor.
Definition: nmt.c:148
co_csdo_t * co_nmt_get_csdo(const co_nmt_t *nmt, co_unsigned8_t n)
Returns a pointer to a Client-SDO service.
Definition: nmt.c:1820
An NMT error control state change event.
Definition: nmt.h:89
static co_nmt_state_t * co_nmt_preop_on_boot(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es)
The &#39;boot slave completed&#39; transition function of the &#39;pre-operational&#39; state.
Definition: nmt.c:2975
void co_nmt_get_up_ind(const co_nmt_t *nmt, co_nmt_sdo_ind_t **pind, void **pdata)
Retrieves the indication function used to notify the user of the progress of the current SDO upload r...
Definition: nmt.c:1303
static co_nmt_state_t *const co_nmt_reset_comm_state
The NMT &#39;reset communication&#39; state.
Definition: nmt.c:467
int co_nmt_get_timeout(const co_nmt_t *nmt)
Returns the default SDO timeout used during the NMT &#39;boot slave&#39; and &#39;check configuration&#39; processes...
Definition: nmt.c:1439
An informational message.
Definition: diag.h:45