Lely core libraries  2.3.4
nmt.c
Go to the documentation of this file.
1 
24 #include "co.h"
25 #include <lely/util/diag.h>
26 #if !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 #if !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 #if !LELY_NO_CO_RPDO
38 #include <lely/co/rpdo.h>
39 #endif
40 #include <lely/co/sdo.h>
41 #if !LELY_NO_CO_TPDO
42 #include <lely/co/tpdo.h>
43 #endif
44 #include <lely/co/val.h>
45 #if !LELY_NO_CO_NMT_BOOT
46 #include "nmt_boot.h"
47 #endif
48 #if !LELY_NO_CO_NMT_CFG
49 #include "nmt_cfg.h"
50 #endif
51 #include "nmt_hb.h"
52 #include "nmt_srv.h"
53 
54 #include <assert.h>
55 #include <stdlib.h>
56 #if LELY_NO_MALLOC
57 #include <string.h>
58 #endif
59 
60 #if LELY_NO_MALLOC
61 #ifndef CO_NMT_CAN_BUF_SIZE
62 
66 #define CO_NMT_CAN_BUF_SIZE 16
67 #endif
68 #ifndef CO_NMT_MAX_NHB
69 
74 #define CO_NMT_MAX_NHB CO_NUM_NODES
75 #endif
76 #endif // LELY_NO_MALLOC
77 
78 struct __co_nmt_state;
80 typedef const struct __co_nmt_state co_nmt_state_t;
81 
82 #if !LELY_NO_CO_MASTER
83 struct co_nmt_slave {
92 #if !LELY_NO_CO_NG
95 #endif
96  co_unsigned32_t assignment;
99  co_unsigned8_t est;
101  co_unsigned8_t rst;
102 #if !LELY_NO_CO_NMT_BOOT
103  char es;
106  unsigned booting : 1;
107 #endif
108 #if !LELY_NO_CO_NMT_CFG
109 
113  unsigned configuring : 1;
114 #endif
115  unsigned bootup : 1;
117 #if !LELY_NO_CO_NMT_BOOT
118  unsigned booted : 1;
122 #endif
123 #if !LELY_NO_CO_NMT_CFG
124  co_nmt_cfg_t *cfg;
129  void *cfg_data;
130 #endif
131 #if !LELY_NO_CO_NG
132  co_unsigned16_t gt;
135  co_unsigned8_t ltf;
137  co_unsigned8_t rtr;
142  int ng_state;
143 #endif
144 };
145 #endif
146 
148 struct __co_nmt {
154  co_unsigned8_t id;
155 #if !LELY_NO_CO_DCF_RESTORE
156  void *dcf_node;
158 #endif
159  void *dcf_comm;
164  struct co_nmt_srv srv;
166  co_unsigned32_t startup;
167 #if !LELY_NO_CO_MASTER
168  int master;
170 #endif
176  void *cs_data;
179 #if !LELY_NO_CO_MASTER && !LELY_NO_CO_NG
183  void *ng_data;
184 #endif
185 
190  co_unsigned8_t st;
191 #if !LELY_NO_CO_NG
192  co_unsigned16_t gt;
195  co_unsigned8_t ltf;
200  int lg_state;
204  void *lg_data;
205 #endif
206  co_unsigned16_t ms;
209 #if LELY_NO_MALLOC
210  co_nmt_hb_t *hbs[CO_NMT_MAX_NHB];
211 #else
213 #endif
214  co_unsigned8_t nhb;
219  void *hb_data;
223  void *st_data;
224 #if !LELY_NO_CO_MASTER
225  struct can_buf buf;
227 #if LELY_NO_MALLOC
228 
232  struct can_msg begin[CO_NMT_CAN_BUF_SIZE];
233 #endif
234  struct timespec inhibit;
238 #if !LELY_NO_CO_LSS
242  void *lss_data;
243 #endif
244 
248  int halt;
255  int timeout;
256 #if !LELY_NO_CO_NMT_BOOT
260  void *boot_data;
261 #endif
262 #if !LELY_NO_CO_NMT_CFG
266  void *cfg_data;
267 #endif
271  void *dn_data;
275  void *up_data;
276 #endif
280  void *sync_data;
281 #if !LELY_NO_CO_TPDO
282 
293 #endif
294 };
295 
296 #if !LELY_NO_CO_NG
297 
303 static co_unsigned32_t co_100c_dn_ind(
304  co_sub_t *sub, struct co_sdo_req *req, void *data);
305 
311 static co_unsigned32_t co_100d_dn_ind(
312  co_sub_t *sub, struct co_sdo_req *req, void *data);
313 
314 #endif // !LELY_NO_CO_NG
315 
322 static co_unsigned32_t co_1016_dn_ind(
323  co_sub_t *sub, struct co_sdo_req *req, void *data);
324 
331 static co_unsigned32_t co_1017_dn_ind(
332  co_sub_t *sub, struct co_sdo_req *req, void *data);
333 
334 #if !LELY_NO_CO_NMT_CFG
335 
341 static co_unsigned32_t co_1f25_dn_ind(
342  co_sub_t *sub, struct co_sdo_req *req, void *data);
343 #endif
344 
351 static co_unsigned32_t co_1f80_dn_ind(
352  co_sub_t *sub, struct co_sdo_req *req, void *data);
353 
354 #if !LELY_NO_CO_MASTER
355 
361 static co_unsigned32_t co_1f82_dn_ind(
362  co_sub_t *sub, struct co_sdo_req *req, void *data);
363 #endif
364 
366 static int co_nmt_recv_000(const struct can_msg *msg, void *data);
367 
375 static int co_nmt_recv_700(const struct can_msg *msg, void *data);
376 
377 #if !LELY_NO_CO_MASTER && !LELY_NO_CO_NG
378 static int co_nmt_ng_timer(const struct timespec *tp, void *data);
380 #endif
381 
387 static int co_nmt_ec_timer(const struct timespec *tp, void *data);
388 
389 #if !LELY_NO_CO_MASTER
390 
395 static int co_nmt_cs_timer(const struct timespec *tp, void *data);
396 #endif
397 
405 static void co_nmt_st_ind(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st);
406 
407 #if !LELY_NO_CO_NG
408 
409 #if !LELY_NO_CO_MASTER
410 static void default_ng_ind(co_nmt_t *nmt, co_unsigned8_t id, int state,
412  int reason, void *data);
413 #endif
414 
416 static void default_lg_ind(co_nmt_t *nmt, int state, void *data);
417 
418 #endif // !LELY_NO_CO_NG
419 
421 static void default_hb_ind(co_nmt_t *nmt, co_unsigned8_t id, int state,
422  int reason, void *data);
423 
425 static void default_st_ind(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st,
426  void *data);
427 
428 #if !LELY_NO_CO_NMT_BOOT || !LELY_NO_CO_NMT_CFG
429 
431 static void co_nmt_dn_ind(const co_csdo_t *sdo, co_unsigned16_t idx,
432  co_unsigned8_t subidx, size_t size, size_t nbyte, void *data);
433 
435 static void co_nmt_up_ind(const co_csdo_t *sdo, co_unsigned16_t idx,
436  co_unsigned8_t subidx, size_t size, size_t nbyte, void *data);
437 
438 #endif
439 
440 #if !LELY_NO_CO_TPDO
441 static void co_nmt_tpdo_event_ind(co_unsigned16_t n, void *data);
443 #if !LELY_NO_CO_MPDO
444 static void co_nmt_sam_mpdo_event_ind(co_unsigned16_t n, co_unsigned16_t idx,
446  co_unsigned8_t subidx, void *data);
447 #endif
448 #endif // !LELY_NO_CO_TPDO
449 
454 static void co_nmt_enter(co_nmt_t *nmt, co_nmt_state_t *next);
455 
465 static inline void co_nmt_emit_cs(co_nmt_t *nmt, co_unsigned8_t cs);
466 
467 #if !LELY_NO_CO_NMT_BOOT
468 
477 static inline void co_nmt_emit_boot(
478  co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es);
479 #endif
480 
484  co_nmt_state_t *(*on_enter)(co_nmt_t *nmt);
496  co_nmt_state_t *(*on_cs)(co_nmt_t *nmt, co_unsigned8_t cs);
497 #if !LELY_NO_CO_NMT_BOOT
498 
510  co_nmt_state_t *(*on_boot)(co_nmt_t *nmt, co_unsigned8_t id,
511  co_unsigned8_t st, char es);
512 #endif
513  void (*on_leave)(co_nmt_t *nmt);
515 };
516 
517 #define LELY_CO_DEFINE_STATE(name, ...) \
518  static co_nmt_state_t *const name = &(co_nmt_state_t){ __VA_ARGS__ };
519 
520 #if !LELY_NO_CO_NMT_BOOT
523  co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es);
524 #endif
525 
527 static co_nmt_state_t *co_nmt_init_on_cs(co_nmt_t *nmt, co_unsigned8_t cs);
528 
530 // clang-format off
531 LELY_CO_DEFINE_STATE(co_nmt_init_state,
532  .on_cs = &co_nmt_init_on_cs
533 )
534 // clang-format on
535 
538 
540 // clang-format off
541 LELY_CO_DEFINE_STATE(co_nmt_reset_node_state,
542  .on_enter = &co_nmt_reset_node_on_enter
543 )
544 // clang-format on
545 
548 
554  co_nmt_t *nmt, co_unsigned8_t cs);
555 
557 // clang-format off
558 LELY_CO_DEFINE_STATE(co_nmt_reset_comm_state,
559  .on_enter = &co_nmt_reset_comm_on_enter,
560  .on_cs = &co_nmt_reset_comm_on_cs
561 )
562 // clang-format on
563 
566 
568 static co_nmt_state_t *co_nmt_bootup_on_cs(co_nmt_t *nmt, co_unsigned8_t cs);
569 
571 // clang-format off
572 LELY_CO_DEFINE_STATE(co_nmt_bootup_state,
573  .on_enter = &co_nmt_bootup_on_enter,
574  .on_cs = &co_nmt_bootup_on_cs
575 )
576 // clang-format on
577 
580 
585 static co_nmt_state_t *co_nmt_preop_on_cs(co_nmt_t *nmt, co_unsigned8_t cs);
586 
587 #if !LELY_NO_CO_NMT_BOOT
588 
593  co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es);
594 #endif
595 
597 // clang-format off
598 #if LELY_NO_CO_NMT_BOOT
599 LELY_CO_DEFINE_STATE(co_nmt_preop_state,
600  .on_enter = &co_nmt_preop_on_enter,
601  .on_cs = &co_nmt_preop_on_cs
602 )
603 #else
604 LELY_CO_DEFINE_STATE(co_nmt_preop_state,
605  .on_enter = &co_nmt_preop_on_enter,
606  .on_cs = &co_nmt_preop_on_cs,
607  .on_boot = &co_nmt_preop_on_boot
608 )
609 #endif
610 // clang-format on
611 
614 
616 static co_nmt_state_t *co_nmt_start_on_cs(co_nmt_t *nmt, co_unsigned8_t cs);
617 
619 // clang-format off
620 #if LELY_NO_CO_NMT_BOOT
621 LELY_CO_DEFINE_STATE(co_nmt_start_state,
622  .on_enter = &co_nmt_start_on_enter,
623  .on_cs = &co_nmt_start_on_cs
624 )
625 #else
626 LELY_CO_DEFINE_STATE(co_nmt_start_state,
627  .on_enter = &co_nmt_start_on_enter,
628  .on_cs = &co_nmt_start_on_cs,
629  .on_boot = &co_nmt_default_on_boot
630 )
631 #endif
632 // clang-format on
633 
636 
638 static co_nmt_state_t *co_nmt_stop_on_cs(co_nmt_t *nmt, co_unsigned8_t cs);
639 
641 // clang-format off
642 #if LELY_NO_CO_NMT_BOOT
643 LELY_CO_DEFINE_STATE(co_nmt_stop_state,
644  .on_enter = &co_nmt_stop_on_enter,
645  .on_cs = &co_nmt_stop_on_cs
646 )
647 #else
648 LELY_CO_DEFINE_STATE(co_nmt_stop_state,
649  .on_enter = &co_nmt_stop_on_enter,
650  .on_cs = &co_nmt_stop_on_cs,
651  .on_boot = &co_nmt_default_on_boot
652 )
653 #endif
654 // clang-format on
655 
656 #undef LELY_CO_DEFINE_STATE
657 
660 
661 #if !LELY_NO_CO_MASTER
664 #endif
665 
668 
670 static void co_nmt_ec_init(co_nmt_t *nmt);
671 
673 static void co_nmt_ec_fini(co_nmt_t *nmt);
674 
681 static void co_nmt_ec_update(co_nmt_t *nmt);
682 
691 static int co_nmt_ec_send_res(co_nmt_t *nmt, co_unsigned8_t st);
692 
694 static void co_nmt_hb_init(co_nmt_t *nmt);
695 
697 static void co_nmt_hb_fini(co_nmt_t *nmt);
698 
699 #if !LELY_NO_CO_MASTER
700 
701 #if !LELY_NO_CO_NMT_BOOT || !LELY_NO_CO_NMT_CFG
704  co_nmt_t *nmt, co_unsigned8_t id, co_unsigned16_t *pms);
705 #endif
706 
708 static void co_nmt_slaves_init(co_nmt_t *nmt);
709 
711 static void co_nmt_slaves_fini(co_nmt_t *nmt);
712 
713 #if !LELY_NO_CO_NMT_BOOT
714 
720 static int co_nmt_slaves_boot(co_nmt_t *nmt);
721 #endif
722 
728 static int co_nmt_chk_bootup_slaves(const co_nmt_t *nmt);
729 
730 #endif
731 
733 #define CO_NMT_PREOP_SRV \
734  (CO_NMT_STOP_SRV | CO_NMT_SRV_SDO | CO_NMT_SRV_SYNC | CO_NMT_SRV_TIME \
735  | CO_NMT_SRV_EMCY)
736 
738 #define CO_NMT_START_SRV (CO_NMT_PREOP_SRV | CO_NMT_SRV_PDO)
739 
741 #define CO_NMT_STOP_SRV CO_NMT_SRV_LSS
742 
743 co_unsigned32_t
744 co_dev_cfg_hb(co_dev_t *dev, co_unsigned8_t id, co_unsigned16_t ms)
745 {
746  assert(dev);
747 
748  co_obj_t *obj_1016 = co_dev_find_obj(dev, 0x1016);
749  if (!obj_1016)
750  return CO_SDO_AC_NO_OBJ;
751 
752  co_unsigned8_t n = co_obj_get_val_u8(obj_1016, 0x00);
753  co_unsigned8_t i = 0;
754  // If the node-ID is valid, find an existing heartbeat consumer with the
755  // same ID.
756  if (id && id <= CO_NUM_NODES) {
757  for (i = 1; i <= n; i++) {
758  co_unsigned32_t val_i = co_obj_get_val_u32(obj_1016, i);
759  co_unsigned8_t id_i = (val_i >> 16) & 0xff;
760  if (id_i == id)
761  break;
762  }
763  }
764  // If the node-ID is invalid or no heartbeat consumer exists, find an
765  // unused consumer.
766  if (!i || i > n) {
767  for (i = 1; i <= n; i++) {
768  co_unsigned32_t val_i = co_obj_get_val_u32(obj_1016, i);
769  co_unsigned8_t id_i = (val_i >> 16) & 0xff;
770  if (!id_i || id_i > CO_NUM_NODES)
771  break;
772  }
773  }
774 
775  if (!i || i > n)
776  return CO_SDO_AC_NO_SUB;
777  co_sub_t *sub = co_obj_find_sub(obj_1016, i);
778  if (!sub)
779  return CO_SDO_AC_NO_SUB;
780 
781  co_unsigned32_t val = ((co_unsigned32_t)id << 16) | ms;
782  return co_sub_dn_ind_val(sub, CO_DEFTYPE_UNSIGNED32, &val);
783 }
784 
785 const char *
787 {
788  switch (es) {
789  case 'A': return "The CANopen device is not listed in object 1F81.";
790  case 'B':
791  return "No response received for upload request of object 1000.";
792  case 'C':
793  return "Value of object 1000 from CANopen device is different to value in object 1F84 (Device type).";
794  case 'D':
795  return "Value of object 1018 sub-index 01 from CANopen device is different to value in object 1F85 (Vendor-ID).";
796  case 'E':
797  return "Heartbeat event. No heartbeat message received from CANopen device.";
798  case 'F':
799  return "Node guarding event. No confirmation for guarding request received from CANopen device.";
800  case 'G':
801  return "Objects for program download are not configured or inconsistent.";
802  case 'H':
803  return "Software update is required, but not allowed because of configuration or current status.";
804  case 'I':
805  return "Software update is required, but program download failed.";
806  case 'J': return "Configuration download failed.";
807  case 'K':
808  return "Heartbeat event during start error control service. No heartbeat message received from CANopen device during start error control service.";
809  case 'L': return "NMT slave was initially operational.";
810  case 'M':
811  return "Value of object 1018 sub-index 02 from CANopen device is different to value in object 1F86 (Product code).";
812  case 'N':
813  return "Value of object 1018 sub-index 03 from CANopen device is different to value in object 1F87 (Revision number).";
814  case 'O':
815  return "Value of object 1018 sub-index 04 from CANopen device is different to value in object 1F88 (Serial number).";
816  default: return "Unknown error status";
817  }
818 }
819 
820 void *
821 __co_nmt_alloc(void)
822 {
823  void *ptr = malloc(sizeof(struct __co_nmt));
824 #if !LELY_NO_ERRNO
825  if (!ptr)
826  set_errc(errno2c(errno));
827 #endif
828  return ptr;
829 }
830 
831 void
832 __co_nmt_free(void *ptr)
833 {
834  free(ptr);
835 }
836 
837 struct __co_nmt *
838 __co_nmt_init(struct __co_nmt *nmt, can_net_t *net, co_dev_t *dev)
839 {
840  assert(nmt);
841  assert(net);
842  assert(dev);
843 
844  int errc = 0;
845 
846  nmt->net = net;
847  nmt->dev = dev;
848 
849  nmt->id = co_dev_get_id(nmt->dev);
850 
851 #if !LELY_NO_CO_DCF_RESTORE
852  // Store a concise DCF containing the application parameters.
853  if (co_dev_write_dcf(nmt->dev, 0x2000, 0x9fff, &nmt->dcf_node) == -1) {
854  errc = get_errc();
855  goto error_write_dcf_node;
856  }
857 #endif
858 
859  // Store a concise DCF containing the communication parameters.
860  if (co_dev_write_dcf(nmt->dev, 0x1000, 0x1fff, &nmt->dcf_comm) == -1) {
861  errc = get_errc();
862  goto error_write_dcf_comm;
863  }
864 
865  nmt->state = NULL;
866 
867  co_nmt_srv_init(&nmt->srv, nmt);
868 
869  nmt->startup = 0;
870 #if !LELY_NO_CO_MASTER
871  nmt->master = 0;
872 #endif
873 
874  // Create the CAN frame receiver for NMT messages.
875  nmt->recv_000 = can_recv_create();
876  if (!nmt->recv_000) {
877  errc = get_errc();
878  goto error_create_recv_000;
879  }
881 
882  nmt->cs_ind = NULL;
883  nmt->cs_data = NULL;
884 
885  // Create the CAN frame receiver for node guarding RTR and boot-up
886  // messages.
887  nmt->recv_700 = can_recv_create();
888  if (!nmt->recv_700) {
889  errc = get_errc();
890  goto error_create_recv_700;
891  }
893 
894 #if !LELY_NO_CO_MASTER && !LELY_NO_CO_NG
895  nmt->ng_ind = &default_ng_ind;
896  nmt->ng_data = NULL;
897 #endif
898 
899  nmt->ec_timer = can_timer_create();
900  if (!nmt->ec_timer) {
901  errc = get_errc();
902  goto error_create_ec_timer;
903  }
905 
906  nmt->st = CO_NMT_ST_BOOTUP;
907 #if !LELY_NO_CO_NG
908  nmt->gt = 0;
909  nmt->ltf = 0;
910 
912  nmt->lg_ind = &default_lg_ind;
913  nmt->lg_data = NULL;
914 #endif
915 
916  nmt->ms = 0;
917 
918 #if LELY_NO_MALLOC
919  memset(nmt->hbs, 0, CO_NMT_MAX_NHB * sizeof(*nmt->hbs));
920 #else
921  nmt->hbs = NULL;
922 #endif
923  nmt->nhb = 0;
924  nmt->hb_ind = &default_hb_ind;
925  nmt->hb_data = NULL;
926 
927  nmt->st_ind = &default_st_ind;
928  nmt->st_data = NULL;
929 
930 #if !LELY_NO_CO_MASTER
931  // Create a CAN fame buffer for pending NMT messages that will be sent
932  // once the inhibit time has elapsed.
933 #if LELY_NO_MALLOC
934  can_buf_init(&nmt->buf, nmt->begin, CO_NMT_CAN_BUF_SIZE);
935  memset(nmt->begin, 0, CO_NMT_CAN_BUF_SIZE * sizeof(*nmt->begin));
936 #else
937  can_buf_init(&nmt->buf, NULL, 0);
938 #endif
939 
940  can_net_get_time(nmt->net, &nmt->inhibit);
941  nmt->cs_timer = can_timer_create();
942  if (!nmt->cs_timer) {
943  errc = get_errc();
944  goto error_create_cs_timer;
945  }
947 
948 #if !LELY_NO_CO_LSS
949  nmt->lss_req = NULL;
950  nmt->lss_data = NULL;
951 #endif
952 
953  nmt->halt = 0;
954 
955  for (co_unsigned8_t id = 1; id <= CO_NUM_NODES; id++) {
956  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
957  slave->nmt = nmt;
958 
959  slave->recv = NULL;
960 #if !LELY_NO_CO_NG
961  slave->timer = NULL;
962 #endif
963 
964  slave->assignment = 0;
965  slave->est = 0;
966  slave->rst = 0;
967 
968 #if !LELY_NO_CO_NMT_BOOT
969  slave->es = 0;
970 
971  slave->booting = 0;
972  slave->booted = 0;
973 
974  slave->boot = NULL;
975 #endif
976 
977 #if !LELY_NO_CO_NMT_CFG
978  slave->configuring = 0;
979 
980  slave->cfg = NULL;
981  slave->cfg_con = NULL;
982  slave->cfg_data = NULL;
983 #endif
984 
985 #if !LELY_NO_CO_NG
986  slave->gt = 0;
987  slave->ltf = 0;
988  slave->rtr = 0;
989  slave->ng_state = CO_NMT_EC_RESOLVED;
990 #endif
991  }
992 
993  for (co_unsigned8_t id = 1; id <= CO_NUM_NODES; id++) {
994  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
995 
996  slave->recv = can_recv_create();
997  if (!slave->recv) {
998  errc = get_errc();
999  goto error_init_slave;
1000  }
1002 
1003 #if !LELY_NO_CO_NG
1004  slave->timer = can_timer_create();
1005  if (!slave->timer) {
1006  errc = get_errc();
1007  goto error_init_slave;
1008  }
1009  can_timer_set_func(slave->timer, &co_nmt_ng_timer, slave);
1010 #endif
1011  }
1012 
1014 
1015 #if !LELY_NO_CO_NMT_BOOT
1016  nmt->boot_ind = NULL;
1017  nmt->boot_data = NULL;
1018 #endif
1019 #if !LELY_NO_CO_NMT_CFG
1020  nmt->cfg_ind = NULL;
1021  nmt->cfg_data = NULL;
1022 #endif
1023  nmt->dn_ind = NULL;
1024  nmt->dn_data = NULL;
1025  nmt->up_ind = NULL;
1026  nmt->up_data = NULL;
1027 #endif
1028  nmt->sync_ind = NULL;
1029  nmt->sync_data = NULL;
1030 
1031 #if !LELY_NO_CO_TPDO
1032  nmt->tpdo_event_wait = 0;
1033  for (int i = 0; i < CO_NUM_PDOS / LONG_BIT; i++)
1034  nmt->tpdo_event_mask[i] = 0;
1035 
1036  // Set the Transmit-PDO event indication function.
1038 
1039 #if !LELY_NO_CO_MPDO
1040  // Set the SAM-MPDO event indication function.
1043 #endif
1044 #endif // !LELY_NO_CO_TPDO
1045 
1046 #if !LELY_NO_CO_NG
1047  // Set the download indication function for the guard time.
1048  co_obj_t *obj_100c = co_dev_find_obj(nmt->dev, 0x100c);
1049  if (obj_100c)
1050  co_obj_set_dn_ind(obj_100c, &co_100c_dn_ind, nmt);
1051 
1052  // Set the download indication function for the life time factor.
1053  co_obj_t *obj_100d = co_dev_find_obj(nmt->dev, 0x100d);
1054  if (obj_100d)
1055  co_obj_set_dn_ind(obj_100d, &co_100d_dn_ind, nmt);
1056 #endif
1057 
1058  // Set the download indication function for the consumer heartbeat time.
1059  co_obj_t *obj_1016 = co_dev_find_obj(nmt->dev, 0x1016);
1060  if (obj_1016)
1061  co_obj_set_dn_ind(obj_1016, &co_1016_dn_ind, nmt);
1062 
1063  // Set the download indication function for the producer heartbeat time.
1064  co_obj_t *obj_1017 = co_dev_find_obj(nmt->dev, 0x1017);
1065  if (obj_1017)
1066  co_obj_set_dn_ind(obj_1017, &co_1017_dn_ind, nmt);
1067 
1068 #if !LELY_NO_CO_NMT_CFG
1069  // Set the download indication function for the configuration request
1070  // value.
1071  co_obj_t *obj_1f25 = co_dev_find_obj(nmt->dev, 0x1f25);
1072  if (obj_1f25)
1073  co_obj_set_dn_ind(obj_1f25, &co_1f25_dn_ind, nmt);
1074 #endif
1075 
1076  // Set the download indication function for the NMT startup value.
1077  co_obj_t *obj_1f80 = co_dev_find_obj(nmt->dev, 0x1f80);
1078  if (obj_1f80)
1079  co_obj_set_dn_ind(obj_1f80, &co_1f80_dn_ind, nmt);
1080 
1081 #if !LELY_NO_CO_MASTER
1082  // Set the download indication function for the request NMT value.
1083  co_obj_t *obj_1f82 = co_dev_find_obj(nmt->dev, 0x1f82);
1084  if (obj_1f82)
1085  co_obj_set_dn_ind(obj_1f82, &co_1f82_dn_ind, nmt);
1086 #endif
1087 
1089  return nmt;
1090 
1091 // #if !LELY_NO_CO_MASTER
1092 // if (obj_1f82)
1093 // co_obj_set_dn_ind(obj_1f82, NULL, NULL);
1094 // #endif
1095 // if (obj_1f80)
1096 // co_obj_set_dn_ind(obj_1f80, NULL, NULL);
1097 // #if !LELY_NO_CO_NMT_CFG
1098 // if (obj_1f25)
1099 // co_obj_set_dn_ind(obj_1f25, NULL, NULL);
1100 // #endif
1101 // if (obj_1017)
1102 // co_obj_set_dn_ind(obj_1017, NULL, NULL);
1103 // if (obj_1016)
1104 // co_obj_set_dn_ind(obj_1016, NULL, NULL);
1105 // if (obj_100d)
1106 // co_obj_set_dn_ind(obj_100d, NULL, NULL);
1107 // if (obj_100c)
1108 // co_obj_set_dn_ind(obj_100c, NULL, NULL);
1109 // #if !LELY_NO_CO_TPDO
1110 // co_dev_set_tpdo_event_ind(nmt->dev, NULL, NULL);
1111 // #endif
1112 #if !LELY_NO_CO_MASTER
1113 error_init_slave:
1114  for (co_unsigned8_t id = 1; id <= CO_NUM_NODES; id++) {
1115  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
1116 
1117  can_recv_destroy(slave->recv);
1118 #if !LELY_NO_CO_NG
1119  can_timer_destroy(slave->timer);
1120 #endif
1121  }
1123 error_create_cs_timer:
1124  can_buf_fini(&nmt->buf);
1125 #endif
1127 error_create_ec_timer:
1129 error_create_recv_700:
1131 error_create_recv_000:
1132  co_nmt_srv_fini(&nmt->srv);
1134 error_write_dcf_comm:
1135 #if !LELY_NO_CO_DCF_RESTORE
1137 error_write_dcf_node:
1138 #endif
1139  set_errc(errc);
1140  return NULL;
1141 }
1142 
1143 void
1144 __co_nmt_fini(struct __co_nmt *nmt)
1145 {
1146  assert(nmt);
1147 
1148 #if !LELY_NO_CO_MASTER
1149  // Remove the download indication function for the request NMT value.
1150  co_obj_t *obj_1f82 = co_dev_find_obj(nmt->dev, 0x1f82);
1151  if (obj_1f82)
1152  co_obj_set_dn_ind(obj_1f82, NULL, NULL);
1153 #endif
1154 
1155  // Remove the download indication function for the NMT startup value.
1156  co_obj_t *obj_1f80 = co_dev_find_obj(nmt->dev, 0x1f80);
1157  if (obj_1f80)
1158  co_obj_set_dn_ind(obj_1f80, NULL, NULL);
1159 
1160 #if !LELY_NO_CO_NMT_CFG
1161  // Remove the download indication function for the configuration request
1162  // value.
1163  co_obj_t *obj_1f25 = co_dev_find_obj(nmt->dev, 0x1f25);
1164  if (obj_1f25)
1165  co_obj_set_dn_ind(obj_1f25, NULL, NULL);
1166 #endif
1167 
1168  // Remove the download indication function for the producer heartbeat
1169  // time.
1170  co_obj_t *obj_1017 = co_dev_find_obj(nmt->dev, 0x1017);
1171  if (obj_1017)
1172  co_obj_set_dn_ind(obj_1017, NULL, NULL);
1173 
1174  // Remove the download indication function for the consumer heartbeat
1175  // time.
1176  co_obj_t *obj_1016 = co_dev_find_obj(nmt->dev, 0x1016);
1177  if (obj_1016)
1178  co_obj_set_dn_ind(obj_1016, NULL, NULL);
1179 
1180 #if !LELY_NO_CO_NG
1181  // Remove the download indication function for the life time factor.
1182  co_obj_t *obj_100d = co_dev_find_obj(nmt->dev, 0x100d);
1183  if (obj_100d)
1184  co_obj_set_dn_ind(obj_100d, NULL, NULL);
1185 
1186  // Remove the download indication function for the guard time.
1187  co_obj_t *obj_100c = co_dev_find_obj(nmt->dev, 0x100c);
1188  if (obj_100c)
1189  co_obj_set_dn_ind(obj_100c, NULL, NULL);
1190 #endif
1191 
1192 #if !LELY_NO_CO_TPDO
1193  // Remove the Transmit-PDO event indication function.
1194  co_dev_set_tpdo_event_ind(nmt->dev, NULL, NULL);
1195 #endif
1196 
1197 #if !LELY_NO_CO_MASTER
1199 
1200  for (co_unsigned8_t id = 1; id <= CO_NUM_NODES; id++) {
1201  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
1202 
1203  can_recv_destroy(slave->recv);
1204 #if !LELY_NO_CO_NG
1205  can_timer_destroy(slave->timer);
1206 #endif
1207  }
1208 #endif
1209 
1210 #if !LELY_NO_CO_MASTER
1212  can_buf_fini(&nmt->buf);
1213 #endif
1214 
1216 
1218 
1221 
1223 
1224  co_nmt_srv_fini(&nmt->srv);
1225 
1227 #if !LELY_NO_CO_DCF_RESTORE
1229 #endif
1230 }
1231 
1232 co_nmt_t *
1234 {
1235  int errc = 0;
1236 
1237  co_nmt_t *nmt = __co_nmt_alloc();
1238  if (!nmt) {
1239  errc = get_errc();
1240  goto error_alloc_nmt;
1241  }
1242 
1243  if (!__co_nmt_init(nmt, net, dev)) {
1244  errc = get_errc();
1245  goto error_init_nmt;
1246  }
1247 
1248  return nmt;
1249 
1250 error_init_nmt:
1251  __co_nmt_free(nmt);
1252 error_alloc_nmt:
1253  set_errc(errc);
1254  return NULL;
1255 }
1256 
1257 void
1259 {
1260  if (nmt) {
1261  __co_nmt_fini(nmt);
1262  __co_nmt_free(nmt);
1263  }
1264 }
1265 
1266 can_net_t *
1268 {
1269  assert(nmt);
1270 
1271  return nmt->net;
1272 }
1273 
1274 co_dev_t *
1276 {
1277  assert(nmt);
1278 
1279  return nmt->dev;
1280 }
1281 
1282 void
1283 co_nmt_get_cs_ind(const co_nmt_t *nmt, co_nmt_cs_ind_t **pind, void **pdata)
1284 {
1285  assert(nmt);
1286 
1287  if (pind)
1288  *pind = nmt->cs_ind;
1289  if (pdata)
1290  *pdata = nmt->cs_data;
1291 }
1292 
1293 void
1295 {
1296  assert(nmt);
1297 
1298  nmt->cs_ind = ind;
1299  nmt->cs_data = data;
1300 }
1301 
1302 #if !LELY_NO_CO_NG
1303 
1304 #if !LELY_NO_CO_MASTER
1305 
1306 void
1307 co_nmt_get_ng_ind(const co_nmt_t *nmt, co_nmt_ng_ind_t **pind, void **pdata)
1308 {
1309  assert(nmt);
1310 
1311  if (pind)
1312  *pind = nmt->ng_ind;
1313  if (pdata)
1314  *pdata = nmt->ng_data;
1315 }
1316 
1317 void
1319 {
1320  assert(nmt);
1321 
1322  nmt->ng_ind = ind ? ind : &default_ng_ind;
1323  nmt->ng_data = ind ? data : NULL;
1324 }
1325 
1326 void
1327 co_nmt_on_ng(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason)
1328 {
1329  assert(nmt);
1330  (void)reason;
1331 
1332  if (!id || id > CO_NUM_NODES)
1333  return;
1334 
1335  if (co_nmt_is_master(nmt) && state == CO_NMT_EC_OCCURRED)
1336  co_nmt_node_err_ind(nmt, id);
1337 }
1338 
1339 #endif // !LELY_NO_CO_MASTER
1340 
1341 void
1342 co_nmt_get_lg_ind(const co_nmt_t *nmt, co_nmt_lg_ind_t **pind, void **pdata)
1343 {
1344  assert(nmt);
1345 
1346  if (pind)
1347  *pind = nmt->lg_ind;
1348  if (pdata)
1349  *pdata = nmt->lg_data;
1350 }
1351 
1352 void
1354 {
1355  assert(nmt);
1356 
1357  nmt->lg_ind = ind ? ind : &default_lg_ind;
1358  nmt->lg_data = ind ? data : NULL;
1359 }
1360 
1361 void
1363 {
1364  assert(nmt);
1365 
1366  if (state == CO_NMT_EC_OCCURRED) {
1367  co_nmt_on_err(nmt, 0x8130, 0x10, NULL);
1368  } else if (state == CO_NMT_EC_RESOLVED) {
1369  if (nmt->srv.emcy) {
1370  // Remove the EMCY message from the stack.
1371  ssize_t n = co_emcy_find(nmt->srv.emcy, 0x8130);
1372  if (n >= 0)
1373  co_emcy_remove(nmt->srv.emcy, n);
1374  }
1375  }
1376 }
1377 
1378 #endif // !LELY_NO_CO_MASTER
1379 
1380 void
1381 co_nmt_get_hb_ind(const co_nmt_t *nmt, co_nmt_hb_ind_t **pind, void **pdata)
1382 {
1383  assert(nmt);
1384 
1385  if (pind)
1386  *pind = nmt->hb_ind;
1387  if (pdata)
1388  *pdata = nmt->hb_data;
1389 }
1390 
1391 void
1393 {
1394  assert(nmt);
1395 
1396  nmt->hb_ind = ind ? ind : &default_hb_ind;
1397  nmt->hb_data = ind ? data : NULL;
1398 }
1399 
1400 void
1401 co_nmt_on_hb(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason)
1402 {
1403  assert(nmt);
1404 
1405  if (!id || id > CO_NUM_NODES)
1406  return;
1407 
1408  if (reason != CO_NMT_EC_TIMEOUT)
1409  return;
1410 
1411  if (state == CO_NMT_EC_OCCURRED) {
1412 #if !LELY_NO_CO_MASTER
1413  if (co_nmt_is_master(nmt)) {
1414  co_nmt_node_err_ind(nmt, id);
1415  return;
1416  }
1417 #endif
1418  co_nmt_on_err(nmt, 0x8130, 0x10, NULL);
1419  } else if (state == CO_NMT_EC_RESOLVED) {
1420 #if !LELY_NO_CO_MASTER
1421  if (co_nmt_is_master(nmt))
1422  return;
1423 #endif
1424  if (nmt->srv.emcy) {
1425  // Remove the EMCY message from the stack.
1426  ssize_t n = co_emcy_find(nmt->srv.emcy, 0x8130);
1427  if (n >= 0)
1428  co_emcy_remove(nmt->srv.emcy, n);
1429  }
1430  }
1431 }
1432 
1433 void
1434 co_nmt_get_st_ind(const co_nmt_t *nmt, co_nmt_st_ind_t **pind, void **pdata)
1435 {
1436  assert(nmt);
1437 
1438  if (pind)
1439  *pind = nmt->st_ind;
1440  if (pdata)
1441  *pdata = nmt->st_data;
1442 }
1443 
1444 void
1446 {
1447  assert(nmt);
1448 
1449  nmt->st_ind = ind ? ind : &default_st_ind;
1450  nmt->st_data = ind ? data : NULL;
1451 }
1452 
1453 void
1454 co_nmt_on_st(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st)
1455 {
1456  assert(nmt);
1457 
1458  if (!id || id > CO_NUM_NODES)
1459  return;
1460 
1461 #if LELY_NO_CO_NMT_BOOT
1462  (void)nmt;
1463  (void)st;
1464 #else
1465  if (co_nmt_is_master(nmt) && st == CO_NMT_ST_BOOTUP) {
1466  int errc = get_errc();
1467  co_nmt_boot_req(nmt, id, nmt->timeout);
1468  set_errc(errc);
1469  }
1470 #endif
1471 }
1472 
1473 #if !LELY_NO_CO_MASTER
1474 
1475 #if !LELY_NO_CO_LSS
1476 
1477 void
1478 co_nmt_get_lss_req(const co_nmt_t *nmt, co_nmt_lss_req_t **pind, void **pdata)
1479 {
1480  assert(nmt);
1481 
1482  if (pind)
1483  *pind = nmt->lss_req;
1484  if (pdata)
1485  *pdata = nmt->lss_data;
1486 }
1487 
1488 void
1490 {
1491  assert(nmt);
1492 
1493  nmt->lss_req = ind;
1494  nmt->lss_data = data;
1495 }
1496 
1497 #endif
1498 
1499 #if !LELY_NO_CO_NMT_BOOT
1500 
1501 void
1502 co_nmt_get_boot_ind(const co_nmt_t *nmt, co_nmt_boot_ind_t **pind, void **pdata)
1503 {
1504  assert(nmt);
1505 
1506  if (pind)
1507  *pind = nmt->boot_ind;
1508  if (pdata)
1509  *pdata = nmt->boot_data;
1510 }
1511 
1512 void
1514 {
1515  assert(nmt);
1516 
1517  nmt->boot_ind = ind;
1518  nmt->boot_data = data;
1519 }
1520 
1521 #endif // !LELY_NO_CO_NMT_BOOT
1522 
1523 #if !LELY_NO_CO_NMT_CFG
1524 
1525 void
1526 co_nmt_get_cfg_ind(const co_nmt_t *nmt, co_nmt_cfg_ind_t **pind, void **pdata)
1527 {
1528  assert(nmt);
1529 
1530  if (pind)
1531  *pind = nmt->cfg_ind;
1532  if (pdata)
1533  *pdata = nmt->cfg_data;
1534 }
1535 
1536 void
1538 {
1539  assert(nmt);
1540 
1541  nmt->cfg_ind = ind;
1542  nmt->cfg_data = data;
1543 }
1544 
1545 #endif // !LELY_NO_CO_NMT_BOOT
1546 
1547 void
1548 co_nmt_get_dn_ind(const co_nmt_t *nmt, co_nmt_sdo_ind_t **pind, void **pdata)
1549 {
1550  assert(nmt);
1551 
1552  if (pind)
1553  *pind = nmt->dn_ind;
1554  if (pdata)
1555  *pdata = nmt->dn_data;
1556 }
1557 
1558 void
1560 {
1561  assert(nmt);
1562 
1563  nmt->dn_ind = ind;
1564  nmt->dn_data = data;
1565 }
1566 
1567 void
1568 co_nmt_get_up_ind(const co_nmt_t *nmt, co_nmt_sdo_ind_t **pind, void **pdata)
1569 {
1570  assert(nmt);
1571 
1572  if (pind)
1573  *pind = nmt->up_ind;
1574  if (pdata)
1575  *pdata = nmt->up_data;
1576 }
1577 
1578 void
1580 {
1581  assert(nmt);
1582 
1583  nmt->up_ind = ind;
1584  nmt->up_data = data;
1585 }
1586 
1587 #endif // !LELY_NO_CO_MASTER
1588 
1589 void
1590 co_nmt_get_sync_ind(const co_nmt_t *nmt, co_nmt_sync_ind_t **pind, void **pdata)
1591 {
1592  assert(nmt);
1593 
1594  if (pind)
1595  *pind = nmt->sync_ind;
1596  if (pdata)
1597  *pdata = nmt->sync_data;
1598 }
1599 
1600 void
1602 {
1603  assert(nmt);
1604 
1605  nmt->sync_ind = ind;
1606  nmt->sync_data = data;
1607 }
1608 
1609 void
1610 co_nmt_on_sync(co_nmt_t *nmt, co_unsigned8_t cnt)
1611 {
1612  assert(nmt);
1613 
1614  // Handle TPDOs before RPDOs. This prevents a possible race condition if
1615  // the same object is mapped to both an RPDO and a TPDO. In accordance
1616  // with CiA 301 v4.2.0 we transmit the value from the previous
1617  // synchronous window before updating it with a received PDO.
1618 #if !LELY_NO_CO_TPDO
1619  for (co_unsigned16_t i = 0; i < nmt->srv.ntpdo; i++) {
1620  if (nmt->srv.tpdos[i])
1621  co_tpdo_sync(nmt->srv.tpdos[i], cnt);
1622  }
1623 #endif
1624 #if !LELY_NO_CO_RPDO
1625  for (co_unsigned16_t i = 0; i < nmt->srv.nrpdo; i++) {
1626  if (nmt->srv.rpdos[i])
1627  co_rpdo_sync(nmt->srv.rpdos[i], cnt);
1628  }
1629 #endif
1630 
1631  if (nmt->sync_ind)
1632  nmt->sync_ind(nmt, cnt, nmt->sync_data);
1633 }
1634 
1635 void
1636 co_nmt_on_err(co_nmt_t *nmt, co_unsigned16_t eec, co_unsigned8_t er,
1637  const co_unsigned8_t msef[5])
1638 {
1639  assert(nmt);
1640 
1641  if (eec) {
1642 #if LELY_NO_CO_EMCY
1643  (void)er;
1644  (void)msef;
1645 #else
1646  if (nmt->srv.emcy)
1647  co_emcy_push(nmt->srv.emcy, eec, er, msef);
1648 #endif
1649  // In case of a communication error (0x81xx), invoke the
1650  // behavior specified by 1029:01.
1651  if ((eec & 0xff00) == 0x8100)
1653  }
1654 }
1655 
1656 #if !LELY_NO_CO_TPDO
1657 
1658 void
1659 co_nmt_on_tpdo_event(co_nmt_t *nmt, co_unsigned16_t n)
1660 {
1661  assert(nmt);
1662  assert(nmt->srv.ntpdo <= CO_NUM_PDOS);
1663 
1664  int errsv = get_errc();
1665  if (n) {
1666  co_tpdo_t *pdo = co_nmt_get_tpdo(nmt, n);
1667  if (pdo) {
1668  if (nmt->tpdo_event_wait)
1669  nmt->tpdo_event_mask[(n - 1) / LONG_BIT] |= 1ul
1670  << ((n - 1) % LONG_BIT);
1671  else
1672  co_tpdo_event(pdo);
1673  }
1674  } else {
1675  for (n = 1; n <= nmt->srv.ntpdo; n++) {
1676  co_tpdo_t *pdo = co_nmt_get_tpdo(nmt, n);
1677  if (!pdo)
1678  continue;
1679  if (nmt->tpdo_event_wait)
1680  nmt->tpdo_event_mask[(n - 1) / LONG_BIT] |= 1ul
1681  << ((n - 1) % LONG_BIT);
1682  else
1683  co_tpdo_event(pdo);
1684  }
1685  }
1686  set_errc(errsv);
1687 }
1688 
1689 void
1691 {
1692  assert(nmt);
1693 
1694  nmt->tpdo_event_wait++;
1695 }
1696 
1697 void
1699 {
1700  assert(nmt);
1701  assert(nmt->tpdo_event_wait);
1702 
1703  if (--nmt->tpdo_event_wait)
1704  return;
1705 
1706  // Issue an indication for every postponed Transmit-PDO event.
1707  int errsv = get_errc();
1708  for (int i = 0; i < CO_NUM_PDOS / LONG_BIT; i++) {
1709  if (nmt->tpdo_event_mask[i]) {
1710  co_unsigned16_t n = i * LONG_BIT + 1;
1711  for (int j = 0; j < LONG_BIT && n <= nmt->srv.ntpdo
1712  && nmt->tpdo_event_mask[i];
1713  j++, n++) {
1714  if (!(nmt->tpdo_event_mask[i] & (1ul << j)))
1715  continue;
1716  nmt->tpdo_event_mask[i] &= ~(1ul << j);
1717  co_tpdo_t *pdo = co_nmt_get_tpdo(nmt, n);
1718  if (pdo)
1719  co_tpdo_event(pdo);
1720  }
1721  nmt->tpdo_event_mask[i] = 0;
1722  }
1723  }
1724  set_errc(errsv);
1725 }
1726 
1727 #if !LELY_NO_CO_MPDO
1728 void
1729 co_nmt_on_sam_mpdo_event(co_nmt_t *nmt, co_unsigned16_t n, co_unsigned16_t idx,
1730  co_unsigned8_t subidx)
1731 {
1732  assert(nmt);
1733 
1734  co_tpdo_t *pdo = co_nmt_get_tpdo(nmt, n);
1735  if (pdo)
1736  co_sam_mpdo_event(pdo, idx, subidx);
1737 }
1738 #endif
1739 
1740 #endif // !LELY_NO_CO_TPDO
1741 
1742 co_unsigned8_t
1744 {
1745  assert(nmt);
1746 
1747  return nmt->id;
1748 }
1749 
1750 int
1751 co_nmt_set_id(co_nmt_t *nmt, co_unsigned8_t id)
1752 {
1753  assert(nmt);
1754 
1755  if (!id || (id > CO_NUM_NODES && id != 0xff)) {
1757  return -1;
1758  }
1759 
1760  nmt->id = id;
1761 
1762  return 0;
1763 }
1764 
1765 co_unsigned8_t
1767 {
1768  assert(nmt);
1769 
1770  return nmt->st & ~CO_NMT_ST_TOGGLE;
1771 }
1772 
1773 int
1775 {
1776 #if LELY_NO_CO_MASTER
1777  (void)nmt;
1778 
1779  return 0;
1780 #else
1781  assert(nmt);
1782 
1783  return nmt->master;
1784 #endif
1785 }
1786 
1787 #if !LELY_NO_CO_MASTER
1788 
1789 int
1791 {
1792  assert(nmt);
1793 
1794  return nmt->timeout;
1795 }
1796 
1797 void
1799 {
1800  assert(nmt);
1801 
1802  nmt->timeout = timeout;
1803 }
1804 
1805 int
1806 co_nmt_cs_req(co_nmt_t *nmt, co_unsigned8_t cs, co_unsigned8_t id)
1807 {
1808  assert(nmt);
1809 
1810  if (!nmt->master) {
1812  return -1;
1813  }
1814 
1815  switch (cs) {
1816  case CO_NMT_CS_START:
1817  case CO_NMT_CS_STOP:
1818  case CO_NMT_CS_ENTER_PREOP:
1819  case CO_NMT_CS_RESET_NODE:
1820  case CO_NMT_CS_RESET_COMM: break;
1821  default: set_errnum(ERRNUM_INVAL); return -1;
1822  }
1823 
1824  if (id > CO_NUM_NODES) {
1826  return -1;
1827  }
1828 
1829  if (id == co_dev_get_id(nmt->dev))
1830  return co_nmt_cs_ind(nmt, cs);
1831 
1832  trace("NMT: sending command specifier %d to node %d", cs, id);
1833 
1834  struct can_msg msg = CAN_MSG_INIT;
1835  msg.id = CO_NMT_CS_CANID;
1836  msg.len = 2;
1837  msg.data[0] = cs;
1838  msg.data[1] = id;
1839 
1840  // Add the frame to the buffer.
1841  if (!can_buf_write(&nmt->buf, &msg, 1)) {
1842  if (!can_buf_reserve(&nmt->buf, 1))
1843  return -1;
1844  can_buf_write(&nmt->buf, &msg, 1);
1845  }
1846 
1847  // Send the frame by triggering the inhibit timer.
1848  return co_nmt_cs_timer(NULL, nmt);
1849 }
1850 
1851 #if !LELY_NO_CO_LSS
1852 int
1854 {
1855  assert(nmt);
1856 
1857  if (!nmt->master || nmt->state != co_nmt_reset_comm_state) {
1859  return -1;
1860  }
1861 
1863 
1864  return 0;
1865 }
1866 #endif
1867 
1868 #if !LELY_NO_CO_NMT_BOOT
1869 
1870 int
1871 co_nmt_boot_req(co_nmt_t *nmt, co_unsigned8_t id, int timeout)
1872 {
1873  assert(nmt);
1874 
1875  int errc = 0;
1876 
1877  if (!nmt->master) {
1878  errc = errnum2c(ERRNUM_PERM);
1879  goto error_param;
1880  }
1881 
1882  if (!id || id > CO_NUM_NODES || id == co_dev_get_id(nmt->dev)) {
1883  errc = errnum2c(ERRNUM_INVAL);
1884  goto error_param;
1885  }
1886  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
1887 
1888  if (slave->booting) {
1889  errc = errnum2c(ERRNUM_INPROGRESS);
1890  goto error_param;
1891  }
1892 
1893  trace("NMT: booting slave %d", id);
1894 
1895  // Disable the heartbeat consumer during the 'boot slave' process.
1896  co_nmt_hb_t *hb = co_nmt_hb_find(nmt, id, NULL);
1897  if (hb)
1898  co_nmt_hb_set_1016(hb, id, 0);
1899 
1900  slave->booting = 1;
1901 
1902  slave->boot = co_nmt_boot_create(nmt->net, nmt->dev, nmt);
1903  if (!slave->boot) {
1904  errc = get_errc();
1905  goto error_create_boot;
1906  }
1907 
1908  // clang-format off
1909  if (co_nmt_boot_boot_req(slave->boot, id, timeout, &co_nmt_dn_ind,
1910  &co_nmt_up_ind, nmt) == -1) {
1911  // clang-format on
1912  errc = get_errc();
1913  goto error_boot_req;
1914  }
1915 
1916  return 0;
1917 
1918 error_boot_req:
1919  co_nmt_boot_destroy(slave->boot);
1920  slave->boot = NULL;
1921 error_create_boot:
1922  slave->booting = 0;
1923 error_param:
1924  set_errc(errc);
1925  return -1;
1926 }
1927 
1928 int
1929 co_nmt_is_booting(const co_nmt_t *nmt, co_unsigned8_t id)
1930 {
1931  assert(nmt);
1932 
1933  if (!nmt->master)
1934  return 0;
1935 
1936  if (!id || id > CO_NUM_NODES || id == co_dev_get_id(nmt->dev))
1937  return 0;
1938 
1939  return !!nmt->slaves[id - 1].boot;
1940 }
1941 
1942 #endif // !LELY_NO_CO_NMT_BOOT
1943 
1944 #if !LELY_NO_CO_MASTER
1945 int
1946 co_nmt_chk_bootup(const co_nmt_t *nmt, co_unsigned8_t id)
1947 {
1948  assert(nmt);
1949 
1950  if (!nmt->master) {
1952  return -1;
1953  }
1954 
1955  if (id > CO_NUM_NODES) {
1957  return -1;
1958  }
1959 
1960  if (id == co_dev_get_id(nmt->dev)) {
1961  switch (co_nmt_get_st(nmt)) {
1962  case CO_NMT_ST_STOP:
1963  case CO_NMT_ST_START:
1964  case CO_NMT_ST_PREOP: return 1;
1965  default: return 0;
1966  }
1967  }
1968 
1969  if (id == 0)
1970  return co_nmt_chk_bootup_slaves(nmt);
1971  else
1972  return !!nmt->slaves[id - 1].bootup;
1973 }
1974 #endif
1975 
1976 #if !LELY_NO_CO_NMT_CFG
1977 
1978 int
1979 co_nmt_cfg_req(co_nmt_t *nmt, co_unsigned8_t id, int timeout,
1980  co_nmt_cfg_con_t *con, void *data)
1981 {
1982  assert(nmt);
1983 
1984  int errc = 0;
1985 
1986  if (!nmt->master) {
1987  errc = errnum2c(ERRNUM_PERM);
1988  goto error_param;
1989  }
1990 
1991  if (!id || id > CO_NUM_NODES || id == co_dev_get_id(nmt->dev)) {
1992  errc = errnum2c(ERRNUM_INVAL);
1993  goto error_param;
1994  }
1995  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
1996 
1997  if (slave->configuring) {
1998  errc = errnum2c(ERRNUM_INPROGRESS);
1999  goto error_param;
2000  }
2001 
2002  trace("NMT: starting update configuration process for node %d", id);
2003 
2004  // Disable the heartbeat consumer during a configuration request.
2005  co_nmt_hb_t *hb = co_nmt_hb_find(nmt, id, NULL);
2006  if (hb)
2007  co_nmt_hb_set_1016(hb, id, 0);
2008 
2009  slave->configuring = 1;
2010 
2011  slave->cfg = co_nmt_cfg_create(nmt->net, nmt->dev, nmt);
2012  if (!slave->cfg) {
2013  errc = get_errc();
2014  goto error_create_cfg;
2015  }
2016  slave->cfg_con = con;
2017  slave->cfg_data = data;
2018 
2019  // clang-format off
2020  if (co_nmt_cfg_cfg_req(slave->cfg, id, timeout, &co_nmt_dn_ind,
2021  &co_nmt_up_ind, nmt) == -1) {
2022  // clang-format on
2023  errc = get_errc();
2024  goto error_cfg_req;
2025  }
2026 
2027  return 0;
2028 
2029 error_cfg_req:
2030  co_nmt_cfg_destroy(slave->cfg);
2031  slave->cfg = NULL;
2032 error_create_cfg:
2033  slave->configuring = 0;
2034 error_param:
2035  set_errc(errc);
2036  return -1;
2037 }
2038 
2039 int
2040 co_nmt_cfg_res(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned32_t ac)
2041 {
2042  assert(nmt);
2043 
2044  if (!nmt->master) {
2046  return -1;
2047  }
2048 
2049  if (!id || id > CO_NUM_NODES || !nmt->slaves[id - 1].cfg) {
2051  return -1;
2052  }
2053 
2054  return co_nmt_cfg_cfg_res(nmt->slaves[id - 1].cfg, ac);
2055 }
2056 
2057 #endif // !LELY_NO_CO_NMT_CFG
2058 
2059 #if !LELY_NO_CO_NG
2060 int
2061 co_nmt_ng_req(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned16_t gt,
2062  co_unsigned8_t ltf)
2063 {
2064  assert(nmt);
2065 
2066  if (!nmt->master) {
2068  return -1;
2069  }
2070 
2071  if (!id || id > CO_NUM_NODES || id == co_dev_get_id(nmt->dev)) {
2073  return -1;
2074  }
2075  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
2076 
2077  if (!gt || !ltf) {
2078  can_timer_stop(slave->timer);
2079 
2080  slave->gt = 0;
2081  slave->ltf = 0;
2082  slave->rtr = 0;
2083  } else {
2084  slave->gt = gt;
2085  slave->ltf = ltf;
2086  slave->rtr = 0;
2087 
2088  can_timer_timeout(slave->timer, nmt->net, slave->gt);
2089  }
2090 
2091  return 0;
2092 }
2093 #endif // !LELY_NO_CO_NG
2094 
2095 #endif // !LELY_NO_CO_MASTER
2096 
2097 int
2098 co_nmt_cs_ind(co_nmt_t *nmt, co_unsigned8_t cs)
2099 {
2100  assert(nmt);
2101 
2102  switch (cs) {
2103  case CO_NMT_CS_START:
2104  case CO_NMT_CS_STOP:
2105  case CO_NMT_CS_ENTER_PREOP:
2106  case CO_NMT_CS_RESET_NODE:
2107  case CO_NMT_CS_RESET_COMM:
2108  trace("NMT: received command specifier %d", cs);
2109  co_nmt_emit_cs(nmt, cs);
2110  return 0;
2111  default: set_errnum(ERRNUM_INVAL); return -1;
2112  }
2113 }
2114 
2115 void
2117 {
2118  assert(nmt);
2119 
2120  diag(DIAG_INFO, 0, "NMT: communication error indicated");
2121  switch (co_dev_get_val_u8(nmt->dev, 0x1029, 0x01)) {
2122  case 0:
2125  break;
2126  case 2: co_nmt_cs_ind(nmt, CO_NMT_CS_STOP); break;
2127  }
2128 }
2129 
2130 #if !LELY_NO_CO_MASTER
2131 int
2132 co_nmt_node_err_ind(co_nmt_t *nmt, co_unsigned8_t id)
2133 {
2134  assert(nmt);
2135 
2136  if (!nmt->master) {
2138  return -1;
2139  }
2140 
2141  if (!id || id > CO_NUM_NODES) {
2143  return -1;
2144  }
2145 
2146  co_unsigned32_t assignment = co_dev_get_val_u32(nmt->dev, 0x1f81, id);
2147  // Ignore the error event if the slave is no longer in the network list.
2148  if (!(assignment & 0x01))
2149  return 0;
2150  int mandatory = !!(assignment & 0x08);
2151 
2152  diag(DIAG_INFO, 0, "NMT: error indicated for %s slave %d",
2153  mandatory ? "mandatory" : "optional", id);
2154 
2155  if (mandatory && (nmt->startup & 0x40)) {
2156  // If the slave is mandatory and bit 6 of the NMT startup value
2157  // is set, stop all nodes, including the master.
2159  return co_nmt_cs_ind(nmt, CO_NMT_CS_STOP);
2160  } else if (mandatory && (nmt->startup & 0x10)) {
2161  // If the slave is mandatory and bit 4 of the NMT startup value
2162  // is set, reset all nodes, including the master.
2165  } else {
2166  // If the slave is not mandatory, or bits 4 and 6 of the NMT
2167  // startup value are zero, reset the node individually.
2169  return 0;
2170  }
2171 }
2172 #endif
2173 
2174 co_rpdo_t *
2175 co_nmt_get_rpdo(const co_nmt_t *nmt, co_unsigned16_t n)
2176 {
2177 #if LELY_NO_CO_RPDO
2178  (void)nmt;
2179  (void)n;
2180 
2181  return NULL;
2182 #else
2183  assert(nmt);
2184 
2185  if (!n || n > nmt->srv.nrpdo)
2186  return NULL;
2187 
2188  return nmt->srv.rpdos[n - 1];
2189 #endif
2190 }
2191 
2192 co_tpdo_t *
2193 co_nmt_get_tpdo(const co_nmt_t *nmt, co_unsigned16_t n)
2194 {
2195 #if LELY_NO_CO_TPDO
2196  (void)nmt;
2197  (void)n;
2198 
2199  return NULL;
2200 #else
2201  assert(nmt);
2202 
2203  if (!n || n > nmt->srv.ntpdo)
2204  return NULL;
2205 
2206  return nmt->srv.tpdos[n - 1];
2207 #endif
2208 }
2209 
2210 co_ssdo_t *
2211 co_nmt_get_ssdo(const co_nmt_t *nmt, co_unsigned8_t n)
2212 {
2213  assert(nmt);
2214 
2215  if (!n || n > nmt->srv.nssdo)
2216  return NULL;
2217 
2218  return nmt->srv.ssdos[n - 1];
2219 }
2220 
2221 co_csdo_t *
2222 co_nmt_get_csdo(const co_nmt_t *nmt, co_unsigned8_t n)
2223 {
2224 #if LELY_NO_CO_CSDO
2225  (void)nmt;
2226  (void)n;
2227 
2228  return NULL;
2229 #else
2230  assert(nmt);
2231 
2232  if (!n || n > nmt->srv.ncsdo)
2233  return NULL;
2234 
2235  return nmt->srv.csdos[n - 1];
2236 #endif
2237 }
2238 
2239 co_sync_t *
2241 {
2242 #if LELY_NO_CO_SYNC
2243  (void)nmt;
2244 
2245  return NULL;
2246 #else
2247  assert(nmt);
2248 
2249  return nmt->srv.sync;
2250 #endif
2251 }
2252 
2253 co_time_t *
2255 {
2256 #if LELY_NO_CO_TIME
2257  (void)nmt;
2258 
2259  return NULL;
2260 #else
2261  assert(nmt);
2262 
2263  return nmt->srv.time;
2264 #endif
2265 }
2266 
2267 co_emcy_t *
2269 {
2270 #if LELY_NO_CO_EMCY
2271  (void)nmt;
2272 
2273  return NULL;
2274 #else
2275  assert(nmt);
2276 
2277  return nmt->srv.emcy;
2278 #endif
2279 }
2280 
2281 co_lss_t *
2283 {
2284 #if LELY_NO_CO_LSS
2285  (void)nmt;
2286 
2287  return NULL;
2288 #else
2289  assert(nmt);
2290 
2291  return nmt->srv.lss;
2292 #endif
2293 }
2294 
2295 #if !LELY_NO_CO_NMT_BOOT
2296 void
2297 co_nmt_boot_con(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es)
2298 {
2299  assert(nmt);
2300  assert(nmt->master);
2301  assert(id && id <= CO_NUM_NODES);
2302 
2303  // Update the NMT slave state, including the assignment, in case it
2304  // changed during the 'boot slave' procedure.
2305  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
2306  slave->assignment = co_dev_get_val_u32(nmt->dev, 0x1f81, id);
2307  slave->est = st & ~CO_NMT_ST_TOGGLE;
2308  // If we did not (yet) receive a state but the error control service was
2309  // successfully started, assume the node is pre-operational.
2310  if (!slave->est && (!es || es == 'L'))
2311  slave->est = CO_NMT_ST_PREOP;
2312  slave->rst = st;
2313  slave->es = es;
2314  slave->booting = 0;
2315  slave->booted = 1;
2316  co_nmt_boot_destroy(slave->boot);
2317  slave->boot = NULL;
2318 
2319  // Re-enable the heartbeat consumer for the node, if necessary.
2320  co_unsigned16_t ms = 0;
2321  co_nmt_hb_t *hb = co_nmt_hb_find(nmt, id, &ms);
2322  if (hb)
2323  co_nmt_hb_set_1016(hb, id, ms);
2324 
2325  // Update object 1F82 (Request NMT) with the NMT state.
2326  co_sub_t *sub = co_dev_find_sub(nmt->dev, 0x1f82, id);
2327  if (sub)
2328  co_sub_set_val_u8(sub, st & ~CO_NMT_ST_TOGGLE);
2329 
2330  // If the slave booted successfully and can be started by the NMT
2331  // service, and if the master is allowed to start the nodes (bit 3 of
2332  // the NMT startup value) and has to start the slaves individually (bit
2333  // 1) or is in the operational state, send the NMT 'start' command to
2334  // the slave.
2335  // clang-format off
2336  if (!es && (slave->assignment & 0x05) == 0x05 && !(nmt->startup & 0x08)
2337  && (!(nmt->startup & 0x02)
2339  // clang-format on
2341 
2342  // If the error control service was successfully started, resume
2343  // heartbeat consumption or node guarding.
2344  if (!es || es == 'L') {
2345  if (hb) {
2346  co_nmt_hb_set_st(hb, st);
2347 #if !LELY_NO_CO_NG
2348  // Disable node guarding.
2349  slave->assignment &= 0xff;
2350  } else {
2351  // Enable node guarding if the guard time and lifetime
2352  // factor are non-zero.
2353  co_unsigned16_t gt = (slave->assignment >> 16) & 0xffff;
2354  co_unsigned8_t ltf = (slave->assignment >> 8) & 0xff;
2355  if (co_nmt_ng_req(nmt, id, gt, ltf) == -1)
2357  "unable to guard node %02X",
2358  id);
2359 #endif
2360  }
2361  }
2362 
2363  trace("NMT: slave %d finished booting with error status %c", id,
2364  es ? es : '0');
2365  if (nmt->boot_ind)
2366  nmt->boot_ind(nmt, id, st, es, nmt->boot_data);
2367 
2368  co_nmt_emit_boot(nmt, id, st, es);
2369 }
2370 #endif // !LELY_NO_CO_NMT_BOOT
2371 
2372 #if !LELY_NO_CO_NMT_CFG
2373 
2374 void
2375 co_nmt_cfg_ind(co_nmt_t *nmt, co_unsigned8_t id, co_csdo_t *sdo)
2376 {
2377  assert(nmt);
2378  assert(nmt->master);
2379  assert(id && id <= CO_NUM_NODES);
2380 
2381  if (nmt->cfg_ind) {
2382  nmt->cfg_ind(nmt, id, sdo, nmt->cfg_data);
2383  } else {
2384  co_nmt_cfg_res(nmt, id, 0);
2385  }
2386 }
2387 
2388 void
2389 co_nmt_cfg_con(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned32_t ac)
2390 {
2391  assert(nmt);
2392  assert(nmt->master);
2393  assert(id && id <= CO_NUM_NODES);
2394 
2395  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
2396  slave->configuring = 0;
2397  co_nmt_cfg_destroy(slave->cfg);
2398  slave->cfg = NULL;
2399 
2400 #if !LELY_NO_CO_NMT_BOOT
2401  // Re-enable the heartbeat consumer for the node, if necessary.
2402  if (!slave->booting) {
2403 #endif
2404  co_unsigned16_t ms = 0;
2405  co_nmt_hb_t *hb = co_nmt_hb_find(nmt, id, &ms);
2406  if (hb)
2407  co_nmt_hb_set_1016(hb, id, ms);
2408 #if !LELY_NO_CO_NMT_BOOT
2409  }
2410 #endif
2411 
2412  trace("NMT: update configuration process completed for slave %d", id);
2413  if (slave->cfg_con)
2414  slave->cfg_con(nmt, id, ac, slave->cfg_data);
2415 }
2416 
2417 #endif // !LELY_NO_CO_NMT_BOOT
2418 
2419 void
2420 co_nmt_hb_ind(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason,
2421  co_unsigned8_t st)
2422 {
2423  assert(nmt);
2424  assert(nmt->hb_ind);
2425 
2426  if (!id || id > CO_NUM_NODES)
2427  return;
2428 
2429  nmt->hb_ind(nmt, id, state, reason, nmt->hb_data);
2430 
2431  if (reason == CO_NMT_EC_STATE)
2432  co_nmt_st_ind(nmt, id, st);
2433 }
2434 
2435 #if !LELY_NO_CO_NG
2436 
2437 static co_unsigned32_t
2438 co_100c_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
2439 {
2440  assert(sub);
2441  assert(co_obj_get_idx(co_sub_get_obj(sub)) == 0x100c);
2442  assert(req);
2443  co_nmt_t *nmt = data;
2444  assert(nmt);
2445 
2446  co_unsigned16_t type = co_sub_get_type(sub);
2447  assert(!co_type_is_array(type));
2448 
2449  union co_val val;
2450  co_unsigned32_t ac = 0;
2451  if (co_sdo_req_dn_val(req, type, &val, &ac) == -1)
2452  return ac;
2453 
2454  if (co_sub_get_subidx(sub))
2455  return CO_SDO_AC_NO_SUB;
2456 
2457  assert(type == CO_DEFTYPE_UNSIGNED16);
2458  co_unsigned16_t gt = val.u16;
2459  co_unsigned16_t gt_old = co_sub_get_val_u16(sub);
2460  if (gt == gt_old)
2461  return 0;
2462 
2463  nmt->gt = gt;
2464 
2465  co_sub_dn(sub, &val);
2466 
2467  co_nmt_ec_update(nmt);
2468  return 0;
2469 }
2470 
2471 static co_unsigned32_t
2472 co_100d_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
2473 {
2474  assert(sub);
2475  assert(co_obj_get_idx(co_sub_get_obj(sub)) == 0x100d);
2476  assert(req);
2477  co_nmt_t *nmt = data;
2478  assert(nmt);
2479 
2480  co_unsigned16_t type = co_sub_get_type(sub);
2481  assert(!co_type_is_array(type));
2482 
2483  union co_val val;
2484  co_unsigned32_t ac = 0;
2485  if (co_sdo_req_dn_val(req, type, &val, &ac) == -1)
2486  return ac;
2487 
2488  if (co_sub_get_subidx(sub))
2489  return CO_SDO_AC_NO_SUB;
2490 
2491  assert(type == CO_DEFTYPE_UNSIGNED8);
2492  co_unsigned8_t ltf = val.u8;
2493  co_unsigned8_t ltf_old = co_sub_get_val_u8(sub);
2494  if (ltf == ltf_old)
2495  return 0;
2496 
2497  nmt->ltf = ltf;
2498 
2499  co_sub_dn(sub, &val);
2500 
2501  co_nmt_ec_update(nmt);
2502  return 0;
2503 }
2504 
2505 #endif // !LELY_NO_CO_NG
2506 
2507 static co_unsigned32_t
2508 co_1016_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
2509 {
2510  assert(sub);
2511  assert(co_obj_get_idx(co_sub_get_obj(sub)) == 0x1016);
2512  assert(req);
2513  co_nmt_t *nmt = data;
2514  assert(nmt);
2515 
2516  co_unsigned16_t type = co_sub_get_type(sub);
2517  assert(!co_type_is_array(type));
2518 
2519  union co_val val;
2520  co_unsigned32_t ac = 0;
2521  if (co_sdo_req_dn_val(req, type, &val, &ac) == -1)
2522  return ac;
2523 
2524  co_unsigned8_t subidx = co_sub_get_subidx(sub);
2525  if (!subidx)
2526  return CO_SDO_AC_NO_WRITE;
2527  if (subidx > nmt->nhb)
2528  return CO_SDO_AC_NO_SUB;
2529 
2530  assert(type == CO_DEFTYPE_UNSIGNED32);
2531  if (val.u32 == co_sub_get_val_u32(sub))
2532  return 0;
2533 
2534  co_unsigned8_t id = (val.u32 >> 16) & 0xff;
2535  co_unsigned16_t ms = val.u32 & 0xffff;
2536 
2537  // If the heartbeat consumer is active (valid node-ID and non-zero
2538  // heartbeat time), check the other entries for duplicate node-IDs.
2539  co_obj_t *obj_1016 = co_dev_find_obj(nmt->dev, 0x1016);
2540  if (id && id <= CO_NUM_NODES && ms) {
2541  for (co_unsigned8_t i = 1; i <= CO_NUM_NODES; i++) {
2542  // Skip the current entry.
2543  if (i == subidx)
2544  continue;
2545  co_unsigned32_t val_i = co_obj_get_val_u32(obj_1016, i);
2546  co_unsigned8_t id_i = (val_i >> 16) & 0xff;
2547  co_unsigned16_t ms_i = val_i & 0xffff;
2548  // It's not allowed to have two active heartbeat
2549  // consumers with the same node-ID.
2550  if (id_i == id && ms_i)
2551  return CO_SDO_AC_PARAM;
2552  }
2553  // Disable heartbeat consumption for booting slaves or slaves
2554  // that are being configured.
2555 #if !LELY_NO_CO_NMT_BOOT
2556  if (nmt->slaves[id - 1].boot)
2557  ms = 0;
2558 #endif
2559 #if !LELY_NO_CO_NMT_CFG
2560  if (nmt->slaves[id - 1].cfg)
2561  ms = 0;
2562 #endif
2563  }
2564 
2565  co_sub_dn(sub, &val);
2566 
2567  co_nmt_hb_set_1016(nmt->hbs[subidx - 1], id, ms);
2568  return 0;
2569 }
2570 
2571 static co_unsigned32_t
2572 co_1017_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
2573 {
2574  assert(sub);
2575  assert(co_obj_get_idx(co_sub_get_obj(sub)) == 0x1017);
2576  assert(req);
2577  co_nmt_t *nmt = data;
2578  assert(nmt);
2579 
2580  co_unsigned16_t type = co_sub_get_type(sub);
2581  assert(!co_type_is_array(type));
2582 
2583  union co_val val;
2584  co_unsigned32_t ac = 0;
2585  if (co_sdo_req_dn_val(req, type, &val, &ac) == -1)
2586  return ac;
2587 
2588  if (co_sub_get_subidx(sub))
2589  return CO_SDO_AC_NO_SUB;
2590 
2591  assert(type == CO_DEFTYPE_UNSIGNED16);
2592  co_unsigned16_t ms = val.u16;
2593  co_unsigned16_t ms_old = co_sub_get_val_u16(sub);
2594  if (ms == ms_old)
2595  return 0;
2596 
2597  nmt->ms = ms;
2598 
2599  co_sub_dn(sub, &val);
2600 
2601  co_nmt_ec_update(nmt);
2602  return 0;
2603 }
2604 
2605 #if !LELY_NO_CO_NMT_CFG
2606 static co_unsigned32_t
2607 co_1f25_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
2608 {
2609  assert(sub);
2610  assert(co_obj_get_idx(co_sub_get_obj(sub)) == 0x1f25);
2611  assert(req);
2612  co_nmt_t *nmt = data;
2613  assert(nmt);
2614 
2615  co_unsigned16_t type = co_sub_get_type(sub);
2616  assert(!co_type_is_array(type));
2617 
2618  union co_val val;
2619  co_unsigned32_t ac = 0;
2620  if (co_sdo_req_dn_val(req, type, &val, &ac) == -1)
2621  return ac;
2622 
2623  co_unsigned8_t subidx = co_sub_get_subidx(sub);
2624  if (!subidx)
2625  return CO_SDO_AC_NO_WRITE;
2626 
2627  // Sub-index 80 indicates all nodes.
2628  co_unsigned8_t id = subidx == 0x80 ? 0 : subidx;
2629  // Abort with an error if the node-ID is unknown.
2630  if (id > CO_NUM_NODES
2631  // cppcheck-suppress knownConditionTrueFalse
2632  || (id && !(nmt->slaves[id - 1].assignment & 0x01)))
2633  return CO_SDO_AC_PARAM_VAL;
2634 
2635  // Check if the value 'conf' was downloaded.
2636  if (!nmt->master || val.u32 != UINT32_C(0x666e6f63))
2637  return CO_SDO_AC_DATA_CTL;
2638 
2639  // cppcheck-suppress knownConditionTrueFalse
2640  if (id) {
2641  // Check if the entry for this node is present in object 1F20
2642  // (Store DCF) or 1F22 (Concise DCF).
2643 #if LELY_NO_CO_DCF
2644  if (!co_dev_get_val(nmt->dev, 0x1f22, id))
2645 #else
2646  if (!co_dev_get_val(nmt->dev, 0x1f20, id)
2647  && !co_dev_get_val(nmt->dev, 0x1f22, id))
2648 #endif
2649  return CO_SDO_AC_NO_DATA;
2650  // Abort if the slave is already being configured.
2651  if (nmt->slaves[id - 1].cfg)
2652  return CO_SDO_AC_DATA_DEV;
2653  co_nmt_cfg_req(nmt, id, nmt->timeout, NULL, NULL);
2654  } else {
2655  // Check if object 1F20 (Store DCF) or 1F22 (Concise DCF)
2656  // exists.
2657 #if LELY_NO_CO_DCF
2658  if (!co_dev_find_obj(nmt->dev, 0x1f22))
2659 #else
2660  if (!co_dev_find_obj(nmt->dev, 0x1f20)
2661  && !co_dev_find_obj(nmt->dev, 0x1f22))
2662 #endif
2663  return CO_SDO_AC_NO_DATA;
2664  for (id = 1; id <= CO_NUM_NODES; id++) {
2665  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
2666  // Skip slaves that are not in the network list or are
2667  // already being configured.
2668  if (!(slave->assignment & 0x01) || slave->configuring)
2669  continue;
2670  co_nmt_cfg_req(nmt, id, nmt->timeout, NULL, NULL);
2671  }
2672  }
2673 
2674  return 0;
2675 }
2676 #endif // !LELY_NO_CO_NMT_CFG
2677 
2678 static co_unsigned32_t
2679 co_1f80_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
2680 {
2681  assert(sub);
2682  assert(co_obj_get_idx(co_sub_get_obj(sub)) == 0x1f80);
2683  assert(req);
2684  (void)data;
2685 
2686  co_unsigned16_t type = co_sub_get_type(sub);
2687  assert(!co_type_is_array(type));
2688 
2689  union co_val val;
2690  co_unsigned32_t ac = 0;
2691  if (co_sdo_req_dn_val(req, type, &val, &ac) == -1)
2692  return ac;
2693 
2694  if (co_sub_get_subidx(sub))
2695  return CO_SDO_AC_NO_SUB;
2696 
2697  assert(type == CO_DEFTYPE_UNSIGNED32);
2698  co_unsigned32_t startup = val.u32;
2699  co_unsigned32_t startup_old = co_sub_get_val_u32(sub);
2700  if (startup == startup_old)
2701  return 0;
2702 
2703  // Only bits 0..4 and 6 are supported.
2704  if ((startup ^ startup_old) != 0x5f)
2705  return CO_SDO_AC_PARAM_VAL;
2706 
2707  co_sub_dn(sub, &val);
2708 
2709  return 0;
2710 }
2711 
2712 #if !LELY_NO_CO_MASTER
2713 static co_unsigned32_t
2714 co_1f82_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
2715 {
2716  assert(sub);
2717  assert(co_obj_get_idx(co_sub_get_obj(sub)) == 0x1f82);
2718  assert(req);
2719  co_nmt_t *nmt = data;
2720  assert(nmt);
2721 
2722  co_unsigned16_t type = co_sub_get_type(sub);
2723  assert(!co_type_is_array(type));
2724 
2725  union co_val val;
2726  co_unsigned32_t ac = 0;
2727  if (co_sdo_req_dn_val(req, type, &val, &ac) == -1)
2728  return ac;
2729 
2730  co_unsigned8_t subidx = co_sub_get_subidx(sub);
2731  if (!subidx)
2732  return CO_SDO_AC_NO_WRITE;
2733 
2734  // Sub-index 80 indicates all nodes.
2735  co_unsigned8_t id = subidx == 0x80 ? 0 : subidx;
2736  // Abort with an error if the node-ID is unknown.
2737  if (id > CO_NUM_NODES
2738  // cppcheck-suppress knownConditionTrueFalse
2739  || (id && !(nmt->slaves[id - 1].assignment & 0x01)))
2740  return CO_SDO_AC_PARAM_VAL;
2741 
2742  if (!nmt->master)
2743  return CO_SDO_AC_DATA_CTL;
2744 
2745  assert(type == CO_DEFTYPE_UNSIGNED8);
2746  switch (val.u8) {
2747  case CO_NMT_ST_STOP: co_nmt_cs_req(nmt, CO_NMT_CS_STOP, id); break;
2748  case CO_NMT_ST_START: co_nmt_cs_req(nmt, CO_NMT_CS_START, id); break;
2749  case CO_NMT_ST_RESET_NODE:
2751  break;
2752  case CO_NMT_ST_RESET_COMM:
2754  break;
2755  case CO_NMT_ST_PREOP:
2757  break;
2758  default: ac = CO_SDO_AC_PARAM_VAL; break;
2759  }
2760 
2761  return 0;
2762 }
2763 #endif // !LELY_NO_CO_MASTER
2764 
2765 static int
2766 co_nmt_recv_000(const struct can_msg *msg, void *data)
2767 {
2768  assert(msg);
2769  co_nmt_t *nmt = data;
2770  assert(nmt);
2771 
2772 #if !LELY_NO_CO_MASTER
2773  // Ignore NMT commands if we're the master.
2774  if (nmt->master)
2775  return 0;
2776 #endif
2777 
2778  if (msg->len < 2)
2779  return 0;
2780  co_unsigned8_t cs = msg->data[0];
2781  co_unsigned8_t id = msg->data[1];
2782 
2783  // Ignore NMT commands to other nodes.
2784  if (!id || id == co_dev_get_id(nmt->dev))
2785  co_nmt_emit_cs(nmt, cs);
2786 
2787  return 0;
2788 }
2789 
2790 static int
2791 co_nmt_recv_700(const struct can_msg *msg, void *data)
2792 {
2793  assert(msg);
2794  assert(msg->id > 0x700 && msg->id <= 0x77f);
2795 #if LELY_NO_CO_MASTER && LELY_NO_CO_NG
2796  (void)data;
2797 #else
2798  co_nmt_t *nmt = data;
2799  assert(nmt);
2800 #endif
2801 
2802  if (msg->flags & CAN_FLAG_RTR) {
2803 #if !LELY_NO_CO_NG
2804  assert(nmt->gt && nmt->ltf);
2805  assert(nmt->lg_ind);
2806 
2807  // Respond with the state and flip the toggle bit.
2808  co_nmt_ec_send_res(nmt, nmt->st);
2809  nmt->st ^= CO_NMT_ST_TOGGLE;
2810 
2811  // Reset the life guarding timer.
2812  can_timer_timeout(nmt->ec_timer, nmt->net, nmt->gt * nmt->ltf);
2813 
2814  if (nmt->lg_state == CO_NMT_EC_OCCURRED) {
2815  diag(DIAG_INFO, 0, "NMT: life guarding event resolved");
2816  // Notify the user of the resolution of a life guarding
2817  // error.
2819  nmt->lg_ind(nmt, nmt->lg_state, nmt->lg_data);
2820  }
2821 #endif
2822 #if !LELY_NO_CO_MASTER
2823  } else {
2824  assert(nmt->master);
2825 #if !LELY_NO_CO_NG
2826  assert(nmt->ng_ind);
2827 #endif
2828 
2829  co_unsigned8_t id = (msg->id - 0x700) & 0x7f;
2830  if (!id)
2831  return 0;
2832  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
2833 
2834  if (msg->len < 1)
2835  return 0;
2836  co_unsigned8_t st = msg->data[0];
2837 
2838  if (st == CO_NMT_ST_BOOTUP) {
2839  // The expected state after a boot-up event is
2840  // pre-operational.
2841  slave->est = CO_NMT_ST_PREOP;
2842  // Record the reception of the boot-up message.
2843  slave->bootup = 1;
2844 
2845  // Inform the application of the boot-up event.
2846  co_nmt_st_ind(nmt, id, st);
2847  return 0;
2848  }
2849 
2850  // Ignore messages from booting slaves or slaves that are being
2851  // configured.
2852 #if !LELY_NO_CO_NMT_BOOT
2853  if (slave->booting)
2854  return 0;
2855 #endif
2856 #if !LELY_NO_CO_NMT_CFG
2857  if (slave->configuring)
2858  return 0;
2859 #endif
2860 
2861 #if !LELY_NO_CO_NG
2862  // Ignore messages if node guarding is disabled.
2863  if (!slave->gt || !slave->ltf)
2864  return 0;
2865 
2866  // Check the toggle bit and ignore the message if it does not
2867  // match.
2868  if (!((st ^ slave->rst) & CO_NMT_ST_TOGGLE))
2869  return 0;
2870  slave->rst ^= CO_NMT_ST_TOGGLE;
2871 
2872  // Notify the application of the resolution of a node guarding
2873  // timeout.
2874  if (slave->rtr >= slave->ltf) {
2875  diag(DIAG_INFO, 0,
2876  "NMT: node guarding time out resolved for node %d",
2877  id);
2880  }
2881  slave->rtr = 0;
2882 
2883  // Notify the application of the occurrence or resolution of an
2884  // unexpected state change.
2885  if (slave->est != (st & ~CO_NMT_ST_TOGGLE)
2886  && slave->ng_state == CO_NMT_EC_RESOLVED) {
2887  diag(DIAG_INFO, 0,
2888  "NMT: node guarding state change occurred for node %d",
2889  id);
2890  slave->ng_state = CO_NMT_EC_OCCURRED;
2891  nmt->ng_ind(nmt, id, slave->ng_state, CO_NMT_EC_STATE,
2892  nmt->ng_data);
2893  } else if (slave->est == (st & ~CO_NMT_ST_TOGGLE)
2894  && slave->ng_state == CO_NMT_EC_OCCURRED) {
2895  diag(DIAG_INFO, 0,
2896  "NMT: node guarding state change resolved for node %d",
2897  id);
2898  slave->ng_state = CO_NMT_EC_RESOLVED;
2899  nmt->ng_ind(nmt, id, slave->ng_state, CO_NMT_EC_STATE,
2900  nmt->ng_data);
2901  }
2902 
2903  // Notify the application of the occurrence of a state change.
2904  if (st != slave->rst)
2905  co_nmt_st_ind(nmt, id, st);
2906 #endif // !LELY_NO_CO_NG
2907 #endif // !LELY_NO_CO_MASTER
2908  }
2909 
2910  return 0;
2911 }
2912 
2913 #if !LELY_NO_CO_MASTER && !LELY_NO_CO_NG
2914 static int
2915 co_nmt_ng_timer(const struct timespec *tp, void *data)
2916 {
2917  (void)tp;
2918  struct co_nmt_slave *slave = data;
2919  assert(slave);
2920  assert(slave->gt && slave->ltf);
2921  co_nmt_t *nmt = slave->nmt;
2922  assert(nmt);
2923  assert(nmt->master);
2924  assert(nmt->ng_ind);
2925  co_unsigned8_t id = slave - nmt->slaves + 1;
2926  assert(id && id <= CO_NUM_NODES);
2927 
2928  // Reset the timer for the next RTR.
2929  can_timer_timeout(slave->timer, nmt->net, slave->gt);
2930 
2931 #if !LELY_NO_CO_NMT_BOOT
2932  // Do not send node guarding RTRs to slaves that have not finished
2933  // booting.
2934  if (!slave->booted)
2935  return 0;
2936 #endif
2937 
2938  // Notify the application once of the occurrence of a node guarding
2939  // timeout.
2940  if (slave->rtr <= slave->ltf && ++slave->rtr == slave->ltf) {
2941  diag(DIAG_INFO, 0,
2942  "NMT: node guarding time out occurred for node %d",
2943  id);
2945  nmt->ng_data);
2946  return 0;
2947  }
2948 
2949  struct can_msg msg = CAN_MSG_INIT;
2950  msg.id = CO_NMT_EC_CANID(id);
2951  msg.flags |= CAN_FLAG_RTR;
2952 
2953  return can_net_send(nmt->net, &msg);
2954 }
2955 #endif // !LELY_NO_CO_MASTER && !LELY_NO_CO_NG
2956 
2957 static int
2958 co_nmt_ec_timer(const struct timespec *tp, void *data)
2959 {
2960  (void)tp;
2961  co_nmt_t *nmt = data;
2962  assert(nmt);
2963 
2964  if (nmt->ms) {
2965  // Send the state of the NMT service (excluding the toggle bit).
2966  co_nmt_ec_send_res(nmt, nmt->st & ~CO_NMT_ST_TOGGLE);
2967 #if !LELY_NO_CO_NG
2968  } else if (nmt->gt && nmt->ltf) {
2969  assert(nmt->lg_ind);
2970  // Notify the user of the occurrence of a life guarding error.
2971  diag(DIAG_INFO, 0, "NMT: life guarding event occurred");
2973  nmt->lg_ind(nmt, nmt->lg_state, nmt->lg_data);
2974 #endif
2975  }
2976 
2977  return 0;
2978 }
2979 
2980 #if !LELY_NO_CO_MASTER
2981 static int
2982 co_nmt_cs_timer(const struct timespec *tp, void *data)
2983 {
2984  (void)tp;
2985  co_nmt_t *nmt = data;
2986  assert(nmt);
2987  assert(nmt->master);
2988 
2989  co_unsigned16_t inhibit = co_dev_get_val_u16(nmt->dev, 0x102a, 0x00);
2990 
2991  can_timer_stop(nmt->cs_timer);
2992 
2993  struct timespec now = { 0, 0 };
2994  can_net_get_time(nmt->net, &now);
2995 
2996  struct can_msg msg;
2997  while (can_buf_peek(&nmt->buf, &msg, 1)) {
2998  assert(msg.id == CO_NMT_CS_CANID);
2999  assert(msg.len == 2);
3000  // Wait until the inhibit time has elapsed.
3001  if (inhibit && timespec_cmp(&now, &nmt->inhibit) < 0) {
3002  can_timer_start(nmt->cs_timer, nmt->net, &nmt->inhibit,
3003  NULL);
3004  return 0;
3005  }
3006  // Try to send the frame.
3007  if (can_net_send(nmt->net, &msg) == -1)
3008  return -1;
3009  can_buf_read(&nmt->buf, NULL, 1);
3010  // Update the expected state of the node(s).
3011  co_unsigned8_t st = 0;
3012  switch (msg.data[0]) {
3013  case CO_NMT_CS_START: st = CO_NMT_ST_START; break;
3014  case CO_NMT_CS_STOP: st = CO_NMT_ST_STOP; break;
3015  case CO_NMT_CS_ENTER_PREOP: st = CO_NMT_ST_PREOP; break;
3016  }
3017  co_unsigned8_t id = msg.data[1];
3018  assert(id <= CO_NUM_NODES);
3019  if (id) {
3020  if (nmt->slaves[id - 1].est)
3021  nmt->slaves[id - 1].est = st;
3022  } else {
3023  for (id = 1; id <= CO_NUM_NODES; id++) {
3024  if (nmt->slaves[id - 1].est)
3025  nmt->slaves[id - 1].est = st;
3026  }
3027  }
3028  // Update the inhibit time.
3029  can_net_get_time(nmt->net, &now);
3030  nmt->inhibit = now;
3031  timespec_add_usec(&nmt->inhibit, inhibit * 100);
3032  }
3033 
3034  return 0;
3035 }
3036 #endif // !LELY_NO_CO_MASTER
3037 
3038 static void
3039 co_nmt_st_ind(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st)
3040 {
3041  assert(nmt);
3042  assert(nmt->st_ind);
3043 
3044  if (!id || id > CO_NUM_NODES)
3045  return;
3046 
3047 #if !LELY_NO_CO_MASTER
3048  if (nmt->master) {
3049  nmt->slaves[id - 1].rst = st;
3050 
3051  // Update object 1F82 (Request NMT) with the NMT state.
3052  co_sub_t *sub = co_dev_find_sub(nmt->dev, 0x1f82, id);
3053  if (sub)
3054  co_sub_set_val_u8(sub, st & ~CO_NMT_ST_TOGGLE);
3055  }
3056 #endif
3057 
3058  nmt->st_ind(nmt, id, st & ~CO_NMT_ST_TOGGLE, nmt->st_data);
3059 }
3060 
3061 #if !LELY_NO_CO_NG
3062 
3063 #if !LELY_NO_CO_MASTER
3064 static void
3065 default_ng_ind(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason,
3066  void *data)
3067 {
3068  (void)data;
3069 
3070  co_nmt_on_ng(nmt, id, state, reason);
3071 }
3072 #endif
3073 
3074 static void
3075 default_lg_ind(co_nmt_t *nmt, int state, void *data)
3076 {
3077  (void)data;
3078 
3079  co_nmt_on_lg(nmt, state);
3080 }
3081 
3082 #endif // !LELY_NO_CO_NG
3083 
3084 static void
3085 default_hb_ind(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason,
3086  void *data)
3087 {
3088  (void)data;
3089 
3090  co_nmt_on_hb(nmt, id, state, reason);
3091 }
3092 
3093 static void
3094 default_st_ind(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, void *data)
3095 {
3096  (void)data;
3097 
3098  co_nmt_on_st(nmt, id, st);
3099 }
3100 
3101 #if !LELY_NO_CO_NMT_BOOT || !LELY_NO_CO_NMT_CFG
3102 
3103 static void
3104 co_nmt_dn_ind(const co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx,
3105  size_t size, size_t nbyte, void *data)
3106 {
3107  co_nmt_t *nmt = data;
3108  assert(nmt);
3109 
3110  if (nmt->dn_ind)
3111  nmt->dn_ind(nmt, co_csdo_get_num(sdo), idx, subidx, size, nbyte,
3112  nmt->dn_data);
3113 }
3114 
3115 static void
3116 co_nmt_up_ind(const co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx,
3117  size_t size, size_t nbyte, void *data)
3118 {
3119  co_nmt_t *nmt = data;
3120  assert(nmt);
3121 
3122  if (nmt->up_ind)
3123  nmt->up_ind(nmt, co_csdo_get_num(sdo), idx, subidx, size, nbyte,
3124  nmt->up_data);
3125 }
3126 
3127 #endif // !LELY_NO_CO_NMT_BOOT || !LELY_NO_CO_NMT_CFG
3128 
3129 #if !LELY_NO_CO_TPDO
3130 
3131 static void
3132 co_nmt_tpdo_event_ind(co_unsigned16_t n, void *data)
3133 {
3134  co_nmt_t *nmt = data;
3135  assert(nmt);
3136 
3137  co_nmt_on_tpdo_event(nmt, n);
3138 }
3139 
3140 #if !LELY_NO_CO_MPDO
3141 static void
3142 co_nmt_sam_mpdo_event_ind(co_unsigned16_t n, co_unsigned16_t idx,
3143  co_unsigned8_t subidx, void *data)
3144 {
3145  co_nmt_t *nmt = data;
3146  assert(nmt);
3147 
3148  co_nmt_on_sam_mpdo_event(nmt, n, idx, subidx);
3149 }
3150 #endif
3151 
3152 #endif // !LELY_NO_CO_TPDO
3153 
3154 static void
3156 {
3157  assert(nmt);
3158 
3159  while (next) {
3160  co_nmt_state_t *prev = nmt->state;
3161  nmt->state = next;
3162 
3163  if (prev && prev->on_leave)
3164  prev->on_leave(nmt);
3165 
3166  next = next->on_enter ? next->on_enter(nmt) : NULL;
3167  }
3168 }
3169 
3170 static inline void
3171 co_nmt_emit_cs(co_nmt_t *nmt, co_unsigned8_t cs)
3172 {
3173  assert(nmt);
3174  assert(nmt->state);
3175  assert(nmt->state->on_cs);
3176 
3177  co_nmt_enter(nmt, nmt->state->on_cs(nmt, cs));
3178 }
3179 
3180 #if !LELY_NO_CO_NMT_BOOT
3181 
3182 static inline void
3183 co_nmt_emit_boot(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es)
3184 {
3185  assert(nmt);
3186  assert(nmt->state);
3187  assert(nmt->state->on_boot);
3188 
3189  co_nmt_enter(nmt, nmt->state->on_boot(nmt, id, st, es));
3190 }
3191 
3192 static co_nmt_state_t *
3194  co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es)
3195 {
3196  (void)nmt;
3197  (void)id;
3198  (void)st;
3199  (void)es;
3200 
3201  return NULL;
3202 }
3203 
3204 #endif // !LELY_NO_CO_NMT_BOOT
3205 
3206 static co_nmt_state_t *
3207 co_nmt_init_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
3208 {
3209  (void)nmt;
3210 
3211  switch (cs) {
3213  default: return NULL;
3214  }
3215 }
3216 
3217 static co_nmt_state_t *
3219 {
3220  assert(nmt);
3221 
3222  diag(DIAG_INFO, 0, "NMT: entering reset application state");
3223 
3224 #if !LELY_NO_CO_MASTER
3225  // Disable NMT slave management.
3226  co_nmt_slaves_fini(nmt);
3227  nmt->halt = 0;
3228 #endif
3229 
3230  // Disable all services.
3231  co_nmt_srv_set(&nmt->srv, nmt, 0);
3232 
3233  // Disable heartbeat consumption.
3234  co_nmt_hb_fini(nmt);
3235 
3236  // Disable error control services.
3237  co_nmt_ec_fini(nmt);
3238 
3239  // Stop receiving NMT commands.
3240  can_recv_stop(nmt->recv_000);
3241 
3242 #if !LELY_NO_CO_DCF_RESTORE
3243  // Reset application parameters.
3244  if (co_dev_read_dcf(nmt->dev, NULL, NULL, &nmt->dcf_node) == -1)
3246  "unable to reset application parameters");
3247 #endif
3248 
3249  nmt->st = CO_NMT_ST_RESET_NODE;
3250  co_nmt_st_ind(nmt, co_dev_get_id(nmt->dev), 0);
3251 
3252  if (nmt->cs_ind)
3253  nmt->cs_ind(nmt, CO_NMT_CS_RESET_NODE, nmt->cs_data);
3254 
3255  return co_nmt_reset_comm_state;
3256 }
3257 
3258 static co_nmt_state_t *
3260 {
3261  assert(nmt);
3262 
3263  diag(DIAG_INFO, 0, "NMT: entering reset communication state");
3264 
3265 #if !LELY_NO_CO_MASTER
3266  // Disable NMT slave management.
3267  co_nmt_slaves_fini(nmt);
3268  nmt->halt = 0;
3269 #endif
3270 
3271  // Disable all services.
3272  co_nmt_srv_set(&nmt->srv, nmt, 0);
3273 
3274  // Disable heartbeat consumption.
3275  co_nmt_hb_fini(nmt);
3276 
3277  // Disable error control services.
3278  co_nmt_ec_fini(nmt);
3279 
3280  // Stop receiving NMT commands.
3281  can_recv_stop(nmt->recv_000);
3282 
3283  // Reset communication parameters.
3284  if (co_dev_read_dcf(nmt->dev, NULL, NULL, &nmt->dcf_comm) == -1)
3286  "unable to reset communication parameters");
3287 
3288  // Update the node-ID if necessary.
3289  if (nmt->id != co_dev_get_id(nmt->dev)) {
3290  co_dev_set_id(nmt->dev, nmt->id);
3292  if (co_dev_write_dcf(nmt->dev, 0x1000, 0x1fff, &nmt->dcf_comm)
3293  == -1)
3295  "unable to store communication parameters");
3296  }
3297 
3298  // Load the NMT startup value.
3299  nmt->startup = co_dev_get_val_u32(nmt->dev, 0x1f80, 0x00);
3300 #if !LELY_NO_CO_MASTER
3301  // Bit 0 of the NMT startup value determines whether we are a master or
3302  // a slave.
3303  nmt->master = !!(nmt->startup & 0x01);
3304 #endif
3305  diag(DIAG_INFO, 0, "NMT: running as %s",
3306  co_nmt_is_master(nmt) ? "master" : "slave");
3307 
3308  nmt->st = CO_NMT_ST_RESET_COMM;
3309  co_nmt_st_ind(nmt, co_dev_get_id(nmt->dev), 0);
3310 
3311  // Start receiving NMT commands.
3312  if (!co_nmt_is_master(nmt))
3313  can_recv_start(nmt->recv_000, nmt->net, CO_NMT_CS_CANID, 0);
3314 
3315  // Enable LSS.
3316  co_nmt_srv_set(&nmt->srv, nmt, CO_NMT_SRV_LSS);
3317 
3318  if (nmt->cs_ind)
3319  nmt->cs_ind(nmt, CO_NMT_CS_RESET_COMM, nmt->cs_data);
3320 
3321 #if !LELY_NO_CO_MASTER && !LELY_NO_CO_LSS
3322  // If LSS is required, invoked the user-defined callback function and
3323  // wait for the process to complete.
3324  if (nmt->master && nmt->lss_req) {
3325  nmt->lss_req(nmt, co_nmt_get_lss(nmt), nmt->lss_data);
3326  return NULL;
3327  }
3328 #endif
3329 
3330  return co_nmt_bootup_state;
3331 }
3332 
3333 static co_nmt_state_t *
3334 co_nmt_reset_comm_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
3335 {
3336  (void)nmt;
3337 
3338  switch (cs) {
3341  default: return NULL;
3342  }
3343 }
3344 
3345 static co_nmt_state_t *
3347 {
3348  assert(nmt);
3349 
3350  // Don't enter the 'pre-operational' state if the node-ID is invalid.
3351  if (co_dev_get_id(nmt->dev) == 0xff) {
3352  diag(DIAG_INFO, 0, "NMT: unconfigured node-ID");
3353  return NULL;
3354  }
3355 
3356  // Enable error control services.
3357  co_nmt_ec_init(nmt);
3358 
3359  // Enable heartbeat consumption.
3360  co_nmt_hb_init(nmt);
3361 
3362  // Send the boot-up signal to notify the master we exist.
3364 
3365  return co_nmt_preop_state;
3366 }
3367 
3368 static co_nmt_state_t *
3369 co_nmt_bootup_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
3370 {
3371  (void)nmt;
3372 
3373  switch (cs) {
3376  default: return NULL;
3377  }
3378 }
3379 
3380 static co_nmt_state_t *
3382 {
3383  assert(nmt);
3384 
3385  diag(DIAG_INFO, 0, "NMT: entering pre-operational state");
3386 
3387 #if !LELY_NO_CO_MASTER
3388  // Disable NMT slave management.
3389  co_nmt_slaves_fini(nmt);
3390  nmt->halt = 0;
3391 #endif
3392 
3393  // Enable all services except PDO.
3394  co_nmt_srv_set(&nmt->srv, nmt, CO_NMT_PREOP_SRV);
3395 
3396  nmt->st = CO_NMT_ST_PREOP | (nmt->st & CO_NMT_ST_TOGGLE);
3397  co_nmt_st_ind(nmt, co_dev_get_id(nmt->dev), nmt->st);
3398 
3399  if (nmt->cs_ind)
3400  nmt->cs_ind(nmt, CO_NMT_CS_ENTER_PREOP, nmt->cs_data);
3401 
3402  return co_nmt_startup(nmt);
3403 }
3404 
3405 static co_nmt_state_t *
3406 co_nmt_preop_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
3407 {
3408  (void)nmt;
3409 
3410  switch (cs) {
3411  case CO_NMT_CS_START: return co_nmt_start_state;
3412  case CO_NMT_CS_STOP: return co_nmt_stop_state;
3415  default: return NULL;
3416  }
3417 }
3418 
3419 #if !LELY_NO_CO_NMT_BOOT
3420 static co_nmt_state_t *
3422  co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es)
3423 {
3424  assert(nmt);
3425  assert(nmt->master);
3426  assert(id && id <= CO_NUM_NODES);
3427  (void)st;
3428 
3429  // If the 'boot slave' process failed for a mandatory slave, halt the
3430  // network boot-up procedure.
3431  if ((nmt->slaves[id - 1].assignment & 0x09) == 0x09 && es && es != 'L')
3432  nmt->halt = 1;
3433 
3434  // Wait for any mandatory slaves that have not yet finished booting.
3435  int wait = nmt->halt;
3436  for (co_unsigned8_t id = 1; !wait && id <= CO_NUM_NODES; id++)
3437  wait = (nmt->slaves[id - 1].assignment & 0x09) == 0x09
3438  && nmt->slaves[id - 1].boot;
3439  if (!wait) {
3440  trace("NMT: all mandatory slaves started successfully");
3441  return co_nmt_startup_slave(nmt);
3442  }
3443  return NULL;
3444 }
3445 #endif
3446 
3447 static co_nmt_state_t *
3449 {
3450  assert(nmt);
3451 
3452  diag(DIAG_INFO, 0, "NMT: entering operational state");
3453 
3454 #if !LELY_NO_CO_TPDO
3455  // Reset all Transmit-PDO events.
3456  for (int i = 0; i < CO_NUM_PDOS / LONG_BIT; i++)
3457  nmt->tpdo_event_mask[i] = 0;
3458 #endif
3459 
3460  // Enable all services.
3461  co_nmt_srv_set(&nmt->srv, nmt, CO_NMT_START_SRV);
3462 
3463  nmt->st = CO_NMT_ST_START | (nmt->st & CO_NMT_ST_TOGGLE);
3464  co_nmt_st_ind(nmt, co_dev_get_id(nmt->dev), nmt->st);
3465 
3466 #if !LELY_NO_CO_NMT_BOOT
3467  // If we're the master and bit 3 of the NMT startup value is 0 and bit 1
3468  // is 1, send the NMT start remote node command to all nodes (see Fig. 2
3469  // in CiA 302-2 version 4.1.0).
3470  if (nmt->master && (nmt->startup & 0x0a) == 0x02) {
3471  // Check if all slaves booted successfully.
3472  int boot = 1;
3473  for (co_unsigned8_t id = 1; boot && id <= CO_NUM_NODES; id++) {
3474  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
3475  // Skip those slaves that are not in the network list.
3476  if (!(slave->assignment & 0x01))
3477  continue;
3478  // Check if the slave finished booting successfully and
3479  // can be started by the master.
3480  boot = slave->booted && (!slave->es || slave->es == 'L')
3481  && !(slave->assignment & 0x04);
3482  }
3483  if (boot) {
3484  // Start all NMT slaves at once.
3486  } else {
3487  for (co_unsigned8_t id = 1; id <= CO_NUM_NODES; id++) {
3488  struct co_nmt_slave *slave =
3489  &nmt->slaves[id - 1];
3490  // Skip those slaves that are not in the network
3491  // list (bit 0), or that we are not allowed to
3492  // boot (bit 2).
3493  if ((slave->assignment & 0x05) != 0x05)
3494  continue;
3495  // Only start slaves that have finished booting
3496  // successfully and are not already (expected to
3497  // be) operational.
3498  if (slave->booted
3499  && (!slave->es || slave->es == 'L')
3500  && slave->est != CO_NMT_ST_START)
3502  }
3503  }
3504  }
3505 #endif
3506 
3507  if (nmt->cs_ind)
3509 
3510  return NULL;
3511 }
3512 
3513 static co_nmt_state_t *
3514 co_nmt_start_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
3515 {
3516  (void)nmt;
3517 
3518  switch (cs) {
3519  case CO_NMT_CS_STOP: return co_nmt_stop_state;
3523  default: return NULL;
3524  }
3525 }
3526 
3527 static co_nmt_state_t *
3529 {
3530  assert(nmt);
3531 
3532  diag(DIAG_INFO, 0, "NMT: entering stopped state");
3533 
3534  // Disable all services (except LSS).
3536 
3539 
3540  if (nmt->cs_ind)
3542 
3543  return NULL;
3544 }
3545 
3546 static co_nmt_state_t *
3547 co_nmt_stop_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
3548 {
3549  (void)nmt;
3550 
3551  switch (cs) {
3552  case CO_NMT_CS_START: return co_nmt_start_state;
3556  default: return NULL;
3557  }
3558 }
3559 
3560 static co_nmt_state_t *
3562 {
3563  assert(nmt);
3564 
3565 #if !LELY_NO_CO_MASTER
3566  if (nmt->master)
3567  return co_nmt_startup_master(nmt);
3568 #endif
3569  return co_nmt_startup_slave(nmt);
3570 }
3571 
3572 #if !LELY_NO_CO_MASTER
3573 static co_nmt_state_t *
3575 {
3576  assert(nmt);
3577  assert(nmt->master);
3578 
3579  // Enable NMT slave management.
3581 
3582 #if LELY_NO_CO_NMT_BOOT
3583  // Send the NMT 'reset communication' command to all slaves.
3585 
3586  return co_nmt_startup_slave(nmt);
3587 #else
3588  // Check if any node has the keep-alive bit set.
3589  int keep = 0;
3590  for (co_unsigned8_t id = 1; !keep && id <= CO_NUM_NODES; id++)
3591  keep = (nmt->slaves[id - 1].assignment & 0x11) == 0x11;
3592 
3593  // Send the NMT 'reset communication' command to all slaves with
3594  // the keep-alive bit _not_ set. This includes slaves which are not in
3595  // the network list.
3596  if (keep) {
3597  for (co_unsigned8_t id = 1; id <= CO_NUM_NODES; id++) {
3598  // Do not reset the master itself.
3599  if (id == co_dev_get_id(nmt->dev))
3600  continue;
3601  if ((nmt->slaves[id - 1].assignment & 0x11) != 0x11)
3603  }
3604  } else {
3606  }
3607 
3608  // Start the 'boot slave' processes.
3609  switch (co_nmt_slaves_boot(nmt)) {
3610  case -1:
3611  // Halt the network boot-up procedure if the 'boot slave'
3612  // process failed for a mandatory slave.
3613  nmt->halt = 1;
3614  return NULL;
3615  case 0: return co_nmt_startup_slave(nmt);
3616  default:
3617  // Wait for all mandatory slaves to finish booting.
3618  trace("NMT: waiting for mandatory slaves to start");
3619  return NULL;
3620  }
3621 #endif
3622 }
3623 #endif
3624 
3625 static co_nmt_state_t *
3627 {
3628  assert(nmt);
3629 
3630  // Enter the operational state automatically if bit 2 of the NMT startup
3631  // value is 0.
3632  return (nmt->startup & 0x04) ? NULL : co_nmt_start_state;
3633 }
3634 
3635 static void
3637 {
3638  assert(nmt);
3639 
3640  // Enable life guarding or heartbeat production.
3641 #if !LELY_NO_CO_NG
3642  nmt->gt = co_dev_get_val_u16(nmt->dev, 0x100c, 0x00);
3643  nmt->ltf = co_dev_get_val_u8(nmt->dev, 0x100d, 0x00);
3644 #endif
3645  nmt->ms = co_dev_get_val_u16(nmt->dev, 0x1017, 0x00);
3646 
3647 #if !LELY_NO_CO_NG
3649 #endif
3650 
3652 }
3653 
3654 static void
3656 {
3657  assert(nmt);
3658 
3659  // Disable life guarding and heartbeat production.
3660 #if !LELY_NO_CO_NG
3661  nmt->gt = 0;
3662  nmt->ltf = 0;
3663 #endif
3664  nmt->ms = 0;
3665 
3666 #if !LELY_NO_CO_NG
3668 #endif
3669 
3671 }
3672 
3673 static void
3675 {
3676  assert(nmt);
3677 
3678 #if !LELY_NO_CO_NG
3679  // Heartbeat production has precedence over life guarding.
3680  int lt = nmt->ms ? 0 : nmt->gt * nmt->ltf;
3681 #if !LELY_NO_CO_MASTER
3682  // Disable life guarding for the master.
3683  if (nmt->master)
3684  lt = 0;
3685 #endif
3686 
3687  if (lt) {
3688  // Start the CAN frame receiver for node guarding RTRs.
3691  CAN_FLAG_RTR);
3692  } else {
3694  }
3695 #endif
3696 
3697  // Start the CAN timer for heartbeat production or life guarding, if
3698  // necessary.
3699 #if LELY_NO_CO_NG
3700  int ms = nmt->ms;
3701 #else
3702  int ms = nmt->ms ? nmt->ms : lt;
3703 #endif
3704  if (ms) {
3705  struct timespec interval = { ms / 1000, (ms % 1000) * 1000000 };
3706  can_timer_start(nmt->ec_timer, nmt->net, NULL, &interval);
3707  } else {
3708  can_timer_stop(nmt->ec_timer);
3709  }
3710 }
3711 
3712 static int
3713 co_nmt_ec_send_res(co_nmt_t *nmt, co_unsigned8_t st)
3714 {
3715  assert(nmt);
3716 
3717  struct can_msg msg = CAN_MSG_INIT;
3718  msg.id = CO_NMT_EC_CANID(co_dev_get_id(nmt->dev));
3719  msg.len = 1;
3720  msg.data[0] = st;
3721 
3722  return can_net_send(nmt->net, &msg);
3723 }
3724 
3725 static void
3727 {
3728  assert(nmt);
3729 
3730  // Create and initialize the heartbeat consumers.
3731 #if LELY_NO_MALLOC
3732  memset(nmt->hbs, 0, CO_NMT_MAX_NHB * sizeof(*nmt->hbs));
3733 #else
3734  assert(!nmt->hbs);
3735 #endif
3736  assert(!nmt->nhb);
3737  co_obj_t *obj_1016 = co_dev_find_obj(nmt->dev, 0x1016);
3738  if (obj_1016) {
3739  nmt->nhb = co_obj_get_val_u8(obj_1016, 0x00);
3740 #if LELY_NO_MALLOC
3741  if (nmt->nhb > CO_NMT_MAX_NHB) {
3743 #else // !LELY_NO_MALLOC
3744  nmt->hbs = calloc(nmt->nhb, sizeof(*nmt->hbs));
3745  if (!nmt->hbs && nmt->nhb) {
3746 #if !LELY_NO_ERRNO
3747  set_errc(errno2c(errno));
3748 #endif
3749 #endif // !LELY_NO_MALLOC
3751  "unable to create heartbeat consumers");
3752  nmt->nhb = 0;
3753  }
3754  }
3755 
3756  for (co_unsigned8_t i = 0; i < nmt->nhb; i++) {
3757  nmt->hbs[i] = co_nmt_hb_create(nmt->net, nmt);
3758  if (!nmt->hbs[i]) {
3760  "unable to create heartbeat consumer 0x%02X",
3761  (co_unsigned8_t)(i + 1));
3762  continue;
3763  }
3764 
3765  co_unsigned32_t val = co_obj_get_val_u32(obj_1016, i + 1);
3766  co_unsigned8_t id = (val >> 16) & 0xff;
3767  co_unsigned16_t ms = val & 0xffff;
3768  co_nmt_hb_set_1016(nmt->hbs[i], id, ms);
3769  }
3770 }
3771 
3772 static void
3774 {
3775  assert(nmt);
3776 
3777  // Destroy all heartbeat consumers.
3778  for (size_t i = 0; i < nmt->nhb; i++)
3779  co_nmt_hb_destroy(nmt->hbs[i]);
3780 #if !LELY_NO_MALLOC
3781  free(nmt->hbs);
3782  nmt->hbs = NULL;
3783 #endif
3784  nmt->nhb = 0;
3785 }
3786 
3787 #if !LELY_NO_CO_MASTER
3788 
3789 #if !LELY_NO_CO_NMT_BOOT || !LELY_NO_CO_NMT_CFG
3790 static co_nmt_hb_t *
3791 co_nmt_hb_find(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned16_t *pms)
3792 {
3793  assert(nmt);
3794  assert(id && id <= CO_NUM_NODES);
3795 
3796  const co_obj_t *obj_1016 = co_dev_find_obj(nmt->dev, 0x1016);
3797  if (!obj_1016)
3798  return NULL;
3799 
3800  for (co_unsigned8_t i = 0; i < nmt->nhb; i++) {
3801  co_unsigned32_t val = co_obj_get_val_u32(obj_1016, i + 1);
3802  if (id == ((val >> 16) & 0xff)) {
3803  if (pms)
3804  *pms = val & 0xffff;
3805  return nmt->hbs[i];
3806  }
3807  }
3808  return NULL;
3809 }
3810 #endif
3811 
3812 static void
3814 {
3815  assert(nmt);
3816  assert(nmt->master);
3817 
3818  co_nmt_slaves_fini(nmt);
3819 
3820  for (co_unsigned8_t id = 1; id <= CO_NUM_NODES; id++)
3821  // Start listening for boot-up notifications.
3822  can_recv_start(nmt->slaves[id - 1].recv, nmt->net,
3823  CO_NMT_EC_CANID(id), 0);
3824 
3825  co_obj_t *obj_1f81 = co_dev_find_obj(nmt->dev, 0x1f81);
3826  if (!obj_1f81)
3827  return;
3828 
3829  co_unsigned8_t n = co_obj_get_val_u8(obj_1f81, 0x00);
3830  for (co_unsigned8_t i = 0; i < MIN(n, CO_NUM_NODES); i++)
3831  nmt->slaves[i].assignment = co_obj_get_val_u32(obj_1f81, i + 1);
3832 }
3833 
3834 static void
3836 {
3837  assert(nmt);
3838 
3839  for (co_unsigned8_t id = 1; id <= CO_NUM_NODES; id++) {
3840  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
3841 
3842  can_recv_stop(slave->recv);
3843 #if !LELY_NO_CO_NG
3844  can_timer_stop(slave->timer);
3845 #endif
3846 
3847  slave->assignment = 0;
3848  slave->est = 0;
3849  slave->rst = 0;
3850 
3851 #if !LELY_NO_CO_NMT_BOOT
3852  slave->es = 0;
3853 
3854  slave->booting = 0;
3855  slave->booted = 0;
3856 
3857  co_nmt_boot_destroy(slave->boot);
3858  slave->boot = NULL;
3859 #endif
3860  slave->bootup = 0;
3861 
3862 #if !LELY_NO_CO_NMT_CFG
3863  slave->configuring = 0;
3864 
3865  co_nmt_cfg_destroy(slave->cfg);
3866  slave->cfg = NULL;
3867  slave->cfg_con = NULL;
3868  slave->cfg_data = NULL;
3869 #endif
3870 
3871 #if !LELY_NO_CO_NG
3872  slave->gt = 0;
3873  slave->ltf = 0;
3874  slave->rtr = 0;
3875  slave->ng_state = CO_NMT_EC_RESOLVED;
3876 #endif
3877  }
3878 }
3879 
3880 #if !LELY_NO_CO_NMT_BOOT
3881 static int
3883 {
3884  assert(nmt);
3885  assert(nmt->master);
3886 
3887  int res = 0;
3888  for (co_unsigned8_t id = 1; id <= CO_NUM_NODES; id++) {
3889  struct co_nmt_slave *slave = &nmt->slaves[id - 1];
3890  // Skip those slaves that are not in the network list (bit 0).
3891  if ((slave->assignment & 0x01) != 0x01)
3892  continue;
3893  int mandatory = !!(slave->assignment & 0x08);
3894  // Wait for all mandatory slaves to finish booting.
3895  if (!res && mandatory)
3896  res = 1;
3897  // Optional slaves with the keep-alive bit _not_ set are booted
3898  // when we receive their boot-up signal.
3899  if (!mandatory && !(slave->assignment & 0x10))
3900  continue;
3901  // Halt the network boot-up procedure if the 'boot slave'
3902  // process failed for a mandatory slave with the keep-alive bit
3903  // set.
3904  if (co_nmt_boot_req(nmt, id, nmt->timeout) == -1 && mandatory)
3905  res = -1;
3906  }
3907  return res;
3908 }
3909 #endif
3910 
3911 static int
3913 {
3914  for (co_unsigned8_t node_id = 1; node_id <= CO_NUM_NODES; node_id++) {
3915  const struct co_nmt_slave *const slave =
3916  &nmt->slaves[node_id - 1];
3917  // Skip those slaves that are not in the network list (bit 0).
3918  if ((slave->assignment & 0x01) != 0x01)
3919  continue;
3920  // Skip non-mandatory slaves (bit 3).
3921  if ((slave->assignment & 0x08) != 0x08)
3922  continue;
3923  // Check if we have received a boot-up message from a slave.
3924  if (!slave->bootup)
3925  return 0;
3926  }
3927  return 1;
3928 }
3929 
3930 #endif // !LELY_NO_CO_MASTER
co_nmt_get_st
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,...
Definition: nmt.c:1766
emcy.h
co_nmt_get_time
co_time_t * co_nmt_get_time(const co_nmt_t *nmt)
Returns a pointer to the TIME producer/consumer service.
Definition: nmt.c:2254
co_nmt_slave::booted
unsigned booted
A flag specifying whether the 'boot slave' process has ended.
Definition: nmt.c:119
CO_NMT_ST_STOP
#define CO_NMT_ST_STOP
The NMT state 'stopped'.
Definition: nmt.h:58
can_recv_destroy
void can_recv_destroy(can_recv_t *recv)
Destroys a CAN frame receiver.
Definition: net.c:558
co_nmt_hb_find
static co_nmt_hb_t * co_nmt_hb_find(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned16_t *pms)
Find the heartbeat consumer for the specified node.
Definition: nmt.c:3791
co_sub_dn
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:996
__co_nmt::tpdo_event_wait
size_t tpdo_event_wait
The number of calls to co_nmt_on_tpdo_event_lock() minus the number of calls to co_nmt_on_tpdo_event_...
Definition: nmt.c:286
__co_nmt::cs_data
void * cs_data
A pointer to user-specified data for cs_ind.
Definition: nmt.c:176
co_nmt_init_state
static co_nmt_state_t *const co_nmt_init_state
The 'initializing' state.
Definition: nmt.c:533
can_msg::flags
uint_least8_t flags
The flags (any combination of CAN_FLAG_IDE, CAN_FLAG_RTR, CAN_FLAG_FDF, CAN_FLAG_BRS and CAN_FLAG_ESI...
Definition: msg.h:94
__co_nmt::nhb
co_unsigned8_t nhb
The number of heartbeat consumers.
Definition: nmt.c:215
co_nmt_recv_700
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:2791
co_nmt_get_up_ind
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:1568
co_nmt_stop_on_cs
static co_nmt_state_t * co_nmt_stop_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
The 'NMT command received' transition function of the 'stopped' state.
Definition: nmt.c:3547
co_nmt_node_err_ind
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:2132
co_nmt_reset_node_state
static co_nmt_state_t *const co_nmt_reset_node_state
The NMT 'reset application' state.
Definition: nmt.c:543
co_100d_dn_ind
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:2472
__co_nmt::dcf_node
void * dcf_node
The concise DCF of the application parameters.
Definition: nmt.c:157
co_nmt_cs_ind_t
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_nmt_srv::lss
co_lss_t * lss
A pointer to the LSS master/slave service.
Definition: nmt_srv.h:75
can_buf_read
LELY_CAN_BUF_INLINE 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:224
co_nmt_slave::es
char es
The error status of the 'boot slave' process.
Definition: nmt.c:104
can_buf
A CAN frame buffer.
Definition: buf.h:42
can_msg::data
uint_least8_t data[CAN_MSG_MAX_LEN]
The frame payload (in case of a data frame).
Definition: msg.h:102
co_emcy_find
ssize_t co_emcy_find(const co_emcy_t *emcy, co_unsigned16_t eec)
Finds a CANopen EMCY message in the stack.
Definition: emcy.c:582
co_nmt_stop_on_enter
static co_nmt_state_t * co_nmt_stop_on_enter(co_nmt_t *nmt)
The entry function of the 'stopped' state.
Definition: nmt.c:3528
__co_nmt::net
can_net_t * net
A pointer to a CAN network interface.
Definition: nmt.c:150
__co_nmt::recv_000
can_recv_t * recv_000
A pointer to the CAN frame receiver for NMT messages.
Definition: nmt.c:172
co_sam_mpdo_event
int co_sam_mpdo_event(co_tpdo_t *pdo, co_unsigned16_t idx, co_unsigned8_t subidx)
Triggers the transmission of a DAM-MPDO.
Definition: tpdo.c:730
co_nmt_ec_timer
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:2958
co_nmt_hb_ind
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:2420
dev.h
can_net_get_time
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:196
co_nmt_hb_ind_t
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
co_nmt_lss_con
int co_nmt_lss_con(co_nmt_t *nmt)
Confirms the completion of the process when booting an NMT master.
Definition: nmt.c:1853
co_1f82_dn_ind
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:2714
co_nmt_cs_timer
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:2982
val.h
co_val_fini
void co_val_fini(co_unsigned16_t type, void *val)
Finalizes a value of the specified data type.
Definition: val.c:249
CO_NMT_ST_RESET_NODE
#define CO_NMT_ST_RESET_NODE
The NMT sub-state 'reset application'.
Definition: nmt.h:64
CO_DEFTYPE_UNSIGNED32
#define CO_DEFTYPE_UNSIGNED32
The data type (and object index) of a 32-bit unsigned integer.
Definition: type.h:50
co_nmt_get_st_ind
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:1434
co_nmt_get_lss_req
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:1478
co_nmt_get_timeout
int co_nmt_get_timeout(const co_nmt_t *nmt)
Returns the default SDO timeout used during the NMT 'boot slave' and 'check configuration' processes.
Definition: nmt.c:1790
co_nmt_set_cs_ind
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:1294
co_nmt_bootup_on_enter
static co_nmt_state_t * co_nmt_bootup_on_enter(co_nmt_t *nmt)
The entry function of the 'boot-up' state.
Definition: nmt.c:3346
co_nmt_get_lss
co_lss_t * co_nmt_get_lss(const co_nmt_t *nmt)
Returns a pointer to the LSS master/slave service.
Definition: nmt.c:2282
can_buf_reserve
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:53
co_nmt_srv::nrpdo
co_unsigned16_t nrpdo
The number of Receive-PDO services.
Definition: nmt_srv.h:43
string.h
DIAG_INFO
@ DIAG_INFO
An informational message.
Definition: diag.h:53
co_nmt_on_err
void co_nmt_on_err(co_nmt_t *nmt, co_unsigned16_t eec, co_unsigned8_t er, const co_unsigned8_t msef[5])
Implements the default error handling behavior by generating an EMCY message with co_emcy_push() and ...
Definition: nmt.c:1636
__co_nmt::st
co_unsigned8_t st
The state of the NMT service (including the toggle bit).
Definition: nmt.c:190
co_nmt_ng_ind_t
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
co_nmt_srv::sync
co_sync_t * sync
A pointer to the SYNC producer/consumer service.
Definition: nmt_srv.h:63
__co_time
A CANopen TIME producer/consumer service.
Definition: time.c:41
co_nmt_cfg_con
void co_nmt_cfg_con(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned32_t ac)
The CANopen NMT 'configuration request' confirmation function, invoked when a configuration request c...
Definition: nmt.c:2389
can_msg
A CAN or CAN FD format frame.
Definition: msg.h:87
CO_DEFTYPE_UNSIGNED16
#define CO_DEFTYPE_UNSIGNED16
The data type (and object index) of a 16-bit unsigned integer.
Definition: type.h:47
CO_NUM_NODES
#define CO_NUM_NODES
The maximum number of nodes in a CANopen network.
Definition: dev.h:56
__co_nmt::timeout
int timeout
The default SDO timeout (in milliseconds) used during the NMT 'boot slave' and 'check configuration' ...
Definition: nmt.c:255
can_msg::len
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:100
diag.h
co_nmt_hb_set_st
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:192
co_nmt_on_ng
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....
Definition: nmt.c:1327
co_nmt_slaves_fini
static void co_nmt_slaves_fini(co_nmt_t *nmt)
Finalizes NMT slave management.
Definition: nmt.c:3835
can_timer_set_func
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:422
MIN
#define MIN(a, b)
Returns the minimum of a and b.
Definition: util.h:57
can_timer_stop
void can_timer_stop(can_timer_t *timer)
Stops a CAN timer and unregisters it with a network interface.
Definition: net.c:462
co_nmt_get_rpdo
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:2175
co_nmt_start_state
static co_nmt_state_t *const co_nmt_start_state
The NMT 'operational' state.
Definition: nmt.c:630
co_nmt_sdo_ind_t
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
__can_recv
A CAN frame receiver.
Definition: net.c:86
co_nmt_cfg_con_t
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 'configuration request' confirmation callback function, invoked when a conf...
Definition: nmt.h:227
co_nmt_slave::cfg_data
void * cfg_data
A pointer to user-specified data for cfg_con.
Definition: nmt.c:129
nmt_boot.h
co_nmt_reset_comm_state
static co_nmt_state_t *const co_nmt_reset_comm_state
The NMT 'reset communication' state.
Definition: nmt.c:561
__co_nmt::halt
int halt
A flag indicating if the startup procedure was halted because of a mandatory slave boot failure.
Definition: nmt.c:248
can_recv_set_func
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:578
co_nmt_srv::csdos
co_csdo_t ** csdos
An array of pointers to the Client-SDO services.
Definition: nmt_srv.h:57
co_nmt_srv::tpdos
co_tpdo_t ** tpdos
An array of pointers to the Transmit-PDO services.
Definition: nmt_srv.h:47
co_nmt_slave::recv
can_recv_t * recv
A pointer to the CAN frame receiver for the boot-up event and node guarding messages.
Definition: nmt.c:91
co_nmt_cfg_res
int co_nmt_cfg_res(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned32_t ac)
Indicates the result of the 'update configuration' step of an NMT 'request configuration' request for...
Definition: nmt.c:2040
co_nmt_lg_ind_t
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_nmt_boot_ind_t
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 'boot slave' indication function, invoked when the 'boot slave' process com...
Definition: nmt.h:200
__co_nmt::hbs
co_nmt_hb_t ** hbs
An array of pointers to the heartbeat consumers.
Definition: nmt.c:212
co_nmt_get_sync_ind
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:1590
__co_nmt::srv
struct co_nmt_srv srv
The NMT service manager.
Definition: nmt.c:164
__co_nmt::id
co_unsigned8_t id
The pending node-ID.
Definition: nmt.c:154
co_nmt_set_ng_ind
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:1318
__co_nmt::hb_ind
co_nmt_hb_ind_t * hb_ind
A pointer to the heartbeat event indication function.
Definition: nmt.c:217
__co_nmt::dn_data
void * dn_data
A pointer to user-specified data for dn_ind.
Definition: nmt.c:271
__co_nmt::dn_ind
co_nmt_sdo_ind_t * dn_ind
A pointer to the SDO download progress indication function.
Definition: nmt.c:269
co_nmt_srv::emcy
co_emcy_t * emcy
A pointer to the EMCY producer/consumer service.
Definition: nmt_srv.h:71
co_dev_get_id
co_unsigned8_t co_dev_get_id(const co_dev_t *dev)
Returns the node-ID of a CANopen device.
Definition: dev.c:197
get_errc
int get_errc(void)
Returns the last (thread-specific) native error code set by a system call or library function.
Definition: errnum.c:932
timespec_cmp
int timespec_cmp(const void *p1, const void *p2)
Compares two times.
Definition: time.h:251
__co_nmt::buf
struct can_buf buf
A pointer to the CAN frame buffer for NMT messages.
Definition: nmt.c:226
ERRNUM_NOMEM
@ ERRNUM_NOMEM
Not enough space.
Definition: errnum.h:172
co_nmt_boot_boot_req
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 'boot slave' service.
Definition: nmt_boot.c:919
co_nmt_boot_create
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 'boot slave' service.
Definition: nmt_boot.c:885
errno2c
int errno2c(int errnum)
Transforms a standard C error number to a native error code.
Definition: errnum.c:46
co_nmt_hb_create
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:137
co_nmt_cfg_ind_t
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 'update configuration' indication function, invoked when a configuration re...
Definition: nmt.h:214
CO_NMT_ST_RESET_COMM
#define CO_NMT_ST_RESET_COMM
The NMT sub-state 'reset communication'.
Definition: nmt.h:67
co_nmt_cfg_req
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 'configuration request' for the specified node.
Definition: nmt.c:1979
CO_DEFTYPE_UNSIGNED8
#define CO_DEFTYPE_UNSIGNED8
The data type (and object index) of an 8-bit unsigned integer.
Definition: type.h:44
co_nmt_startup
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:3561
CO_DEFTYPE_DOMAIN
#define CO_DEFTYPE_DOMAIN
The data type (and object index) of an arbitrary large block of data.
Definition: type.h:77
co_nmt_get_id
co_unsigned8_t co_nmt_get_id(const co_nmt_t *nmt)
Returns the pending node-ID.
Definition: nmt.c:1743
co_nmt_on_tpdo_event_lock
void co_nmt_on_tpdo_event_lock(co_nmt_t *nmt)
Postpones the transmission of PDOs triggered by co_nmt_on_tpdo_event() until a matching call to co_nm...
Definition: nmt.c:1690
co.h
co_1017_dn_ind
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:2572
CO_NMT_ST_PREOP
#define CO_NMT_ST_PREOP
The NMT state 'pre-operational'.
Definition: nmt.h:70
CO_NMT_START_SRV
#define CO_NMT_START_SRV
The services enabled in the NMT 'operational' state.
Definition: nmt.c:738
co_dev_get_val
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:567
CO_NMT_EC_CANID
#define CO_NMT_EC_CANID(id)
The CAN identifier used for both node guarding and heartbeat monitoring.
Definition: nmt.h:76
CO_NMT_EC_TIMEOUT
@ CO_NMT_EC_TIMEOUT
An NMT error control timeout event.
Definition: nmt.h:87
sdo.h
can_timer_start
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:431
__co_nmt::ltf
co_unsigned8_t ltf
The lifetime factor.
Definition: nmt.c:195
co_nmt_lss_req_t
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
CO_NMT_CS_STOP
#define CO_NMT_CS_STOP
The NMT command specifier 'stop'.
Definition: nmt.h:43
CAN_MSG_INIT
#define CAN_MSG_INIT
The static initializer for can_msg.
Definition: msg.h:113
co_nmt_ec_fini
static void co_nmt_ec_fini(co_nmt_t *nmt)
Finalizes the error control services.
Definition: nmt.c:3655
CO_NMT_PREOP_SRV
#define CO_NMT_PREOP_SRV
The services enabled in the NMT 'pre-operational' state.
Definition: nmt.c:733
co_nmt_srv_init
void co_nmt_srv_init(struct co_nmt_srv *srv, co_nmt_t *nmt)
Initializes a CANopen NMT service manager.
Definition: nmt_srv.c:111
default_lg_ind
static void default_lg_ind(co_nmt_t *nmt, int state, void *data)
The default life guarding event handler.
Definition: nmt.c:3075
__co_nmt::slaves
struct co_nmt_slave slaves[CO_NUM_NODES]
An array containing the state of each NMT slave.
Definition: nmt.c:250
co_nmt_slaves_boot
static int co_nmt_slaves_boot(co_nmt_t *nmt)
Starts the NMT 'boot slave' processes.
Definition: nmt.c:3882
default_hb_ind
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:3085
co_rpdo_sync
int co_rpdo_sync(co_rpdo_t *pdo, co_unsigned8_t cnt)
Triggers the actuation of a received synchronous PDO.
Definition: rpdo.c:493
co_nmt_preop_on_enter
static co_nmt_state_t * co_nmt_preop_on_enter(co_nmt_t *nmt)
The entry function of the 'pre-operational' state.
Definition: nmt.c:3381
__co_nmt::ng_data
void * ng_data
A pointer to user-specified data for ng_ind.
Definition: nmt.c:183
CO_NMT_CS_START
#define CO_NMT_CS_START
The NMT command specifier 'start'.
Definition: nmt.h:40
co_nmt_get_cs_ind
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:1283
__co_nmt_state::on_enter
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:484
__co_nmt::cfg_ind
co_nmt_cfg_ind_t * cfg_ind
A pointer to the NMT 'configuration request' indication function.
Definition: nmt.c:264
__co_rpdo
A CANopen Receive-PDO.
Definition: rpdo.c:44
co_nmt_bootup_on_cs
static co_nmt_state_t * co_nmt_bootup_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
The 'NMT command received' transition function of the 'boot-up' state.
Definition: nmt.c:3369
CO_SDO_AC_NO_OBJ
#define CO_SDO_AC_NO_OBJ
SDO abort code: Object does not exist in the object dictionary.
Definition: sdo.h:93
co_nmt_cfg_destroy
void co_nmt_cfg_destroy(co_nmt_cfg_t *cfg)
Destroys a CANopen NMT 'configuration request'.
Definition: nmt_cfg.c:426
__co_ssdo
A CANopen Server-SDO.
Definition: ssdo.c:63
co_nmt_set_id
int co_nmt_set_id(co_nmt_t *nmt, co_unsigned8_t id)
Sets the pending node-ID.
Definition: nmt.c:1751
co_nmt_on_sam_mpdo_event
void co_nmt_on_sam_mpdo_event(co_nmt_t *nmt, co_unsigned16_t n, co_unsigned16_t idx, co_unsigned8_t subidx)
Implements the default behavior when an event is indicated for a source address mode multiplex PDO by...
Definition: nmt.c:1729
co_nmt_chk_bootup_slaves
static int co_nmt_chk_bootup_slaves(const co_nmt_t *nmt)
Checks if boot-up messages have been received from all mandatory slaves.
Definition: nmt.c:3912
CO_SDO_AC_DATA_CTL
#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
co_nmt_srv::nssdo
co_unsigned8_t nssdo
The number of Server-SDO services.
Definition: nmt_srv.h:54
co_obj_set_dn_ind
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:389
co_nmt_is_master
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:1774
co_nmt_set_lss_req
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:1489
buf.h
co_nmt_get_cfg_ind
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 'configuration request' is received.
Definition: nmt.c:1526
co_nmt_srv_fini
void co_nmt_srv_fini(struct co_nmt_srv *srv)
Finalizes a CANopen NMT service manager.
Definition: nmt_srv.c:152
co_nmt_init_on_cs
static co_nmt_state_t * co_nmt_init_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
The 'NMT command received' transition function of the 'initializing' state.
Definition: nmt.c:3207
DIAG_ERROR
@ DIAG_ERROR
An error.
Definition: diag.h:57
co_nmt_tpdo_event_ind
static void co_nmt_tpdo_event_ind(co_unsigned16_t n, void *data)
The Transmit-PDO event indication function.
Definition: nmt.c:3132
__co_tpdo
A CANopen Transmit-PDO.
Definition: tpdo.c:53
co_nmt_ng_timer
static int co_nmt_ng_timer(const struct timespec *tp, void *data)
The CAN timer callback function for node guarding.
Definition: nmt.c:2915
co_nmt_set_dn_ind
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:1559
__can_timer
A CAN timer.
Definition: net.c:63
__co_nmt::lg_state
int lg_state
Indicates whether a life guarding error occurred (CO_NMT_EC_OCCURRED or CO_NMT_EC_RESOLVED).
Definition: nmt.c:200
co_nmt_slave::rtr
co_unsigned8_t rtr
The number of unanswered node guarding RTRs.
Definition: nmt.c:137
co_nmt_destroy
void co_nmt_destroy(co_nmt_t *nmt)
Destroys a CANopen NMT master/slave service.
Definition: nmt.c:1258
CO_NMT_EC_RESOLVED
@ CO_NMT_EC_RESOLVED
An NMT error control event was resolved.
Definition: nmt.h:82
co_nmt_create
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:1233
co_nmt_boot_req
int co_nmt_boot_req(co_nmt_t *nmt, co_unsigned8_t id, int timeout)
Requests the NMT 'boot slave' process for the specified node.
Definition: nmt.c:1871
set_errnum
void set_errnum(errnum_t errnum)
Sets the current (thread-specific) platform-independent error number to errnum.
Definition: errnum.h:424
co_nmt_start_on_enter
static co_nmt_state_t * co_nmt_start_on_enter(co_nmt_t *nmt)
The entry function of the 'operational' state.
Definition: nmt.c:3448
co_nmt_get_net
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:1267
co_nmt_cfg_cfg_req
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 'configuration request'.
Definition: nmt_cfg.c:435
set_errc
void set_errc(int errc)
Sets the current (thread-specific) native error code to errc.
Definition: errnum.c:944
co_nmt_set_up_ind
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:1579
co_nmt_slave::boot
co_nmt_boot_t * boot
A pointer to the NMT 'boot slave' service.
Definition: nmt.c:121
__co_nmt::lss_req
co_nmt_lss_req_t * lss_req
A pointer to the LSS request function.
Definition: nmt.c:240
co_nmt_slave::cfg
co_nmt_cfg_t * cfg
A pointer to the NMT 'update configuration' service.
Definition: nmt.c:125
co_nmt_cs_ind
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:2098
co_nmt_stop_state
static co_nmt_state_t *const co_nmt_stop_state
The NMT 'stopped' state.
Definition: nmt.c:652
default_st_ind
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:3094
__co_nmt::cs_ind
co_nmt_cs_ind_t * cs_ind
A pointer to the NMT command indication function.
Definition: nmt.c:174
can_timer_destroy
void can_timer_destroy(can_timer_t *timer)
Destroys a CAN timer.
Definition: net.c:401
co_nmt_slaves_init
static void co_nmt_slaves_init(co_nmt_t *nmt)
Initializes NMT slave management.
Definition: nmt.c:3813
co_nmt_on_sync
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:1610
co_nmt_sync_ind_t
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
tpdo.h
co_sub_get_obj
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:551
CO_SDO_AC_NO_SUB
#define CO_SDO_AC_NO_SUB
SDO abort code: Sub-index does not exist.
Definition: sdo.h:132
nmt_hb.h
co_dev_set_sam_mpdo_event_ind
void co_dev_set_sam_mpdo_event_ind(co_dev_t *dev, co_dev_sam_mpdo_event_ind_t *ind, void *data)
Sets the indication function invoked by co_dev_sam_mpdo_event() when an event is indicated for (a sub...
Definition: dev.c:929
co_nmt_slave::booting
unsigned booting
A flag specifying whether the 'boot slave' process is in progress.
Definition: nmt.c:106
errnum2c
int errnum2c(errnum_t errnum)
Transforms a platform-independent error number to a native error code.
Definition: errnum.c:810
LONG_BIT
#define LONG_BIT
The number of bits in a long.
Definition: features.h:291
co_sdo_req
A CANopen SDO upload/download request.
Definition: sdo.h:181
co_nmt_dn_ind
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:3104
CO_NMT_ST_TOGGLE
#define CO_NMT_ST_TOGGLE
The mask to get/set the toggle bit from an NMT state.
Definition: nmt.h:73
__co_emcy
A CANopen EMCY producer/consumer service.
Definition: emcy.c:85
co_nmt_hb_fini
static void co_nmt_hb_fini(co_nmt_t *nmt)
Finalizes the heartbeat consumer services.
Definition: nmt.c:3773
CO_NUM_PDOS
#define CO_NUM_PDOS
The maximum number of Receive/Transmit-PDOs.
Definition: pdo.h:28
CO_NMT_ST_BOOTUP
#define CO_NMT_ST_BOOTUP
The NMT state 'boot-up'.
Definition: nmt.h:55
co_nmt_slave::assignment
co_unsigned32_t assignment
The NMT slave assignment (object 1F81).
Definition: nmt.c:97
co_nmt_on_hb
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....
Definition: nmt.c:1401
co_nmt_cs_req
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:1806
co_obj_find_sub
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:240
__co_lss
A CANopen LSS master/slave service.
Definition: lss.c:44
co_nmt_is_booting
int co_nmt_is_booting(const co_nmt_t *nmt, co_unsigned8_t id)
Returns 1 if the NMT 'boot slave' process is currently running for the specified node,...
Definition: nmt.c:1929
CO_NMT_CS_ENTER_PREOP
#define CO_NMT_CS_ENTER_PREOP
The NMT command specifier 'enter pre-operational'.
Definition: nmt.h:46
co_nmt_on_lg
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....
Definition: nmt.c:1362
co_nmt_srv
A CANopen NMT service manager.
Definition: nmt_srv.h:30
__co_nmt_state::on_boot
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 'boot slave' process completes.
Definition: nmt.c:510
ERRNUM_INVAL
@ ERRNUM_INVAL
Invalid argument.
Definition: errnum.h:132
co_nmt_up_ind
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:3116
nmt_cfg.h
__co_nmt::recv_700
can_recv_t * recv_700
A pointer to the CAN frame receiver for NMT error control messages.
Definition: nmt.c:178
can_buf_init
LELY_CAN_BUF_INLINE void can_buf_init(struct can_buf *buf, struct can_msg *ptr, size_t size)
Initializes a CAN frame buffer.
Definition: buf.h:165
co_nmt_slave::est
co_unsigned8_t est
The expected state of the slave (excluding the toggle bit).
Definition: nmt.c:99
__co_nmt::boot_ind
co_nmt_boot_ind_t * boot_ind
A pointer to the NMT 'boot slave' indication function.
Definition: nmt.c:258
co_nmt_slave::ltf
co_unsigned8_t ltf
The lifetime factor.
Definition: nmt.c:135
co_nmt_slave
A struct containing the state of an NMT slave.
Definition: nmt.c:84
__co_nmt_state
A CANopen NMT state.
Definition: nmt.c:482
co_nmt_slave::cfg_con
co_nmt_cfg_con_t * cfg_con
A pointer to the NMT 'configuration request' confirmation function.
Definition: nmt.c:127
__co_nmt::gt
co_unsigned16_t gt
The guard time (in milliseconds).
Definition: nmt.c:193
co_nmt_get_boot_ind
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 'boot slave' process completes.
Definition: nmt.c:1502
co_nmt_st_ind
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:3039
co_nmt_cfg_create
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 'configuration request'.
Definition: nmt_cfg.c:401
__co_nmt::ec_timer
can_timer_t * ec_timer
A pointer to the CAN timer for life guarding or heartbeat production.
Definition: nmt.c:188
co_tpdo_sync
int co_tpdo_sync(co_tpdo_t *pdo, co_unsigned8_t cnt)
Triggers the transmission of a synchronous PDO.
Definition: tpdo.c:565
__co_nmt
A CANopen NMT master/slave service.
Definition: nmt.c:148
co_nmt_srv::time
co_time_t * time
A pointer to the TIME producer/consumer service.
Definition: nmt_srv.h:67
__co_nmt::up_ind
co_nmt_sdo_ind_t * up_ind
A pointer to the SDO upload progress indication function.
Definition: nmt.c:273
co_nmt_hb_init
static void co_nmt_hb_init(co_nmt_t *nmt)
Initializes the heartbeat consumer services.
Definition: nmt.c:3726
__co_nmt::ng_ind
co_nmt_ng_ind_t * ng_ind
A pointer to the node guarding event indication function.
Definition: nmt.c:181
CO_SDO_AC_NO_DATA
#define CO_SDO_AC_NO_DATA
SDO abort code: No data available.
Definition: sdo.h:175
__co_nmt::st_ind
co_nmt_st_ind_t * st_ind
A pointer to the state change event indication function.
Definition: nmt.c:221
co_dev_set_tpdo_event_ind
void co_dev_set_tpdo_event_ind(co_dev_t *dev, co_dev_tpdo_event_ind_t *ind, void *data)
Sets the indication function invoked by co_dev_tpdo_event() when an event is indicated for (a sub-obj...
Definition: dev.c:857
co_nmt_startup_master
static co_nmt_state_t * co_nmt_startup_master(co_nmt_t *nmt)
The NMT master startup procedure.
Definition: nmt.c:3574
__co_nmt_hb
A CANopen NMT heartbeat consumer.
Definition: nmt_hb.c:33
nmt.h
CO_NMT_EC_OCCURRED
@ CO_NMT_EC_OCCURRED
An NMT error control event occurred.
Definition: nmt.h:80
co_nmt_slave::bootup
unsigned bootup
A flag specifying whether NMT boot-up message was received from a slave.
Definition: nmt.c:116
CO_SDO_AC_NO_WRITE
#define CO_SDO_AC_NO_WRITE
SDO abort code: Attempt to write a read only object.
Definition: sdo.h:90
co_emcy_remove
int co_emcy_remove(co_emcy_t *emcy, size_t n)
Pops a CANopen EMCY message from the stack, even if it is not the most recent message,...
Definition: emcy.c:551
co_nmt_srv_set
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:160
__co_nmt::inhibit
struct timespec inhibit
The time at which the next NMT message may be sent.
Definition: nmt.c:235
__co_nmt::state
co_nmt_state_t * state
The current state.
Definition: nmt.c:162
co_nmt_reset_comm_on_enter
static co_nmt_state_t * co_nmt_reset_comm_on_enter(co_nmt_t *nmt)
The entry function of the 'reset communication' state.
Definition: nmt.c:3259
co_nmt_slave::ng_state
int ng_state
Indicates whether a node guarding error occurred (CO_NMT_EC_OCCURRED or CO_NMT_EC_RESOLVED).
Definition: nmt.c:142
ERRNUM_INPROGRESS
@ ERRNUM_INPROGRESS
Operation in progress.
Definition: errnum.h:128
co_nmt_ec_init
static void co_nmt_ec_init(co_nmt_t *nmt)
Initializes the error control services.
Definition: nmt.c:3636
__co_nmt::startup
co_unsigned32_t startup
The NMT startup value (object 1F80).
Definition: nmt.c:166
co_nmt_get_csdo
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:2222
co_nmt_slave::timer
can_timer_t * timer
A pointer to the CAN timer for node guarding.
Definition: nmt.c:94
co_nmt_get_sync
co_sync_t * co_nmt_get_sync(const co_nmt_t *nmt)
Returns a pointer to the SYNC producer/consumer service.
Definition: nmt.c:2240
can_buf_write
LELY_CAN_BUF_INLINE 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:246
time.h
CO_NMT_CS_RESET_NODE
#define CO_NMT_CS_RESET_NODE
The NMT command specifier 'reset node'.
Definition: nmt.h:49
__co_nmt::dcf_comm
void * dcf_comm
The concise DCF of the communication parameters.
Definition: nmt.c:160
co_1f25_dn_ind
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:2607
co_nmt_set_cfg_ind
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 'configuration request' process is received.
Definition: nmt.c:1537
default_ng_ind
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:3065
co_nmt_get_tpdo
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:2193
co_nmt_slave::gt
co_unsigned16_t gt
The guard time (in milliseconds).
Definition: nmt.c:133
__co_nmt_boot
A CANopen NMT 'boot slave' service.
Definition: nmt_boot.c:79
co_tpdo_event
int co_tpdo_event(co_tpdo_t *pdo)
Triggers the transmission of an acyclic or event-driven PDO.
Definition: tpdo.c:509
co_val
A union of the CANopen static data types.
Definition: val.h:273
diag
void diag(enum diag_severity severity, int errc, const char *format,...)
Emits a diagnostic message.
Definition: diag.c:171
co_nmt_preop_state
static co_nmt_state_t *const co_nmt_preop_state
The NMT 'pre-operational' state.
Definition: nmt.c:608
co_nmt_on_tpdo_event_unlock
void co_nmt_on_tpdo_event_unlock(co_nmt_t *nmt)
Undoes the effect of a single call to co_nmt_on_tpdo_event_lock() and possibly triggers the transmiss...
Definition: nmt.c:1698
__co_nmt::boot_data
void * boot_data
A pointer to user-specified data for boot_ind.
Definition: nmt.c:260
co_nmt_chk_bootup
int co_nmt_chk_bootup(const co_nmt_t *nmt, co_unsigned8_t id)
Checks if a boot-up message has been received from the specified node(s).
Definition: nmt.c:1946
__co_nmt::up_data
void * up_data
A pointer to user-specified data for up_ind.
Definition: nmt.c:275
can_recv_start
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:587
co_sub_dn_ind_val
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:974
co_nmt_preop_on_cs
static co_nmt_state_t * co_nmt_preop_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
The 'NMT command received' transition function of the 'pre-operational' state.
Definition: nmt.c:3406
co_nmt_cfg_cfg_res
int co_nmt_cfg_cfg_res(co_nmt_cfg_t *cfg, co_unsigned32_t ac)
Indicates the result of the 'update configuration' step of an NMT 'configuration request'.
Definition: nmt_cfg.c:466
co_sdo_req_dn_val
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:170
co_nmt_set_sync_ind
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:1601
ERRNUM_PERM
@ ERRNUM_PERM
Operation not permitted.
Definition: errnum.h:208
__co_obj
A CANopen object.
Definition: obj.h:31
co_nmt_st_ind_t
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
co_nmt_set_boot_ind
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 'boot slave' process completes.
Definition: nmt.c:1513
co_sub_get_type
co_unsigned16_t co_sub_get_type(const co_sub_t *sub)
Returns the data type of a CANopen sub-object.
Definition: obj.c:603
CO_SDO_AC_PARAM_VAL
#define CO_SDO_AC_PARAM_VAL
SDO abort code: Invalid value for parameter (download only).
Definition: sdo.h:135
CAN_FLAG_RTR
@ CAN_FLAG_RTR
The Remote Transmission Request (RTR) flag (unavailable in CAN FD format frames).
Definition: msg.h:48
co_nmt_get_ssdo
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:2211
co_nmt_srv::rpdos
co_rpdo_t ** rpdos
An array of pointers to the Receive-PDO services.
Definition: nmt_srv.h:41
__co_dev
A CANopen device.
Definition: dev.h:30
co_dev_write_dcf
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,...
Definition: dev.c:768
can_net_send
int can_net_send(can_net_t *net, const struct can_msg *msg)
Sends a CAN frame from a network interface.
Definition: net.c:300
co_nmt_on_st
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:1454
co_nmt_ec_send_res
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:3713
co_nmt_ec_update
static void co_nmt_ec_update(co_nmt_t *nmt)
Updates and (de)activates the life guarding or heartbeat production services.
Definition: nmt.c:3674
LELY_CO_NMT_TIMEOUT
#define LELY_CO_NMT_TIMEOUT
The default SDO timeout (in milliseconds) for the NMT 'boot slave' and 'check configuration' processe...
Definition: nmt.h:33
co_nmt_get_hb_ind
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:1381
co_nmt_boot_destroy
void co_nmt_boot_destroy(co_nmt_boot_t *boot)
Destroys a CANopen NMT 'boot slave' service.
Definition: nmt_boot.c:910
co_nmt_slave::nmt
co_nmt_t * nmt
A pointer to the NMT master service.
Definition: nmt.c:86
co_emcy_push
int co_emcy_push(co_emcy_t *emcy, co_unsigned16_t eec, co_unsigned8_t er, const co_unsigned8_t msef[5])
Pushes a CANopen EMCY message to the stack and broadcasts it if the EMCY producer service is active.
Definition: emcy.c:470
co_sub_get_subidx
co_unsigned8_t co_sub_get_subidx(const co_sub_t *sub)
Returns the sub-index of a CANopen sub-object.
Definition: obj.c:559
__co_nmt::cs_timer
can_timer_t * cs_timer
A pointer to the CAN timer for sending buffered NMT messages.
Definition: nmt.c:237
__co_nmt::lg_ind
co_nmt_lg_ind_t * lg_ind
A pointer to the life guarding event indication function.
Definition: nmt.c:202
__co_csdo
A CANopen Client-SDO.
Definition: csdo.c:71
co_nmt_recv_000
static int co_nmt_recv_000(const struct can_msg *msg, void *data)
The CAN receive callback function for NMT messages.
Definition: nmt.c:2766
co_nmt_emit_boot
static void co_nmt_emit_boot(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es)
Invokes the 'boot slave completed' transition function of the current state of an NMT master service.
Definition: nmt.c:3183
co_nmt_get_ng_ind
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:1307
co_nmt_es2str
const char * co_nmt_es2str(char es)
Returns a pointer to a string describing an NMT boot error status.
Definition: nmt.c:786
co_nmt_srv::ncsdo
co_unsigned8_t ncsdo
The number of Client-SDO services.
Definition: nmt_srv.h:59
co_100c_dn_ind
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:2438
co_nmt_on_tpdo_event
void co_nmt_on_tpdo_event(co_nmt_t *nmt, co_unsigned16_t n)
Implements the default behavior when an event is indicated for an event-driven (asynchronous) Transmi...
Definition: nmt.c:1659
obj.h
co_nmt_sam_mpdo_event_ind
static void co_nmt_sam_mpdo_event_ind(co_unsigned16_t n, co_unsigned16_t idx, co_unsigned8_t subidx, void *data)
The SAM-MPDO event indication function.
Definition: nmt.c:3142
co_nmt_preop_on_boot
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 'boot slave completed' transition function of the 'pre-operational' state.
Definition: nmt.c:3421
CO_NMT_CS_CANID
#define CO_NMT_CS_CANID
The CAN identifier used for NMT commands.
Definition: nmt.h:37
co_nmt_default_on_boot
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 'boot slave completed' transition function.
Definition: nmt.c:3193
co_nmt_enter
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:3155
__co_nmt::master
int master
A flag specifying whether the NMT service is a master or a slave.
Definition: nmt.c:169
__co_nmt::tpdo_event_mask
unsigned long tpdo_event_mask[CO_NUM_PDOS/LONG_BIT]
A bit mask tracking all Transmit-PDO events indicated by co_nmt_on_tpdo_event() that have been postpo...
Definition: nmt.c:292
co_nmt_slave::rst
co_unsigned8_t rst
The received state of the slave (including the toggle bit).
Definition: nmt.c:101
CO_NMT_CS_RESET_COMM
#define CO_NMT_CS_RESET_COMM
The NMT command specifier 'reset communication'.
Definition: nmt.h:52
co_nmt_get_dev
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:1275
co_1f80_dn_ind
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:2679
co_nmt_set_hb_ind
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:1392
co_nmt_ng_req
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:2061
csdo.h
can_buf_peek
LELY_CAN_BUF_INLINE 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:204
CO_NMT_ST_START
#define CO_NMT_ST_START
The NMT state 'operational'.
Definition: nmt.h:61
co_1016_dn_ind
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:2508
co_nmt_srv::ssdos
co_ssdo_t ** ssdos
An array of pointers to the Server-SDO services.
Definition: nmt_srv.h:52
CO_NMT_SRV_LSS
#define CO_NMT_SRV_LSS
The LSS master/slave service.
Definition: nmt_srv.h:95
can_buf_fini
void can_buf_fini(struct can_buf *buf)
Finalizes a CAN frame buffer.
Definition: buf.c:41
stdlib.h
co_nmt_hb_set_1016
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:171
co_nmt_get_lg_ind
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:1342
co_nmt_reset_comm_on_cs
static co_nmt_state_t * co_nmt_reset_comm_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
The 'NMT command received' transition function of the 'reset communication' state.
Definition: nmt.c:3334
__co_nmt::lg_data
void * lg_data
A pointer to user-specified data for lg_ind.
Definition: nmt.c:204
__co_nmt::sync_data
void * sync_data
A pointer to user-specified data for sync_ind.
Definition: nmt.c:280
__co_nmt::sync_ind
co_nmt_sync_ind_t * sync_ind
A pointer to the SYNC indication function.
Definition: nmt.c:278
__co_sub
A CANopen sub-object.
Definition: obj.h:53
co_nmt_get_emcy
co_emcy_t * co_nmt_get_emcy(const co_nmt_t *nmt)
Returns a pointer to the EMCY producer/consumer service.
Definition: nmt.c:2268
co_nmt_set_st_ind
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:1445
co_dev_read_dcf
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,...
Definition: dev.c:707
CO_NMT_STOP_SRV
#define CO_NMT_STOP_SRV
The services enabled in the NMT 'stopped' state.
Definition: nmt.c:741
co_nmt_set_timeout
void co_nmt_set_timeout(co_nmt_t *nmt, int timeout)
Sets the default SDO timeout used during the NMT 'boot slave' and 'check configuration' processes.
Definition: nmt.c:1798
co_dev_find_sub
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
co_nmt_boot_con
void co_nmt_boot_con(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es)
The CANopen NMT 'boot slave' confirmation function, invoked when the 'boot slave' process completes.
Definition: nmt.c:2297
__co_nmt_state::on_leave
void(* on_leave)(co_nmt_t *nmt)
A pointer to the function invoked when the current state is left.
Definition: nmt.c:514
co_dev_cfg_hb
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:744
__co_nmt::dev
co_dev_t * dev
A pointer to a CANopen device.
Definition: nmt.c:152
co_nmt_start_on_cs
static co_nmt_state_t * co_nmt_start_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
The 'NMT command received' transition function of the 'operational' state.
Definition: nmt.c:3514
co_nmt_startup_slave
static co_nmt_state_t * co_nmt_startup_slave(co_nmt_t *nmt)
The NMT slave startup procedure.
Definition: nmt.c:3626
co_nmt_reset_node_on_enter
static co_nmt_state_t * co_nmt_reset_node_on_enter(co_nmt_t *nmt)
The entry function of the 'reset application' state.
Definition: nmt.c:3218
rpdo.h
co_dev_set_id
int co_dev_set_id(co_dev_t *dev, co_unsigned8_t id)
Sets the node-ID of a CANopen device.
Definition: dev.c:205
co_nmt_emit_cs
static void co_nmt_emit_cs(co_nmt_t *nmt, co_unsigned8_t cs)
Invokes the 'NMT command received' transition function of the current state of an NMT master/slave se...
Definition: nmt.c:3171
__co_nmt::ms
co_unsigned16_t ms
The producer heartbeat time (in milliseconds).
Definition: nmt.c:207
CO_NMT_EC_STATE
@ CO_NMT_EC_STATE
An NMT error control state change event.
Definition: nmt.h:89
__co_nmt::hb_data
void * hb_data
A pointer to user-specified data for hb_ind.
Definition: nmt.c:219
co_nmt_set_lg_ind
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:1353
co_nmt_bootup_state
static co_nmt_state_t *const co_nmt_bootup_state
The NMT 'boot-up' state.
Definition: nmt.c:575
__co_sync
A CANopen SYNC producer/consumer service.
Definition: sync.c:40
can_recv_stop
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:609
co_nmt_hb_destroy
void co_nmt_hb_destroy(co_nmt_hb_t *hb)
Destroys a CANopen NMT heartbeat consumer service.
Definition: nmt_hb.c:162
co_nmt_srv::ntpdo
co_unsigned16_t ntpdo
The number of Transmit-PDO services.
Definition: nmt_srv.h:49
co_csdo_get_num
co_unsigned8_t co_csdo_get_num(const co_csdo_t *sdo)
Returns the SDO number of a Client-SDO.
Definition: csdo.c:1118
timespec_add_usec
void timespec_add_usec(struct timespec *tp, uint_least64_t usec)
Adds usec microseconds to the time at tp.
Definition: time.h:149
__can_net
A CAN network interface.
Definition: net.c:37
co_obj_get_idx
co_unsigned16_t co_obj_get_idx(const co_obj_t *obj)
Returns the index of a CANopen object.
Definition: obj.c:164
nmt_srv.h
can_recv_create
can_recv_t * can_recv_create(void)
Creates a new CAN frame receiver.
Definition: net.c:533
can_timer_create
can_timer_t * can_timer_create(void)
Creates a new CAN timer.
Definition: net.c:376
co_nmt_slave::configuring
unsigned configuring
A flag specifying whether an NMT 'configuration request' is in progress.
Definition: nmt.c:113
can_timer_timeout
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:478
__co_nmt::st_data
void * st_data
A pointer to user-specified data for st_ind.
Definition: nmt.c:223
__co_nmt_cfg
A CANopen NMT 'configuration request' service.
Definition: nmt_cfg.c:56
CO_SDO_AC_PARAM
#define CO_SDO_AC_PARAM
SDO abort code: General parameter incompatibility reason.
Definition: sdo.h:105
can_msg::id
uint_least32_t id
The identifier (11 or 29 bits, depending on the CAN_FLAG_IDE flag).
Definition: msg.h:89
CO_SDO_AC_DATA_DEV
#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_type_is_array
int co_type_is_array(co_unsigned16_t type)
Returns 1 if the specified (static) data type is an array, and 0 if not.
Definition: type.c:40
co_nmt_comm_err_ind
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:2116
__co_nmt_state::on_cs
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:496
co_dev_find_obj
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
__co_nmt::cfg_data
void * cfg_data
A pointer to user-specified data for cfg_ind.
Definition: nmt.c:266
co_nmt_cfg_ind
void co_nmt_cfg_ind(co_nmt_t *nmt, co_unsigned8_t id, co_csdo_t *sdo)
The CANopen NMT 'update configuration' indication function, invoked when a configuration request is r...
Definition: nmt.c:2375
__co_nmt::lss_data
void * lss_data
A pointer to user-specified data for lss_req.
Definition: nmt.c:242
co_nmt_get_dn_ind
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:1548