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