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