Lely core libraries 2.3.4
nmt.c
Go to the documentation of this file.
1
24#include "co.h"
25#include <lely/util/diag.h>
26#if !LELY_NO_CO_MASTER
27#include <lely/can/buf.h>
28#include <lely/co/csdo.h>
29#include <lely/util/time.h>
30#endif
31#include <lely/co/dev.h>
32#if !LELY_NO_CO_EMCY
33#include <lely/co/emcy.h>
34#endif
35#include <lely/co/nmt.h>
36#include <lely/co/obj.h>
37#if !LELY_NO_CO_RPDO
38#include <lely/co/rpdo.h>
39#endif
40#include <lely/co/sdo.h>
41#if !LELY_NO_CO_TPDO
42#include <lely/co/tpdo.h>
43#endif
44#include <lely/co/val.h>
45#if !LELY_NO_CO_NMT_BOOT
46#include "nmt_boot.h"
47#endif
48#if !LELY_NO_CO_NMT_CFG
49#include "nmt_cfg.h"
50#endif
51#include "nmt_hb.h"
52#include "nmt_srv.h"
53
54#include <assert.h>
55#include <stdlib.h>
56#if LELY_NO_MALLOC
57#include <string.h>
58#endif
59
60#if LELY_NO_MALLOC
61#ifndef CO_NMT_CAN_BUF_SIZE
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
235 struct timespec inhibit;
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 } else if (state == CO_NMT_EC_RESOLVED) {
1369 if (nmt->srv.emcy) {
1370 // Remove the EMCY message from the stack.
1371 ssize_t n = co_emcy_find(nmt->srv.emcy, 0x8130);
1372 if (n >= 0)
1374 }
1375 }
1376}
1377
1378#endif // !LELY_NO_CO_MASTER
1379
1380void
1381co_nmt_get_hb_ind(const co_nmt_t *nmt, co_nmt_hb_ind_t **pind, void **pdata)
1382{
1383 assert(nmt);
1384
1385 if (pind)
1386 *pind = nmt->hb_ind;
1387 if (pdata)
1388 *pdata = nmt->hb_data;
1389}
1390
1391void
1393{
1394 assert(nmt);
1395
1396 nmt->hb_ind = ind ? ind : &default_hb_ind;
1397 nmt->hb_data = ind ? data : NULL;
1398}
1399
1400void
1401co_nmt_on_hb(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason)
1402{
1403 assert(nmt);
1404
1405 if (!id || id > CO_NUM_NODES)
1406 return;
1407
1408 if (reason != CO_NMT_EC_TIMEOUT)
1409 return;
1410
1411 if (state == CO_NMT_EC_OCCURRED) {
1412#if !LELY_NO_CO_MASTER
1413 if (co_nmt_is_master(nmt)) {
1415 return;
1416 }
1417#endif
1418 co_nmt_on_err(nmt, 0x8130, 0x10, NULL);
1419 } else if (state == CO_NMT_EC_RESOLVED) {
1420#if !LELY_NO_CO_MASTER
1421 if (co_nmt_is_master(nmt))
1422 return;
1423#endif
1424 if (nmt->srv.emcy) {
1425 // Remove the EMCY message from the stack.
1426 ssize_t n = co_emcy_find(nmt->srv.emcy, 0x8130);
1427 if (n >= 0)
1429 }
1430 }
1431}
1432
1433void
1434co_nmt_get_st_ind(const co_nmt_t *nmt, co_nmt_st_ind_t **pind, void **pdata)
1435{
1436 assert(nmt);
1437
1438 if (pind)
1439 *pind = nmt->st_ind;
1440 if (pdata)
1441 *pdata = nmt->st_data;
1442}
1443
1444void
1446{
1447 assert(nmt);
1448
1449 nmt->st_ind = ind ? ind : &default_st_ind;
1450 nmt->st_data = ind ? data : NULL;
1451}
1452
1453void
1454co_nmt_on_st(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st)
1455{
1456 assert(nmt);
1457
1458 if (!id || id > CO_NUM_NODES)
1459 return;
1460
1461#if LELY_NO_CO_NMT_BOOT
1462 (void)nmt;
1463 (void)st;
1464#else
1465 if (co_nmt_is_master(nmt) && st == CO_NMT_ST_BOOTUP) {
1466 int errc = get_errc();
1468 set_errc(errc);
1469 }
1470#endif
1471}
1472
1473#if !LELY_NO_CO_MASTER
1474
1475#if !LELY_NO_CO_LSS
1476
1477void
1479{
1480 assert(nmt);
1481
1482 if (pind)
1483 *pind = nmt->lss_req;
1484 if (pdata)
1485 *pdata = nmt->lss_data;
1486}
1487
1488void
1490{
1491 assert(nmt);
1492
1493 nmt->lss_req = ind;
1494 nmt->lss_data = data;
1495}
1496
1497#endif
1498
1499#if !LELY_NO_CO_NMT_BOOT
1500
1501void
1503{
1504 assert(nmt);
1505
1506 if (pind)
1507 *pind = nmt->boot_ind;
1508 if (pdata)
1509 *pdata = nmt->boot_data;
1510}
1511
1512void
1514{
1515 assert(nmt);
1516
1517 nmt->boot_ind = ind;
1518 nmt->boot_data = data;
1519}
1520
1521#endif // !LELY_NO_CO_NMT_BOOT
1522
1523#if !LELY_NO_CO_NMT_CFG
1524
1525void
1527{
1528 assert(nmt);
1529
1530 if (pind)
1531 *pind = nmt->cfg_ind;
1532 if (pdata)
1533 *pdata = nmt->cfg_data;
1534}
1535
1536void
1538{
1539 assert(nmt);
1540
1541 nmt->cfg_ind = ind;
1542 nmt->cfg_data = data;
1543}
1544
1545#endif // !LELY_NO_CO_NMT_BOOT
1546
1547void
1548co_nmt_get_dn_ind(const co_nmt_t *nmt, co_nmt_sdo_ind_t **pind, void **pdata)
1549{
1550 assert(nmt);
1551
1552 if (pind)
1553 *pind = nmt->dn_ind;
1554 if (pdata)
1555 *pdata = nmt->dn_data;
1556}
1557
1558void
1560{
1561 assert(nmt);
1562
1563 nmt->dn_ind = ind;
1564 nmt->dn_data = data;
1565}
1566
1567void
1568co_nmt_get_up_ind(const co_nmt_t *nmt, co_nmt_sdo_ind_t **pind, void **pdata)
1569{
1570 assert(nmt);
1571
1572 if (pind)
1573 *pind = nmt->up_ind;
1574 if (pdata)
1575 *pdata = nmt->up_data;
1576}
1577
1578void
1580{
1581 assert(nmt);
1582
1583 nmt->up_ind = ind;
1584 nmt->up_data = data;
1585}
1586
1587#endif // !LELY_NO_CO_MASTER
1588
1589void
1591{
1592 assert(nmt);
1593
1594 if (pind)
1595 *pind = nmt->sync_ind;
1596 if (pdata)
1597 *pdata = nmt->sync_data;
1598}
1599
1600void
1602{
1603 assert(nmt);
1604
1605 nmt->sync_ind = ind;
1606 nmt->sync_data = data;
1607}
1608
1609void
1610co_nmt_on_sync(co_nmt_t *nmt, co_unsigned8_t cnt)
1611{
1612 assert(nmt);
1613
1614 // Handle TPDOs before RPDOs. This prevents a possible race condition if
1615 // the same object is mapped to both an RPDO and a TPDO. In accordance
1616 // with CiA 301 v4.2.0 we transmit the value from the previous
1617 // synchronous window before updating it with a received PDO.
1618#if !LELY_NO_CO_TPDO
1619 for (co_unsigned16_t i = 0; i < nmt->srv.ntpdo; i++) {
1620 if (nmt->srv.tpdos[i])
1621 co_tpdo_sync(nmt->srv.tpdos[i], cnt);
1622 }
1623#endif
1624#if !LELY_NO_CO_RPDO
1625 for (co_unsigned16_t i = 0; i < nmt->srv.nrpdo; i++) {
1626 if (nmt->srv.rpdos[i])
1627 co_rpdo_sync(nmt->srv.rpdos[i], cnt);
1628 }
1629#endif
1630
1631 if (nmt->sync_ind)
1632 nmt->sync_ind(nmt, cnt, nmt->sync_data);
1633}
1634
1635void
1636co_nmt_on_err(co_nmt_t *nmt, co_unsigned16_t eec, co_unsigned8_t er,
1637 const co_unsigned8_t msef[5])
1638{
1639 assert(nmt);
1640
1641 if (eec) {
1642#if LELY_NO_CO_EMCY
1643 (void)er;
1644 (void)msef;
1645#else
1646 if (nmt->srv.emcy)
1647 co_emcy_push(nmt->srv.emcy, eec, er, msef);
1648#endif
1649 // In case of a communication error (0x81xx), invoke the
1650 // behavior specified by 1029:01.
1651 if ((eec & 0xff00) == 0x8100)
1653 }
1654}
1655
1656#if !LELY_NO_CO_TPDO
1657
1658void
1660{
1661 assert(nmt);
1662 assert(nmt->srv.ntpdo <= CO_NUM_PDOS);
1663
1664 int errsv = get_errc();
1665 if (n) {
1666 co_tpdo_t *pdo = co_nmt_get_tpdo(nmt, n);
1667 if (pdo) {
1668 if (nmt->tpdo_event_wait)
1669 nmt->tpdo_event_mask[(n - 1) / LONG_BIT] |= 1ul
1670 << ((n - 1) % LONG_BIT);
1671 else
1672 co_tpdo_event(pdo);
1673 }
1674 } else {
1675 for (n = 1; n <= nmt->srv.ntpdo; n++) {
1676 co_tpdo_t *pdo = co_nmt_get_tpdo(nmt, n);
1677 if (!pdo)
1678 continue;
1679 if (nmt->tpdo_event_wait)
1680 nmt->tpdo_event_mask[(n - 1) / LONG_BIT] |= 1ul
1681 << ((n - 1) % LONG_BIT);
1682 else
1683 co_tpdo_event(pdo);
1684 }
1685 }
1686 set_errc(errsv);
1687}
1688
1689void
1691{
1692 assert(nmt);
1693
1695}
1696
1697void
1699{
1700 assert(nmt);
1701 assert(nmt->tpdo_event_wait);
1702
1703 if (--nmt->tpdo_event_wait)
1704 return;
1705
1706 // Issue an indication for every postponed Transmit-PDO event.
1707 int errsv = get_errc();
1708 for (int i = 0; i < CO_NUM_PDOS / LONG_BIT; i++) {
1709 if (nmt->tpdo_event_mask[i]) {
1710 co_unsigned16_t n = i * LONG_BIT + 1;
1711 for (int j = 0; j < LONG_BIT && n <= nmt->srv.ntpdo
1712 && nmt->tpdo_event_mask[i];
1713 j++, n++) {
1714 if (!(nmt->tpdo_event_mask[i] & (1ul << j)))
1715 continue;
1716 nmt->tpdo_event_mask[i] &= ~(1ul << j);
1717 co_tpdo_t *pdo = co_nmt_get_tpdo(nmt, n);
1718 if (pdo)
1719 co_tpdo_event(pdo);
1720 }
1721 nmt->tpdo_event_mask[i] = 0;
1722 }
1723 }
1724 set_errc(errsv);
1725}
1726
1727#if !LELY_NO_CO_MPDO
1728void
1729co_nmt_on_sam_mpdo_event(co_nmt_t *nmt, co_unsigned16_t n, co_unsigned16_t idx,
1730 co_unsigned8_t subidx)
1731{
1732 assert(nmt);
1733
1734 co_tpdo_t *pdo = co_nmt_get_tpdo(nmt, n);
1735 if (pdo)
1736 co_sam_mpdo_event(pdo, idx, subidx);
1737}
1738#endif
1739
1740#endif // !LELY_NO_CO_TPDO
1741
1742co_unsigned8_t
1744{
1745 assert(nmt);
1746
1747 return nmt->id;
1748}
1749
1750int
1751co_nmt_set_id(co_nmt_t *nmt, co_unsigned8_t id)
1752{
1753 assert(nmt);
1754
1755 if (!id || (id > CO_NUM_NODES && id != 0xff)) {
1757 return -1;
1758 }
1759
1760 nmt->id = id;
1761
1762 return 0;
1763}
1764
1765co_unsigned8_t
1767{
1768 assert(nmt);
1769
1770 return nmt->st & ~CO_NMT_ST_TOGGLE;
1771}
1772
1773int
1775{
1776#if LELY_NO_CO_MASTER
1777 (void)nmt;
1778
1779 return 0;
1780#else
1781 assert(nmt);
1782
1783 return nmt->master;
1784#endif
1785}
1786
1787#if !LELY_NO_CO_MASTER
1788
1789int
1791{
1792 assert(nmt);
1793
1794 return nmt->timeout;
1795}
1796
1797void
1799{
1800 assert(nmt);
1801
1802 nmt->timeout = timeout;
1803}
1804
1805int
1806co_nmt_cs_req(co_nmt_t *nmt, co_unsigned8_t cs, co_unsigned8_t id)
1807{
1808 assert(nmt);
1809
1810 if (!nmt->master) {
1812 return -1;
1813 }
1814
1815 switch (cs) {
1816 case CO_NMT_CS_START:
1817 case CO_NMT_CS_STOP:
1820 case CO_NMT_CS_RESET_COMM: break;
1821 default: set_errnum(ERRNUM_INVAL); return -1;
1822 }
1823
1824 if (id > CO_NUM_NODES) {
1826 return -1;
1827 }
1828
1829 if (id == co_dev_get_id(nmt->dev))
1830 return co_nmt_cs_ind(nmt, cs);
1831
1832 trace("NMT: sending command specifier %d to node %d", cs, id);
1833
1834 struct can_msg msg = CAN_MSG_INIT;
1835 msg.id = CO_NMT_CS_CANID;
1836 msg.len = 2;
1837 msg.data[0] = cs;
1838 msg.data[1] = id;
1839
1840 // Add the frame to the buffer.
1841 if (!can_buf_write(&nmt->buf, &msg, 1)) {
1842 if (!can_buf_reserve(&nmt->buf, 1))
1843 return -1;
1844 can_buf_write(&nmt->buf, &msg, 1);
1845 }
1846
1847 // Send the frame by triggering the inhibit timer.
1848 return co_nmt_cs_timer(NULL, nmt);
1849}
1850
1851#if !LELY_NO_CO_LSS
1852int
1854{
1855 assert(nmt);
1856
1857 if (!nmt->master || nmt->state != co_nmt_reset_comm_state) {
1859 return -1;
1860 }
1861
1863
1864 return 0;
1865}
1866#endif
1867
1868#if !LELY_NO_CO_NMT_BOOT
1869
1870int
1871co_nmt_boot_req(co_nmt_t *nmt, co_unsigned8_t id, int timeout)
1872{
1873 assert(nmt);
1874
1875 int errc = 0;
1876
1877 if (!nmt->master) {
1878 errc = errnum2c(ERRNUM_PERM);
1879 goto error_param;
1880 }
1881
1882 if (!id || id > CO_NUM_NODES || id == co_dev_get_id(nmt->dev)) {
1883 errc = errnum2c(ERRNUM_INVAL);
1884 goto error_param;
1885 }
1886 struct co_nmt_slave *slave = &nmt->slaves[id - 1];
1887
1888 if (slave->booting) {
1890 goto error_param;
1891 }
1892
1893 trace("NMT: booting slave %d", id);
1894
1895 // Disable the heartbeat consumer during the 'boot slave' process.
1896 co_nmt_hb_t *hb = co_nmt_hb_find(nmt, id, NULL);
1897 if (hb)
1898 co_nmt_hb_set_1016(hb, id, 0);
1899
1900 slave->booting = 1;
1901
1902 slave->boot = co_nmt_boot_create(nmt->net, nmt->dev, nmt);
1903 if (!slave->boot) {
1904 errc = get_errc();
1905 goto error_create_boot;
1906 }
1907
1908 // clang-format off
1909 if (co_nmt_boot_boot_req(slave->boot, id, timeout, &co_nmt_dn_ind,
1910 &co_nmt_up_ind, nmt) == -1) {
1911 // clang-format on
1912 errc = get_errc();
1913 goto error_boot_req;
1914 }
1915
1916 return 0;
1917
1918error_boot_req:
1919 co_nmt_boot_destroy(slave->boot);
1920 slave->boot = NULL;
1921error_create_boot:
1922 slave->booting = 0;
1923error_param:
1924 set_errc(errc);
1925 return -1;
1926}
1927
1928int
1929co_nmt_is_booting(const co_nmt_t *nmt, co_unsigned8_t id)
1930{
1931 assert(nmt);
1932
1933 if (!nmt->master)
1934 return 0;
1935
1936 if (!id || id > CO_NUM_NODES || id == co_dev_get_id(nmt->dev))
1937 return 0;
1938
1939 return !!nmt->slaves[id - 1].boot;
1940}
1941
1942#endif // !LELY_NO_CO_NMT_BOOT
1943
1944#if !LELY_NO_CO_MASTER
1945int
1946co_nmt_chk_bootup(const co_nmt_t *nmt, co_unsigned8_t id)
1947{
1948 assert(nmt);
1949
1950 if (!nmt->master) {
1952 return -1;
1953 }
1954
1955 if (id > CO_NUM_NODES) {
1957 return -1;
1958 }
1959
1960 if (id == co_dev_get_id(nmt->dev)) {
1961 switch (co_nmt_get_st(nmt)) {
1962 case CO_NMT_ST_STOP:
1963 case CO_NMT_ST_START:
1964 case CO_NMT_ST_PREOP: return 1;
1965 default: return 0;
1966 }
1967 }
1968
1969 if (id == 0)
1971 else
1972 return !!nmt->slaves[id - 1].bootup;
1973}
1974#endif
1975
1976#if !LELY_NO_CO_NMT_CFG
1977
1978int
1979co_nmt_cfg_req(co_nmt_t *nmt, co_unsigned8_t id, int timeout,
1980 co_nmt_cfg_con_t *con, void *data)
1981{
1982 assert(nmt);
1983
1984 int errc = 0;
1985
1986 if (!nmt->master) {
1987 errc = errnum2c(ERRNUM_PERM);
1988 goto error_param;
1989 }
1990
1991 if (!id || id > CO_NUM_NODES || id == co_dev_get_id(nmt->dev)) {
1992 errc = errnum2c(ERRNUM_INVAL);
1993 goto error_param;
1994 }
1995 struct co_nmt_slave *slave = &nmt->slaves[id - 1];
1996
1997 if (slave->configuring) {
1999 goto error_param;
2000 }
2001
2002 trace("NMT: starting update configuration process for node %d", id);
2003
2004 // Disable the heartbeat consumer during a configuration request.
2005 co_nmt_hb_t *hb = co_nmt_hb_find(nmt, id, NULL);
2006 if (hb)
2007 co_nmt_hb_set_1016(hb, id, 0);
2008
2009 slave->configuring = 1;
2010
2011 slave->cfg = co_nmt_cfg_create(nmt->net, nmt->dev, nmt);
2012 if (!slave->cfg) {
2013 errc = get_errc();
2014 goto error_create_cfg;
2015 }
2016 slave->cfg_con = con;
2017 slave->cfg_data = data;
2018
2019 // clang-format off
2020 if (co_nmt_cfg_cfg_req(slave->cfg, id, timeout, &co_nmt_dn_ind,
2021 &co_nmt_up_ind, nmt) == -1) {
2022 // clang-format on
2023 errc = get_errc();
2024 goto error_cfg_req;
2025 }
2026
2027 return 0;
2028
2029error_cfg_req:
2030 co_nmt_cfg_destroy(slave->cfg);
2031 slave->cfg = NULL;
2032error_create_cfg:
2033 slave->configuring = 0;
2034error_param:
2035 set_errc(errc);
2036 return -1;
2037}
2038
2039int
2040co_nmt_cfg_res(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned32_t ac)
2041{
2042 assert(nmt);
2043
2044 if (!nmt->master) {
2046 return -1;
2047 }
2048
2049 if (!id || id > CO_NUM_NODES || !nmt->slaves[id - 1].cfg) {
2051 return -1;
2052 }
2053
2054 return co_nmt_cfg_cfg_res(nmt->slaves[id - 1].cfg, ac);
2055}
2056
2057#endif // !LELY_NO_CO_NMT_CFG
2058
2059#if !LELY_NO_CO_NG
2060int
2061co_nmt_ng_req(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned16_t gt,
2062 co_unsigned8_t ltf)
2063{
2064 assert(nmt);
2065
2066 if (!nmt->master) {
2068 return -1;
2069 }
2070
2071 if (!id || id > CO_NUM_NODES || id == co_dev_get_id(nmt->dev)) {
2073 return -1;
2074 }
2075 struct co_nmt_slave *slave = &nmt->slaves[id - 1];
2076
2077 if (!gt || !ltf) {
2078 can_timer_stop(slave->timer);
2079
2080 slave->gt = 0;
2081 slave->ltf = 0;
2082 slave->rtr = 0;
2083 } else {
2084 slave->gt = gt;
2085 slave->ltf = ltf;
2086 slave->rtr = 0;
2087
2088 can_timer_timeout(slave->timer, nmt->net, slave->gt);
2089 }
2090
2091 return 0;
2092}
2093#endif // !LELY_NO_CO_NG
2094
2095#endif // !LELY_NO_CO_MASTER
2096
2097int
2098co_nmt_cs_ind(co_nmt_t *nmt, co_unsigned8_t cs)
2099{
2100 assert(nmt);
2101
2102 switch (cs) {
2103 case CO_NMT_CS_START:
2104 case CO_NMT_CS_STOP:
2108 trace("NMT: received command specifier %d", cs);
2109 co_nmt_emit_cs(nmt, cs);
2110 return 0;
2111 default: set_errnum(ERRNUM_INVAL); return -1;
2112 }
2113}
2114
2115void
2117{
2118 assert(nmt);
2119
2120 diag(DIAG_INFO, 0, "NMT: communication error indicated");
2121 switch (co_dev_get_val_u8(nmt->dev, 0x1029, 0x01)) {
2122 case 0:
2125 break;
2126 case 2: co_nmt_cs_ind(nmt, CO_NMT_CS_STOP); break;
2127 }
2128}
2129
2130#if !LELY_NO_CO_MASTER
2131int
2133{
2134 assert(nmt);
2135
2136 if (!nmt->master) {
2138 return -1;
2139 }
2140
2141 if (!id || id > CO_NUM_NODES) {
2143 return -1;
2144 }
2145
2146 co_unsigned32_t assignment = co_dev_get_val_u32(nmt->dev, 0x1f81, id);
2147 // Ignore the error event if the slave is no longer in the network list.
2148 if (!(assignment & 0x01))
2149 return 0;
2150 int mandatory = !!(assignment & 0x08);
2151
2152 diag(DIAG_INFO, 0, "NMT: error indicated for %s slave %d",
2153 mandatory ? "mandatory" : "optional", id);
2154
2155 if (mandatory && (nmt->startup & 0x40)) {
2156 // If the slave is mandatory and bit 6 of the NMT startup value
2157 // is set, stop all nodes, including the master.
2160 } else if (mandatory && (nmt->startup & 0x10)) {
2161 // If the slave is mandatory and bit 4 of the NMT startup value
2162 // is set, reset all nodes, including the master.
2165 } else {
2166 // If the slave is not mandatory, or bits 4 and 6 of the NMT
2167 // startup value are zero, reset the node individually.
2169 return 0;
2170 }
2171}
2172#endif
2173
2174co_rpdo_t *
2175co_nmt_get_rpdo(const co_nmt_t *nmt, co_unsigned16_t n)
2176{
2177#if LELY_NO_CO_RPDO
2178 (void)nmt;
2179 (void)n;
2180
2181 return NULL;
2182#else
2183 assert(nmt);
2184
2185 if (!n || n > nmt->srv.nrpdo)
2186 return NULL;
2187
2188 return nmt->srv.rpdos[n - 1];
2189#endif
2190}
2191
2192co_tpdo_t *
2193co_nmt_get_tpdo(const co_nmt_t *nmt, co_unsigned16_t n)
2194{
2195#if LELY_NO_CO_TPDO
2196 (void)nmt;
2197 (void)n;
2198
2199 return NULL;
2200#else
2201 assert(nmt);
2202
2203 if (!n || n > nmt->srv.ntpdo)
2204 return NULL;
2205
2206 return nmt->srv.tpdos[n - 1];
2207#endif
2208}
2209
2210co_ssdo_t *
2211co_nmt_get_ssdo(const co_nmt_t *nmt, co_unsigned8_t n)
2212{
2213 assert(nmt);
2214
2215 if (!n || n > nmt->srv.nssdo)
2216 return NULL;
2217
2218 return nmt->srv.ssdos[n - 1];
2219}
2220
2221co_csdo_t *
2222co_nmt_get_csdo(const co_nmt_t *nmt, co_unsigned8_t n)
2223{
2224#if LELY_NO_CO_CSDO
2225 (void)nmt;
2226 (void)n;
2227
2228 return NULL;
2229#else
2230 assert(nmt);
2231
2232 if (!n || n > nmt->srv.ncsdo)
2233 return NULL;
2234
2235 return nmt->srv.csdos[n - 1];
2236#endif
2237}
2238
2239co_sync_t *
2241{
2242#if LELY_NO_CO_SYNC
2243 (void)nmt;
2244
2245 return NULL;
2246#else
2247 assert(nmt);
2248
2249 return nmt->srv.sync;
2250#endif
2251}
2252
2253co_time_t *
2255{
2256#if LELY_NO_CO_TIME
2257 (void)nmt;
2258
2259 return NULL;
2260#else
2261 assert(nmt);
2262
2263 return nmt->srv.time;
2264#endif
2265}
2266
2267co_emcy_t *
2269{
2270#if LELY_NO_CO_EMCY
2271 (void)nmt;
2272
2273 return NULL;
2274#else
2275 assert(nmt);
2276
2277 return nmt->srv.emcy;
2278#endif
2279}
2280
2281co_lss_t *
2283{
2284#if LELY_NO_CO_LSS
2285 (void)nmt;
2286
2287 return NULL;
2288#else
2289 assert(nmt);
2290
2291 return nmt->srv.lss;
2292#endif
2293}
2294
2295#if !LELY_NO_CO_NMT_BOOT
2296void
2297co_nmt_boot_con(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es)
2298{
2299 assert(nmt);
2300 assert(nmt->master);
2301 assert(id && id <= CO_NUM_NODES);
2302
2303 // Update the NMT slave state, including the assignment, in case it
2304 // changed during the 'boot slave' procedure.
2305 struct co_nmt_slave *slave = &nmt->slaves[id - 1];
2306 slave->assignment = co_dev_get_val_u32(nmt->dev, 0x1f81, id);
2307 slave->est = st & ~CO_NMT_ST_TOGGLE;
2308 // If we did not (yet) receive a state but the error control service was
2309 // successfully started, assume the node is pre-operational.
2310 if (!slave->est && (!es || es == 'L'))
2311 slave->est = CO_NMT_ST_PREOP;
2312 slave->rst = st;
2313 slave->es = es;
2314 slave->booting = 0;
2315 slave->booted = 1;
2316 co_nmt_boot_destroy(slave->boot);
2317 slave->boot = NULL;
2318
2319 // Re-enable the heartbeat consumer for the node, if necessary.
2320 co_unsigned16_t ms = 0;
2321 co_nmt_hb_t *hb = co_nmt_hb_find(nmt, id, &ms);
2322 if (hb)
2323 co_nmt_hb_set_1016(hb, id, ms);
2324
2325 // Update object 1F82 (Request NMT) with the NMT state.
2326 co_sub_t *sub = co_dev_find_sub(nmt->dev, 0x1f82, id);
2327 if (sub)
2328 co_sub_set_val_u8(sub, st & ~CO_NMT_ST_TOGGLE);
2329
2330 // If the slave booted successfully and can be started by the NMT
2331 // service, and if the master is allowed to start the nodes (bit 3 of
2332 // the NMT startup value) and has to start the slaves individually (bit
2333 // 1) or is in the operational state, send the NMT 'start' command to
2334 // the slave.
2335 // clang-format off
2336 if (!es && (slave->assignment & 0x05) == 0x05 && !(nmt->startup & 0x08)
2337 && (!(nmt->startup & 0x02)
2339 // clang-format on
2341
2342 // If the error control service was successfully started, resume
2343 // heartbeat consumption or node guarding.
2344 if (!es || es == 'L') {
2345 if (hb) {
2346 co_nmt_hb_set_st(hb, st);
2347#if !LELY_NO_CO_NG
2348 // Disable node guarding.
2349 slave->assignment &= 0xff;
2350 } else {
2351 // Enable node guarding if the guard time and lifetime
2352 // factor are non-zero.
2353 co_unsigned16_t gt = (slave->assignment >> 16) & 0xffff;
2354 co_unsigned8_t ltf = (slave->assignment >> 8) & 0xff;
2355 if (co_nmt_ng_req(nmt, id, gt, ltf) == -1)
2357 "unable to guard node %02X",
2358 id);
2359#endif
2360 }
2361 }
2362
2363 trace("NMT: slave %d finished booting with error status %c", id,
2364 es ? es : '0');
2365 if (nmt->boot_ind)
2366 nmt->boot_ind(nmt, id, st, es, nmt->boot_data);
2367
2368 co_nmt_emit_boot(nmt, id, st, es);
2369}
2370#endif // !LELY_NO_CO_NMT_BOOT
2371
2372#if !LELY_NO_CO_NMT_CFG
2373
2374void
2375co_nmt_cfg_ind(co_nmt_t *nmt, co_unsigned8_t id, co_csdo_t *sdo)
2376{
2377 assert(nmt);
2378 assert(nmt->master);
2379 assert(id && id <= CO_NUM_NODES);
2380
2381 if (nmt->cfg_ind) {
2382 nmt->cfg_ind(nmt, id, sdo, nmt->cfg_data);
2383 } else {
2384 co_nmt_cfg_res(nmt, id, 0);
2385 }
2386}
2387
2388void
2389co_nmt_cfg_con(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned32_t ac)
2390{
2391 assert(nmt);
2392 assert(nmt->master);
2393 assert(id && id <= CO_NUM_NODES);
2394
2395 struct co_nmt_slave *slave = &nmt->slaves[id - 1];
2396 slave->configuring = 0;
2397 co_nmt_cfg_destroy(slave->cfg);
2398 slave->cfg = NULL;
2399
2400#if !LELY_NO_CO_NMT_BOOT
2401 // Re-enable the heartbeat consumer for the node, if necessary.
2402 if (!slave->booting) {
2403#endif
2404 co_unsigned16_t ms = 0;
2405 co_nmt_hb_t *hb = co_nmt_hb_find(nmt, id, &ms);
2406 if (hb)
2407 co_nmt_hb_set_1016(hb, id, ms);
2408#if !LELY_NO_CO_NMT_BOOT
2409 }
2410#endif
2411
2412 trace("NMT: update configuration process completed for slave %d", id);
2413 if (slave->cfg_con)
2414 slave->cfg_con(nmt, id, ac, slave->cfg_data);
2415}
2416
2417#endif // !LELY_NO_CO_NMT_BOOT
2418
2419void
2420co_nmt_hb_ind(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason,
2421 co_unsigned8_t st)
2422{
2423 assert(nmt);
2424 assert(nmt->hb_ind);
2425
2426 if (!id || id > CO_NUM_NODES)
2427 return;
2428
2429 nmt->hb_ind(nmt, id, state, reason, nmt->hb_data);
2430
2431 if (reason == CO_NMT_EC_STATE)
2432 co_nmt_st_ind(nmt, id, st);
2433}
2434
2435#if !LELY_NO_CO_NG
2436
2437static co_unsigned32_t
2438co_100c_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
2439{
2440 assert(sub);
2441 assert(co_obj_get_idx(co_sub_get_obj(sub)) == 0x100c);
2442 assert(req);
2443 co_nmt_t *nmt = data;
2444 assert(nmt);
2445
2446 co_unsigned16_t type = co_sub_get_type(sub);
2447 assert(!co_type_is_array(type));
2448
2449 union co_val val;
2450 co_unsigned32_t ac = 0;
2451 if (co_sdo_req_dn_val(req, type, &val, &ac) == -1)
2452 return ac;
2453
2454 if (co_sub_get_subidx(sub))
2455 return CO_SDO_AC_NO_SUB;
2456
2457 assert(type == CO_DEFTYPE_UNSIGNED16);
2458 co_unsigned16_t gt = val.u16;
2459 co_unsigned16_t gt_old = co_sub_get_val_u16(sub);
2460 if (gt == gt_old)
2461 return 0;
2462
2463 nmt->gt = gt;
2464
2465 co_sub_dn(sub, &val);
2466
2467 co_nmt_ec_update(nmt);
2468 return 0;
2469}
2470
2471static co_unsigned32_t
2472co_100d_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
2473{
2474 assert(sub);
2475 assert(co_obj_get_idx(co_sub_get_obj(sub)) == 0x100d);
2476 assert(req);
2477 co_nmt_t *nmt = data;
2478 assert(nmt);
2479
2480 co_unsigned16_t type = co_sub_get_type(sub);
2481 assert(!co_type_is_array(type));
2482
2483 union co_val val;
2484 co_unsigned32_t ac = 0;
2485 if (co_sdo_req_dn_val(req, type, &val, &ac) == -1)
2486 return ac;
2487
2488 if (co_sub_get_subidx(sub))
2489 return CO_SDO_AC_NO_SUB;
2490
2491 assert(type == CO_DEFTYPE_UNSIGNED8);
2492 co_unsigned8_t ltf = val.u8;
2493 co_unsigned8_t ltf_old = co_sub_get_val_u8(sub);
2494 if (ltf == ltf_old)
2495 return 0;
2496
2497 nmt->ltf = ltf;
2498
2499 co_sub_dn(sub, &val);
2500
2501 co_nmt_ec_update(nmt);
2502 return 0;
2503}
2504
2505#endif // !LELY_NO_CO_NG
2506
2507static co_unsigned32_t
2508co_1016_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
2509{
2510 assert(sub);
2511 assert(co_obj_get_idx(co_sub_get_obj(sub)) == 0x1016);
2512 assert(req);
2513 co_nmt_t *nmt = data;
2514 assert(nmt);
2515
2516 co_unsigned16_t type = co_sub_get_type(sub);
2517 assert(!co_type_is_array(type));
2518
2519 union co_val val;
2520 co_unsigned32_t ac = 0;
2521 if (co_sdo_req_dn_val(req, type, &val, &ac) == -1)
2522 return ac;
2523
2524 co_unsigned8_t subidx = co_sub_get_subidx(sub);
2525 if (!subidx)
2526 return CO_SDO_AC_NO_WRITE;
2527 if (subidx > nmt->nhb)
2528 return CO_SDO_AC_NO_SUB;
2529
2530 assert(type == CO_DEFTYPE_UNSIGNED32);
2531 if (val.u32 == co_sub_get_val_u32(sub))
2532 return 0;
2533
2534 co_unsigned8_t id = (val.u32 >> 16) & 0xff;
2535 co_unsigned16_t ms = val.u32 & 0xffff;
2536
2537 // If the heartbeat consumer is active (valid node-ID and non-zero
2538 // heartbeat time), check the other entries for duplicate node-IDs.
2539 co_obj_t *obj_1016 = co_dev_find_obj(nmt->dev, 0x1016);
2540 if (id && id <= CO_NUM_NODES && ms) {
2541 for (co_unsigned8_t i = 1; i <= CO_NUM_NODES; i++) {
2542 // Skip the current entry.
2543 if (i == subidx)
2544 continue;
2545 co_unsigned32_t val_i = co_obj_get_val_u32(obj_1016, i);
2546 co_unsigned8_t id_i = (val_i >> 16) & 0xff;
2547 co_unsigned16_t ms_i = val_i & 0xffff;
2548 // It's not allowed to have two active heartbeat
2549 // consumers with the same node-ID.
2550 if (id_i == id && ms_i)
2551 return CO_SDO_AC_PARAM;
2552 }
2553 // Disable heartbeat consumption for booting slaves or slaves
2554 // that are being configured.
2555#if !LELY_NO_CO_NMT_BOOT
2556 if (nmt->slaves[id - 1].boot)
2557 ms = 0;
2558#endif
2559#if !LELY_NO_CO_NMT_CFG
2560 if (nmt->slaves[id - 1].cfg)
2561 ms = 0;
2562#endif
2563 }
2564
2565 co_sub_dn(sub, &val);
2566
2567 co_nmt_hb_set_1016(nmt->hbs[subidx - 1], id, ms);
2568 return 0;
2569}
2570
2571static co_unsigned32_t
2572co_1017_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
2573{
2574 assert(sub);
2575 assert(co_obj_get_idx(co_sub_get_obj(sub)) == 0x1017);
2576 assert(req);
2577 co_nmt_t *nmt = data;
2578 assert(nmt);
2579
2580 co_unsigned16_t type = co_sub_get_type(sub);
2581 assert(!co_type_is_array(type));
2582
2583 union co_val val;
2584 co_unsigned32_t ac = 0;
2585 if (co_sdo_req_dn_val(req, type, &val, &ac) == -1)
2586 return ac;
2587
2588 if (co_sub_get_subidx(sub))
2589 return CO_SDO_AC_NO_SUB;
2590
2591 assert(type == CO_DEFTYPE_UNSIGNED16);
2592 co_unsigned16_t ms = val.u16;
2593 co_unsigned16_t ms_old = co_sub_get_val_u16(sub);
2594 if (ms == ms_old)
2595 return 0;
2596
2597 nmt->ms = ms;
2598
2599 co_sub_dn(sub, &val);
2600
2601 co_nmt_ec_update(nmt);
2602 return 0;
2603}
2604
2605#if !LELY_NO_CO_NMT_CFG
2606static co_unsigned32_t
2607co_1f25_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
2608{
2609 assert(sub);
2610 assert(co_obj_get_idx(co_sub_get_obj(sub)) == 0x1f25);
2611 assert(req);
2612 co_nmt_t *nmt = data;
2613 assert(nmt);
2614
2615 co_unsigned16_t type = co_sub_get_type(sub);
2616 assert(!co_type_is_array(type));
2617
2618 union co_val val;
2619 co_unsigned32_t ac = 0;
2620 if (co_sdo_req_dn_val(req, type, &val, &ac) == -1)
2621 return ac;
2622
2623 co_unsigned8_t subidx = co_sub_get_subidx(sub);
2624 if (!subidx)
2625 return CO_SDO_AC_NO_WRITE;
2626
2627 // Sub-index 80 indicates all nodes.
2628 co_unsigned8_t id = subidx == 0x80 ? 0 : subidx;
2629 // Abort with an error if the node-ID is unknown.
2630 if (id > CO_NUM_NODES
2631 // cppcheck-suppress knownConditionTrueFalse
2632 || (id && !(nmt->slaves[id - 1].assignment & 0x01)))
2633 return CO_SDO_AC_PARAM_VAL;
2634
2635 // Check if the value 'conf' was downloaded.
2636 if (!nmt->master || val.u32 != UINT32_C(0x666e6f63))
2637 return CO_SDO_AC_DATA_CTL;
2638
2639 // cppcheck-suppress knownConditionTrueFalse
2640 if (id) {
2641 // Check if the entry for this node is present in object 1F20
2642 // (Store DCF) or 1F22 (Concise DCF).
2643#if LELY_NO_CO_DCF
2644 if (!co_dev_get_val(nmt->dev, 0x1f22, id))
2645#else
2646 if (!co_dev_get_val(nmt->dev, 0x1f20, id)
2647 && !co_dev_get_val(nmt->dev, 0x1f22, id))
2648#endif
2649 return CO_SDO_AC_NO_DATA;
2650 // Abort if the slave is already being configured.
2651 if (nmt->slaves[id - 1].cfg)
2652 return CO_SDO_AC_DATA_DEV;
2653 co_nmt_cfg_req(nmt, id, nmt->timeout, NULL, NULL);
2654 } else {
2655 // Check if object 1F20 (Store DCF) or 1F22 (Concise DCF)
2656 // exists.
2657#if LELY_NO_CO_DCF
2658 if (!co_dev_find_obj(nmt->dev, 0x1f22))
2659#else
2660 if (!co_dev_find_obj(nmt->dev, 0x1f20)
2661 && !co_dev_find_obj(nmt->dev, 0x1f22))
2662#endif
2663 return CO_SDO_AC_NO_DATA;
2664 for (id = 1; id <= CO_NUM_NODES; id++) {
2665 struct co_nmt_slave *slave = &nmt->slaves[id - 1];
2666 // Skip slaves that are not in the network list or are
2667 // already being configured.
2668 if (!(slave->assignment & 0x01) || slave->configuring)
2669 continue;
2670 co_nmt_cfg_req(nmt, id, nmt->timeout, NULL, NULL);
2671 }
2672 }
2673
2674 return 0;
2675}
2676#endif // !LELY_NO_CO_NMT_CFG
2677
2678static co_unsigned32_t
2679co_1f80_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
2680{
2681 assert(sub);
2682 assert(co_obj_get_idx(co_sub_get_obj(sub)) == 0x1f80);
2683 assert(req);
2684 (void)data;
2685
2686 co_unsigned16_t type = co_sub_get_type(sub);
2687 assert(!co_type_is_array(type));
2688
2689 union co_val val;
2690 co_unsigned32_t ac = 0;
2691 if (co_sdo_req_dn_val(req, type, &val, &ac) == -1)
2692 return ac;
2693
2694 if (co_sub_get_subidx(sub))
2695 return CO_SDO_AC_NO_SUB;
2696
2697 assert(type == CO_DEFTYPE_UNSIGNED32);
2698 co_unsigned32_t startup = val.u32;
2699 co_unsigned32_t startup_old = co_sub_get_val_u32(sub);
2700 if (startup == startup_old)
2701 return 0;
2702
2703 // Only bits 0..4 and 6 are supported.
2704 if ((startup ^ startup_old) != 0x5f)
2705 return CO_SDO_AC_PARAM_VAL;
2706
2707 co_sub_dn(sub, &val);
2708
2709 return 0;
2710}
2711
2712#if !LELY_NO_CO_MASTER
2713static co_unsigned32_t
2714co_1f82_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
2715{
2716 assert(sub);
2717 assert(co_obj_get_idx(co_sub_get_obj(sub)) == 0x1f82);
2718 assert(req);
2719 co_nmt_t *nmt = data;
2720 assert(nmt);
2721
2722 co_unsigned16_t type = co_sub_get_type(sub);
2723 assert(!co_type_is_array(type));
2724
2725 union co_val val;
2726 co_unsigned32_t ac = 0;
2727 if (co_sdo_req_dn_val(req, type, &val, &ac) == -1)
2728 return ac;
2729
2730 co_unsigned8_t subidx = co_sub_get_subidx(sub);
2731 if (!subidx)
2732 return CO_SDO_AC_NO_WRITE;
2733
2734 // Sub-index 80 indicates all nodes.
2735 co_unsigned8_t id = subidx == 0x80 ? 0 : subidx;
2736 // Abort with an error if the node-ID is unknown.
2737 if (id > CO_NUM_NODES
2738 // cppcheck-suppress knownConditionTrueFalse
2739 || (id && !(nmt->slaves[id - 1].assignment & 0x01)))
2740 return CO_SDO_AC_PARAM_VAL;
2741
2742 if (!nmt->master)
2743 return CO_SDO_AC_DATA_CTL;
2744
2745 assert(type == CO_DEFTYPE_UNSIGNED8);
2746 switch (val.u8) {
2747 case CO_NMT_ST_STOP: co_nmt_cs_req(nmt, CO_NMT_CS_STOP, id); break;
2748 case CO_NMT_ST_START: co_nmt_cs_req(nmt, CO_NMT_CS_START, id); break;
2751 break;
2754 break;
2755 case CO_NMT_ST_PREOP:
2757 break;
2758 default: ac = CO_SDO_AC_PARAM_VAL; break;
2759 }
2760
2761 return 0;
2762}
2763#endif // !LELY_NO_CO_MASTER
2764
2765static int
2766co_nmt_recv_000(const struct can_msg *msg, void *data)
2767{
2768 assert(msg);
2769 co_nmt_t *nmt = data;
2770 assert(nmt);
2771
2772#if !LELY_NO_CO_MASTER
2773 // Ignore NMT commands if we're the master.
2774 if (nmt->master)
2775 return 0;
2776#endif
2777
2778 if (msg->len < 2)
2779 return 0;
2780 co_unsigned8_t cs = msg->data[0];
2781 co_unsigned8_t id = msg->data[1];
2782
2783 // Ignore NMT commands to other nodes.
2784 if (!id || id == co_dev_get_id(nmt->dev))
2785 co_nmt_emit_cs(nmt, cs);
2786
2787 return 0;
2788}
2789
2790static int
2791co_nmt_recv_700(const struct can_msg *msg, void *data)
2792{
2793 assert(msg);
2794 assert(msg->id > 0x700 && msg->id <= 0x77f);
2795#if LELY_NO_CO_MASTER && LELY_NO_CO_NG
2796 (void)data;
2797#else
2798 co_nmt_t *nmt = data;
2799 assert(nmt);
2800#endif
2801
2802 if (msg->flags & CAN_FLAG_RTR) {
2803#if !LELY_NO_CO_NG
2804 assert(nmt->gt && nmt->ltf);
2805 assert(nmt->lg_ind);
2806
2807 // Respond with the state and flip the toggle bit.
2808 co_nmt_ec_send_res(nmt, nmt->st);
2809 nmt->st ^= CO_NMT_ST_TOGGLE;
2810
2811 // Reset the life guarding timer.
2812 can_timer_timeout(nmt->ec_timer, nmt->net, nmt->gt * nmt->ltf);
2813
2814 if (nmt->lg_state == CO_NMT_EC_OCCURRED) {
2815 diag(DIAG_INFO, 0, "NMT: life guarding event resolved");
2816 // Notify the user of the resolution of a life guarding
2817 // error.
2819 nmt->lg_ind(nmt, nmt->lg_state, nmt->lg_data);
2820 }
2821#endif
2822#if !LELY_NO_CO_MASTER
2823 } else {
2824 assert(nmt->master);
2825#if !LELY_NO_CO_NG
2826 assert(nmt->ng_ind);
2827#endif
2828
2829 co_unsigned8_t id = (msg->id - 0x700) & 0x7f;
2830 if (!id)
2831 return 0;
2832 struct co_nmt_slave *slave = &nmt->slaves[id - 1];
2833
2834 if (msg->len < 1)
2835 return 0;
2836 co_unsigned8_t st = msg->data[0];
2837
2838 if (st == CO_NMT_ST_BOOTUP) {
2839 // The expected state after a boot-up event is
2840 // pre-operational.
2841 slave->est = CO_NMT_ST_PREOP;
2842 // Record the reception of the boot-up message.
2843 slave->bootup = 1;
2844
2845 // Inform the application of the boot-up event.
2846 co_nmt_st_ind(nmt, id, st);
2847 return 0;
2848 }
2849
2850 // Ignore messages from booting slaves or slaves that are being
2851 // configured.
2852#if !LELY_NO_CO_NMT_BOOT
2853 if (slave->booting)
2854 return 0;
2855#endif
2856#if !LELY_NO_CO_NMT_CFG
2857 if (slave->configuring)
2858 return 0;
2859#endif
2860
2861#if !LELY_NO_CO_NG
2862 // Ignore messages if node guarding is disabled.
2863 if (!slave->gt || !slave->ltf)
2864 return 0;
2865
2866 // Check the toggle bit and ignore the message if it does not
2867 // match.
2868 if (!((st ^ slave->rst) & CO_NMT_ST_TOGGLE))
2869 return 0;
2870 slave->rst ^= CO_NMT_ST_TOGGLE;
2871
2872 // Notify the application of the resolution of a node guarding
2873 // timeout.
2874 if (slave->rtr >= slave->ltf) {
2875 diag(DIAG_INFO, 0,
2876 "NMT: node guarding time out resolved for node %d",
2877 id);
2880 }
2881 slave->rtr = 0;
2882
2883 // Notify the application of the occurrence or resolution of an
2884 // unexpected state change.
2885 if (slave->est != (st & ~CO_NMT_ST_TOGGLE)
2886 && slave->ng_state == CO_NMT_EC_RESOLVED) {
2887 diag(DIAG_INFO, 0,
2888 "NMT: node guarding state change occurred for node %d",
2889 id);
2891 nmt->ng_ind(nmt, id, slave->ng_state, CO_NMT_EC_STATE,
2892 nmt->ng_data);
2893 } else if (slave->est == (st & ~CO_NMT_ST_TOGGLE)
2894 && slave->ng_state == CO_NMT_EC_OCCURRED) {
2895 diag(DIAG_INFO, 0,
2896 "NMT: node guarding state change resolved for node %d",
2897 id);
2899 nmt->ng_ind(nmt, id, slave->ng_state, CO_NMT_EC_STATE,
2900 nmt->ng_data);
2901 }
2902
2903 // Notify the application of the occurrence of a state change.
2904 if (st != slave->rst)
2905 co_nmt_st_ind(nmt, id, st);
2906#endif // !LELY_NO_CO_NG
2907#endif // !LELY_NO_CO_MASTER
2908 }
2909
2910 return 0;
2911}
2912
2913#if !LELY_NO_CO_MASTER && !LELY_NO_CO_NG
2914static int
2915co_nmt_ng_timer(const struct timespec *tp, void *data)
2916{
2917 (void)tp;
2918 struct co_nmt_slave *slave = data;
2919 assert(slave);
2920 assert(slave->gt && slave->ltf);
2921 co_nmt_t *nmt = slave->nmt;
2922 assert(nmt);
2923 assert(nmt->master);
2924 assert(nmt->ng_ind);
2925 co_unsigned8_t id = slave - nmt->slaves + 1;
2926 assert(id && id <= CO_NUM_NODES);
2927
2928 // Reset the timer for the next RTR.
2929 can_timer_timeout(slave->timer, nmt->net, slave->gt);
2930
2931#if !LELY_NO_CO_NMT_BOOT
2932 // Do not send node guarding RTRs to slaves that have not finished
2933 // booting.
2934 if (!slave->booted)
2935 return 0;
2936#endif
2937
2938 // Notify the application once of the occurrence of a node guarding
2939 // timeout.
2940 if (slave->rtr <= slave->ltf && ++slave->rtr == slave->ltf) {
2941 diag(DIAG_INFO, 0,
2942 "NMT: node guarding time out occurred for node %d",
2943 id);
2945 nmt->ng_data);
2946 return 0;
2947 }
2948
2949 struct can_msg msg = CAN_MSG_INIT;
2950 msg.id = CO_NMT_EC_CANID(id);
2951 msg.flags |= CAN_FLAG_RTR;
2952
2953 return can_net_send(nmt->net, &msg);
2954}
2955#endif // !LELY_NO_CO_MASTER && !LELY_NO_CO_NG
2956
2957static int
2958co_nmt_ec_timer(const struct timespec *tp, void *data)
2959{
2960 (void)tp;
2961 co_nmt_t *nmt = data;
2962 assert(nmt);
2963
2964 if (nmt->ms) {
2965 // Send the state of the NMT service (excluding the toggle bit).
2967#if !LELY_NO_CO_NG
2968 } else if (nmt->gt && nmt->ltf) {
2969 assert(nmt->lg_ind);
2970 // Notify the user of the occurrence of a life guarding error.
2971 diag(DIAG_INFO, 0, "NMT: life guarding event occurred");
2973 nmt->lg_ind(nmt, nmt->lg_state, nmt->lg_data);
2974#endif
2975 }
2976
2977 return 0;
2978}
2979
2980#if !LELY_NO_CO_MASTER
2981static int
2982co_nmt_cs_timer(const struct timespec *tp, void *data)
2983{
2984 (void)tp;
2985 co_nmt_t *nmt = data;
2986 assert(nmt);
2987 assert(nmt->master);
2988
2989 co_unsigned16_t inhibit = co_dev_get_val_u16(nmt->dev, 0x102a, 0x00);
2990
2992
2993 struct timespec now = { 0, 0 };
2994 can_net_get_time(nmt->net, &now);
2995
2996 struct can_msg msg;
2997 while (can_buf_peek(&nmt->buf, &msg, 1)) {
2998 assert(msg.id == CO_NMT_CS_CANID);
2999 assert(msg.len == 2);
3000 // Wait until the inhibit time has elapsed.
3001 if (inhibit && timespec_cmp(&now, &nmt->inhibit) < 0) {
3002 can_timer_start(nmt->cs_timer, nmt->net, &nmt->inhibit,
3003 NULL);
3004 return 0;
3005 }
3006 // Try to send the frame.
3007 if (can_net_send(nmt->net, &msg) == -1)
3008 return -1;
3009 can_buf_read(&nmt->buf, NULL, 1);
3010 // Update the expected state of the node(s).
3011 co_unsigned8_t st = 0;
3012 switch (msg.data[0]) {
3013 case CO_NMT_CS_START: st = CO_NMT_ST_START; break;
3014 case CO_NMT_CS_STOP: st = CO_NMT_ST_STOP; break;
3015 case CO_NMT_CS_ENTER_PREOP: st = CO_NMT_ST_PREOP; break;
3016 }
3017 co_unsigned8_t id = msg.data[1];
3018 assert(id <= CO_NUM_NODES);
3019 if (id) {
3020 if (nmt->slaves[id - 1].est)
3021 nmt->slaves[id - 1].est = st;
3022 } else {
3023 for (id = 1; id <= CO_NUM_NODES; id++) {
3024 if (nmt->slaves[id - 1].est)
3025 nmt->slaves[id - 1].est = st;
3026 }
3027 }
3028 // Update the inhibit time.
3029 can_net_get_time(nmt->net, &now);
3030 nmt->inhibit = now;
3031 timespec_add_usec(&nmt->inhibit, inhibit * 100);
3032 }
3033
3034 return 0;
3035}
3036#endif // !LELY_NO_CO_MASTER
3037
3038static void
3039co_nmt_st_ind(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st)
3040{
3041 assert(nmt);
3042 assert(nmt->st_ind);
3043
3044 if (!id || id > CO_NUM_NODES)
3045 return;
3046
3047#if !LELY_NO_CO_MASTER
3048 if (nmt->master) {
3049 nmt->slaves[id - 1].rst = st;
3050
3051 // Update object 1F82 (Request NMT) with the NMT state.
3052 co_sub_t *sub = co_dev_find_sub(nmt->dev, 0x1f82, id);
3053 if (sub)
3054 co_sub_set_val_u8(sub, st & ~CO_NMT_ST_TOGGLE);
3055 }
3056#endif
3057
3058 nmt->st_ind(nmt, id, st & ~CO_NMT_ST_TOGGLE, nmt->st_data);
3059}
3060
3061#if !LELY_NO_CO_NG
3062
3063#if !LELY_NO_CO_MASTER
3064static void
3065default_ng_ind(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason,
3066 void *data)
3067{
3068 (void)data;
3069
3070 co_nmt_on_ng(nmt, id, state, reason);
3071}
3072#endif
3073
3074static void
3075default_lg_ind(co_nmt_t *nmt, int state, void *data)
3076{
3077 (void)data;
3078
3079 co_nmt_on_lg(nmt, state);
3080}
3081
3082#endif // !LELY_NO_CO_NG
3083
3084static void
3085default_hb_ind(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason,
3086 void *data)
3087{
3088 (void)data;
3089
3090 co_nmt_on_hb(nmt, id, state, reason);
3091}
3092
3093static void
3094default_st_ind(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, void *data)
3095{
3096 (void)data;
3097
3098 co_nmt_on_st(nmt, id, st);
3099}
3100
3101#if !LELY_NO_CO_NMT_BOOT || !LELY_NO_CO_NMT_CFG
3102
3103static void
3104co_nmt_dn_ind(const co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx,
3105 size_t size, size_t nbyte, void *data)
3106{
3107 co_nmt_t *nmt = data;
3108 assert(nmt);
3109
3110 if (nmt->dn_ind)
3111 nmt->dn_ind(nmt, co_csdo_get_num(sdo), idx, subidx, size, nbyte,
3112 nmt->dn_data);
3113}
3114
3115static void
3116co_nmt_up_ind(const co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx,
3117 size_t size, size_t nbyte, void *data)
3118{
3119 co_nmt_t *nmt = data;
3120 assert(nmt);
3121
3122 if (nmt->up_ind)
3123 nmt->up_ind(nmt, co_csdo_get_num(sdo), idx, subidx, size, nbyte,
3124 nmt->up_data);
3125}
3126
3127#endif // !LELY_NO_CO_NMT_BOOT || !LELY_NO_CO_NMT_CFG
3128
3129#if !LELY_NO_CO_TPDO
3130
3131static void
3132co_nmt_tpdo_event_ind(co_unsigned16_t n, void *data)
3133{
3134 co_nmt_t *nmt = data;
3135 assert(nmt);
3136
3137 co_nmt_on_tpdo_event(nmt, n);
3138}
3139
3140#if !LELY_NO_CO_MPDO
3141static void
3142co_nmt_sam_mpdo_event_ind(co_unsigned16_t n, co_unsigned16_t idx,
3143 co_unsigned8_t subidx, void *data)
3144{
3145 co_nmt_t *nmt = data;
3146 assert(nmt);
3147
3148 co_nmt_on_sam_mpdo_event(nmt, n, idx, subidx);
3149}
3150#endif
3151
3152#endif // !LELY_NO_CO_TPDO
3153
3154static void
3156{
3157 assert(nmt);
3158
3159 while (next) {
3160 co_nmt_state_t *prev = nmt->state;
3161 nmt->state = next;
3162
3163 if (prev && prev->on_leave)
3164 prev->on_leave(nmt);
3165
3166 next = next->on_enter ? next->on_enter(nmt) : NULL;
3167 }
3168}
3169
3170static inline void
3171co_nmt_emit_cs(co_nmt_t *nmt, co_unsigned8_t cs)
3172{
3173 assert(nmt);
3174 assert(nmt->state);
3175 assert(nmt->state->on_cs);
3176
3177 co_nmt_enter(nmt, nmt->state->on_cs(nmt, cs));
3178}
3179
3180#if !LELY_NO_CO_NMT_BOOT
3181
3182static inline void
3183co_nmt_emit_boot(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es)
3184{
3185 assert(nmt);
3186 assert(nmt->state);
3187 assert(nmt->state->on_boot);
3188
3189 co_nmt_enter(nmt, nmt->state->on_boot(nmt, id, st, es));
3190}
3191
3192static co_nmt_state_t *
3194 co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es)
3195{
3196 (void)nmt;
3197 (void)id;
3198 (void)st;
3199 (void)es;
3200
3201 return NULL;
3202}
3203
3204#endif // !LELY_NO_CO_NMT_BOOT
3205
3206static co_nmt_state_t *
3207co_nmt_init_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
3208{
3209 (void)nmt;
3210
3211 switch (cs) {
3213 default: return NULL;
3214 }
3215}
3216
3217static co_nmt_state_t *
3219{
3220 assert(nmt);
3221
3222 diag(DIAG_INFO, 0, "NMT: entering reset application state");
3223
3224#if !LELY_NO_CO_MASTER
3225 // Disable NMT slave management.
3226 co_nmt_slaves_fini(nmt);
3227 nmt->halt = 0;
3228#endif
3229
3230 // Disable all services.
3231 co_nmt_srv_set(&nmt->srv, nmt, 0);
3232
3233 // Disable heartbeat consumption.
3234 co_nmt_hb_fini(nmt);
3235
3236 // Disable error control services.
3237 co_nmt_ec_fini(nmt);
3238
3239 // Stop receiving NMT commands.
3240 can_recv_stop(nmt->recv_000);
3241
3242#if !LELY_NO_CO_DCF_RESTORE
3243 // Reset application parameters.
3244 if (co_dev_read_dcf(nmt->dev, NULL, NULL, &nmt->dcf_node) == -1)
3246 "unable to reset application parameters");
3247#endif
3248
3249 nmt->st = CO_NMT_ST_RESET_NODE;
3250 co_nmt_st_ind(nmt, co_dev_get_id(nmt->dev), 0);
3251
3252 if (nmt->cs_ind)
3253 nmt->cs_ind(nmt, CO_NMT_CS_RESET_NODE, nmt->cs_data);
3254
3256}
3257
3258static co_nmt_state_t *
3260{
3261 assert(nmt);
3262
3263 diag(DIAG_INFO, 0, "NMT: entering reset communication state");
3264
3265#if !LELY_NO_CO_MASTER
3266 // Disable NMT slave management.
3267 co_nmt_slaves_fini(nmt);
3268 nmt->halt = 0;
3269#endif
3270
3271 // Disable all services.
3272 co_nmt_srv_set(&nmt->srv, nmt, 0);
3273
3274 // Disable heartbeat consumption.
3275 co_nmt_hb_fini(nmt);
3276
3277 // Disable error control services.
3278 co_nmt_ec_fini(nmt);
3279
3280 // Stop receiving NMT commands.
3281 can_recv_stop(nmt->recv_000);
3282
3283 // Reset communication parameters.
3284 if (co_dev_read_dcf(nmt->dev, NULL, NULL, &nmt->dcf_comm) == -1)
3286 "unable to reset communication parameters");
3287
3288 // Update the node-ID if necessary.
3289 if (nmt->id != co_dev_get_id(nmt->dev)) {
3290 co_dev_set_id(nmt->dev, nmt->id);
3292 if (co_dev_write_dcf(nmt->dev, 0x1000, 0x1fff, &nmt->dcf_comm)
3293 == -1)
3295 "unable to store communication parameters");
3296 }
3297
3298 // Load the NMT startup value.
3299 nmt->startup = co_dev_get_val_u32(nmt->dev, 0x1f80, 0x00);
3300#if !LELY_NO_CO_MASTER
3301 // Bit 0 of the NMT startup value determines whether we are a master or
3302 // a slave.
3303 nmt->master = !!(nmt->startup & 0x01);
3304#endif
3305 diag(DIAG_INFO, 0, "NMT: running as %s",
3306 co_nmt_is_master(nmt) ? "master" : "slave");
3307
3308 nmt->st = CO_NMT_ST_RESET_COMM;
3309 co_nmt_st_ind(nmt, co_dev_get_id(nmt->dev), 0);
3310
3311 // Start receiving NMT commands.
3312 if (!co_nmt_is_master(nmt))
3314
3315 // Enable LSS.
3316 co_nmt_srv_set(&nmt->srv, nmt, CO_NMT_SRV_LSS);
3317
3318 if (nmt->cs_ind)
3319 nmt->cs_ind(nmt, CO_NMT_CS_RESET_COMM, nmt->cs_data);
3320
3321#if !LELY_NO_CO_MASTER && !LELY_NO_CO_LSS
3322 // If LSS is required, invoked the user-defined callback function and
3323 // wait for the process to complete.
3324 if (nmt->master && nmt->lss_req) {
3325 nmt->lss_req(nmt, co_nmt_get_lss(nmt), nmt->lss_data);
3326 return NULL;
3327 }
3328#endif
3329
3330 return co_nmt_bootup_state;
3331}
3332
3333static co_nmt_state_t *
3334co_nmt_reset_comm_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
3335{
3336 (void)nmt;
3337
3338 switch (cs) {
3341 default: return NULL;
3342 }
3343}
3344
3345static co_nmt_state_t *
3347{
3348 assert(nmt);
3349
3350 // Don't enter the 'pre-operational' state if the node-ID is invalid.
3351 if (co_dev_get_id(nmt->dev) == 0xff) {
3352 diag(DIAG_INFO, 0, "NMT: unconfigured node-ID");
3353 return NULL;
3354 }
3355
3356 // Enable error control services.
3357 co_nmt_ec_init(nmt);
3358
3359 // Enable heartbeat consumption.
3360 co_nmt_hb_init(nmt);
3361
3362 // Send the boot-up signal to notify the master we exist.
3364
3365 return co_nmt_preop_state;
3366}
3367
3368static co_nmt_state_t *
3369co_nmt_bootup_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
3370{
3371 (void)nmt;
3372
3373 switch (cs) {
3376 default: return NULL;
3377 }
3378}
3379
3380static co_nmt_state_t *
3382{
3383 assert(nmt);
3384
3385 diag(DIAG_INFO, 0, "NMT: entering pre-operational state");
3386
3387#if !LELY_NO_CO_MASTER
3388 // Disable NMT slave management.
3389 co_nmt_slaves_fini(nmt);
3390 nmt->halt = 0;
3391#endif
3392
3393 // Enable all services except PDO.
3394 co_nmt_srv_set(&nmt->srv, nmt, CO_NMT_PREOP_SRV);
3395
3396 nmt->st = CO_NMT_ST_PREOP | (nmt->st & CO_NMT_ST_TOGGLE);
3397 co_nmt_st_ind(nmt, co_dev_get_id(nmt->dev), nmt->st);
3398
3399 if (nmt->cs_ind)
3400 nmt->cs_ind(nmt, CO_NMT_CS_ENTER_PREOP, nmt->cs_data);
3401
3402 return co_nmt_startup(nmt);
3403}
3404
3405static co_nmt_state_t *
3406co_nmt_preop_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
3407{
3408 (void)nmt;
3409
3410 switch (cs) {
3412 case CO_NMT_CS_STOP: return co_nmt_stop_state;
3415 default: return NULL;
3416 }
3417}
3418
3419#if !LELY_NO_CO_NMT_BOOT
3420static co_nmt_state_t *
3422 co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es)
3423{
3424 assert(nmt);
3425 assert(nmt->master);
3426 assert(id && id <= CO_NUM_NODES);
3427 (void)st;
3428
3429 // If the 'boot slave' process failed for a mandatory slave, halt the
3430 // network boot-up procedure.
3431 if ((nmt->slaves[id - 1].assignment & 0x09) == 0x09 && es && es != 'L')
3432 nmt->halt = 1;
3433
3434 // Wait for any mandatory slaves that have not yet finished booting.
3435 int wait = nmt->halt;
3436 for (co_unsigned8_t id = 1; !wait && id <= CO_NUM_NODES; id++)
3437 wait = (nmt->slaves[id - 1].assignment & 0x09) == 0x09
3438 && nmt->slaves[id - 1].boot;
3439 if (!wait) {
3440 trace("NMT: all mandatory slaves started successfully");
3441 return co_nmt_startup_slave(nmt);
3442 }
3443 return NULL;
3444}
3445#endif
3446
3447static co_nmt_state_t *
3449{
3450 assert(nmt);
3451
3452 diag(DIAG_INFO, 0, "NMT: entering operational state");
3453
3454#if !LELY_NO_CO_TPDO
3455 // Reset all Transmit-PDO events.
3456 for (int i = 0; i < CO_NUM_PDOS / LONG_BIT; i++)
3457 nmt->tpdo_event_mask[i] = 0;
3458#endif
3459
3460 // Enable all services.
3461 co_nmt_srv_set(&nmt->srv, nmt, CO_NMT_START_SRV);
3462
3463 nmt->st = CO_NMT_ST_START | (nmt->st & CO_NMT_ST_TOGGLE);
3464 co_nmt_st_ind(nmt, co_dev_get_id(nmt->dev), nmt->st);
3465
3466#if !LELY_NO_CO_NMT_BOOT
3467 // If we're the master and bit 3 of the NMT startup value is 0 and bit 1
3468 // is 1, send the NMT start remote node command to all nodes (see Fig. 2
3469 // in CiA 302-2 version 4.1.0).
3470 if (nmt->master && (nmt->startup & 0x0a) == 0x02) {
3471 // Check if all slaves booted successfully.
3472 int boot = 1;
3473 for (co_unsigned8_t id = 1; boot && id <= CO_NUM_NODES; id++) {
3474 struct co_nmt_slave *slave = &nmt->slaves[id - 1];
3475 // Skip those slaves that are not in the network list.
3476 if (!(slave->assignment & 0x01))
3477 continue;
3478 // Check if the slave finished booting successfully and
3479 // can be started by the master.
3480 boot = slave->booted && (!slave->es || slave->es == 'L')
3481 && !(slave->assignment & 0x04);
3482 }
3483 if (boot) {
3484 // Start all NMT slaves at once.
3486 } else {
3487 for (co_unsigned8_t id = 1; id <= CO_NUM_NODES; id++) {
3488 struct co_nmt_slave *slave =
3489 &nmt->slaves[id - 1];
3490 // Skip those slaves that are not in the network
3491 // list (bit 0), or that we are not allowed to
3492 // boot (bit 2).
3493 if ((slave->assignment & 0x05) != 0x05)
3494 continue;
3495 // Only start slaves that have finished booting
3496 // successfully and are not already (expected to
3497 // be) operational.
3498 if (slave->booted
3499 && (!slave->es || slave->es == 'L')
3500 && slave->est != CO_NMT_ST_START)
3502 }
3503 }
3504 }
3505#endif
3506
3507 if (nmt->cs_ind)
3509
3510 return NULL;
3511}
3512
3513static co_nmt_state_t *
3514co_nmt_start_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
3515{
3516 (void)nmt;
3517
3518 switch (cs) {
3519 case CO_NMT_CS_STOP: return co_nmt_stop_state;
3523 default: return NULL;
3524 }
3525}
3526
3527static co_nmt_state_t *
3529{
3530 assert(nmt);
3531
3532 diag(DIAG_INFO, 0, "NMT: entering stopped state");
3533
3534 // Disable all services (except LSS).
3536
3539
3540 if (nmt->cs_ind)
3542
3543 return NULL;
3544}
3545
3546static co_nmt_state_t *
3547co_nmt_stop_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
3548{
3549 (void)nmt;
3550
3551 switch (cs) {
3556 default: return NULL;
3557 }
3558}
3559
3560static co_nmt_state_t *
3562{
3563 assert(nmt);
3564
3565#if !LELY_NO_CO_MASTER
3566 if (nmt->master)
3567 return co_nmt_startup_master(nmt);
3568#endif
3569 return co_nmt_startup_slave(nmt);
3570}
3571
3572#if !LELY_NO_CO_MASTER
3573static co_nmt_state_t *
3575{
3576 assert(nmt);
3577 assert(nmt->master);
3578
3579 // Enable NMT slave management.
3581
3582#if LELY_NO_CO_NMT_BOOT
3583 // Send the NMT 'reset communication' command to all slaves.
3585
3586 return co_nmt_startup_slave(nmt);
3587#else
3588 // Check if any node has the keep-alive bit set.
3589 int keep = 0;
3590 for (co_unsigned8_t id = 1; !keep && id <= CO_NUM_NODES; id++)
3591 keep = (nmt->slaves[id - 1].assignment & 0x11) == 0x11;
3592
3593 // Send the NMT 'reset communication' command to all slaves with
3594 // the keep-alive bit _not_ set. This includes slaves which are not in
3595 // the network list.
3596 if (keep) {
3597 for (co_unsigned8_t id = 1; id <= CO_NUM_NODES; id++) {
3598 // Do not reset the master itself.
3599 if (id == co_dev_get_id(nmt->dev))
3600 continue;
3601 if ((nmt->slaves[id - 1].assignment & 0x11) != 0x11)
3603 }
3604 } else {
3606 }
3607
3608 // Start the 'boot slave' processes.
3609 switch (co_nmt_slaves_boot(nmt)) {
3610 case -1:
3611 // Halt the network boot-up procedure if the 'boot slave'
3612 // process failed for a mandatory slave.
3613 nmt->halt = 1;
3614 return NULL;
3615 case 0: return co_nmt_startup_slave(nmt);
3616 default:
3617 // Wait for all mandatory slaves to finish booting.
3618 trace("NMT: waiting for mandatory slaves to start");
3619 return NULL;
3620 }
3621#endif
3622}
3623#endif
3624
3625static co_nmt_state_t *
3627{
3628 assert(nmt);
3629
3630 // Enter the operational state automatically if bit 2 of the NMT startup
3631 // value is 0.
3632 return (nmt->startup & 0x04) ? NULL : co_nmt_start_state;
3633}
3634
3635static void
3637{
3638 assert(nmt);
3639
3640 // Enable life guarding or heartbeat production.
3641#if !LELY_NO_CO_NG
3642 nmt->gt = co_dev_get_val_u16(nmt->dev, 0x100c, 0x00);
3643 nmt->ltf = co_dev_get_val_u8(nmt->dev, 0x100d, 0x00);
3644#endif
3645 nmt->ms = co_dev_get_val_u16(nmt->dev, 0x1017, 0x00);
3646
3647#if !LELY_NO_CO_NG
3649#endif
3650
3652}
3653
3654static void
3656{
3657 assert(nmt);
3658
3659 // Disable life guarding and heartbeat production.
3660#if !LELY_NO_CO_NG
3661 nmt->gt = 0;
3662 nmt->ltf = 0;
3663#endif
3664 nmt->ms = 0;
3665
3666#if !LELY_NO_CO_NG
3668#endif
3669
3671}
3672
3673static void
3675{
3676 assert(nmt);
3677
3678#if !LELY_NO_CO_NG
3679 // Heartbeat production has precedence over life guarding.
3680 int lt = nmt->ms ? 0 : nmt->gt * nmt->ltf;
3681#if !LELY_NO_CO_MASTER
3682 // Disable life guarding for the master.
3683 if (nmt->master)
3684 lt = 0;
3685#endif
3686
3687 if (lt) {
3688 // Start the CAN frame receiver for node guarding RTRs.
3691 CAN_FLAG_RTR);
3692 } else {
3694 }
3695#endif
3696
3697 // Start the CAN timer for heartbeat production or life guarding, if
3698 // necessary.
3699#if LELY_NO_CO_NG
3700 int ms = nmt->ms;
3701#else
3702 int ms = nmt->ms ? nmt->ms : lt;
3703#endif
3704 if (ms) {
3705 struct timespec interval = { ms / 1000, (ms % 1000) * 1000000 };
3706 can_timer_start(nmt->ec_timer, nmt->net, NULL, &interval);
3707 } else {
3709 }
3710}
3711
3712static int
3713co_nmt_ec_send_res(co_nmt_t *nmt, co_unsigned8_t st)
3714{
3715 assert(nmt);
3716
3717 struct can_msg msg = CAN_MSG_INIT;
3718 msg.id = CO_NMT_EC_CANID(co_dev_get_id(nmt->dev));
3719 msg.len = 1;
3720 msg.data[0] = st;
3721
3722 return can_net_send(nmt->net, &msg);
3723}
3724
3725static void
3727{
3728 assert(nmt);
3729
3730 // Create and initialize the heartbeat consumers.
3731#if LELY_NO_MALLOC
3732 memset(nmt->hbs, 0, CO_NMT_MAX_NHB * sizeof(*nmt->hbs));
3733#else
3734 assert(!nmt->hbs);
3735#endif
3736 assert(!nmt->nhb);
3737 co_obj_t *obj_1016 = co_dev_find_obj(nmt->dev, 0x1016);
3738 if (obj_1016) {
3739 nmt->nhb = co_obj_get_val_u8(obj_1016, 0x00);
3740#if LELY_NO_MALLOC
3741 if (nmt->nhb > CO_NMT_MAX_NHB) {
3743#else // !LELY_NO_MALLOC
3744 nmt->hbs = calloc(nmt->nhb, sizeof(*nmt->hbs));
3745 if (!nmt->hbs && nmt->nhb) {
3746#if !LELY_NO_ERRNO
3747 set_errc(errno2c(errno));
3748#endif
3749#endif // !LELY_NO_MALLOC
3751 "unable to create heartbeat consumers");
3752 nmt->nhb = 0;
3753 }
3754 }
3755
3756 for (co_unsigned8_t i = 0; i < nmt->nhb; i++) {
3757 nmt->hbs[i] = co_nmt_hb_create(nmt->net, nmt);
3758 if (!nmt->hbs[i]) {
3760 "unable to create heartbeat consumer 0x%02X",
3761 (co_unsigned8_t)(i + 1));
3762 continue;
3763 }
3764
3765 co_unsigned32_t val = co_obj_get_val_u32(obj_1016, i + 1);
3766 co_unsigned8_t id = (val >> 16) & 0xff;
3767 co_unsigned16_t ms = val & 0xffff;
3768 co_nmt_hb_set_1016(nmt->hbs[i], id, ms);
3769 }
3770}
3771
3772static void
3774{
3775 assert(nmt);
3776
3777 // Destroy all heartbeat consumers.
3778 for (size_t i = 0; i < nmt->nhb; i++)
3779 co_nmt_hb_destroy(nmt->hbs[i]);
3780#if !LELY_NO_MALLOC
3781 free(nmt->hbs);
3782 nmt->hbs = NULL;
3783#endif
3784 nmt->nhb = 0;
3785}
3786
3787#if !LELY_NO_CO_MASTER
3788
3789#if !LELY_NO_CO_NMT_BOOT || !LELY_NO_CO_NMT_CFG
3790static co_nmt_hb_t *
3791co_nmt_hb_find(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned16_t *pms)
3792{
3793 assert(nmt);
3794 assert(id && id <= CO_NUM_NODES);
3795
3796 const co_obj_t *obj_1016 = co_dev_find_obj(nmt->dev, 0x1016);
3797 if (!obj_1016)
3798 return NULL;
3799
3800 for (co_unsigned8_t i = 0; i < nmt->nhb; i++) {
3801 co_unsigned32_t val = co_obj_get_val_u32(obj_1016, i + 1);
3802 if (id == ((val >> 16) & 0xff)) {
3803 if (pms)
3804 *pms = val & 0xffff;
3805 return nmt->hbs[i];
3806 }
3807 }
3808 return NULL;
3809}
3810#endif
3811
3812static void
3814{
3815 assert(nmt);
3816 assert(nmt->master);
3817
3818 co_nmt_slaves_fini(nmt);
3819
3820 for (co_unsigned8_t id = 1; id <= CO_NUM_NODES; id++)
3821 // Start listening for boot-up notifications.
3822 can_recv_start(nmt->slaves[id - 1].recv, nmt->net,
3823 CO_NMT_EC_CANID(id), 0);
3824
3825 co_obj_t *obj_1f81 = co_dev_find_obj(nmt->dev, 0x1f81);
3826 if (!obj_1f81)
3827 return;
3828
3829 co_unsigned8_t n = co_obj_get_val_u8(obj_1f81, 0x00);
3830 for (co_unsigned8_t i = 0; i < MIN(n, CO_NUM_NODES); i++)
3831 nmt->slaves[i].assignment = co_obj_get_val_u32(obj_1f81, i + 1);
3832}
3833
3834static void
3836{
3837 assert(nmt);
3838
3839 for (co_unsigned8_t id = 1; id <= CO_NUM_NODES; id++) {
3840 struct co_nmt_slave *slave = &nmt->slaves[id - 1];
3841
3842 can_recv_stop(slave->recv);
3843#if !LELY_NO_CO_NG
3844 can_timer_stop(slave->timer);
3845#endif
3846
3847 slave->assignment = 0;
3848 slave->est = 0;
3849 slave->rst = 0;
3850
3851#if !LELY_NO_CO_NMT_BOOT
3852 slave->es = 0;
3853
3854 slave->booting = 0;
3855 slave->booted = 0;
3856
3857 co_nmt_boot_destroy(slave->boot);
3858 slave->boot = NULL;
3859#endif
3860 slave->bootup = 0;
3861
3862#if !LELY_NO_CO_NMT_CFG
3863 slave->configuring = 0;
3864
3865 co_nmt_cfg_destroy(slave->cfg);
3866 slave->cfg = NULL;
3867 slave->cfg_con = NULL;
3868 slave->cfg_data = NULL;
3869#endif
3870
3871#if !LELY_NO_CO_NG
3872 slave->gt = 0;
3873 slave->ltf = 0;
3874 slave->rtr = 0;
3876#endif
3877 }
3878}
3879
3880#if !LELY_NO_CO_NMT_BOOT
3881static int
3883{
3884 assert(nmt);
3885 assert(nmt->master);
3886
3887 int res = 0;
3888 for (co_unsigned8_t id = 1; id <= CO_NUM_NODES; id++) {
3889 struct co_nmt_slave *slave = &nmt->slaves[id - 1];
3890 // Skip those slaves that are not in the network list (bit 0).
3891 if ((slave->assignment & 0x01) != 0x01)
3892 continue;
3893 int mandatory = !!(slave->assignment & 0x08);
3894 // Wait for all mandatory slaves to finish booting.
3895 if (!res && mandatory)
3896 res = 1;
3897 // Optional slaves with the keep-alive bit _not_ set are booted
3898 // when we receive their boot-up signal.
3899 if (!mandatory && !(slave->assignment & 0x10))
3900 continue;
3901 // Halt the network boot-up procedure if the 'boot slave'
3902 // process failed for a mandatory slave with the keep-alive bit
3903 // set.
3904 if (co_nmt_boot_req(nmt, id, nmt->timeout) == -1 && mandatory)
3905 res = -1;
3906 }
3907 return res;
3908}
3909#endif
3910
3911static int
3913{
3914 for (co_unsigned8_t node_id = 1; node_id <= CO_NUM_NODES; node_id++) {
3915 const struct co_nmt_slave *const slave =
3916 &nmt->slaves[node_id - 1];
3917 // Skip those slaves that are not in the network list (bit 0).
3918 if ((slave->assignment & 0x01) != 0x01)
3919 continue;
3920 // Skip non-mandatory slaves (bit 3).
3921 if ((slave->assignment & 0x08) != 0x08)
3922 continue;
3923 // Check if we have received a boot-up message from a slave.
3924 if (!slave->bootup)
3925 return 0;
3926 }
3927 return 1;
3928}
3929
3930#endif // !LELY_NO_CO_MASTER
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_remove(co_emcy_t *emcy, size_t n)
Pops a CANopen EMCY message from the stack, even if it is not the most recent message,...
Definition: emcy.c:551
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
ssize_t co_emcy_find(const co_emcy_t *emcy, co_unsigned16_t eec)
Finds a CANopen EMCY message in the stack.
Definition: emcy.c:582
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:2958
void co_nmt_set_sync_ind(co_nmt_t *nmt, co_nmt_sync_ind_t *ind, void *data)
Sets the indication function invoked by co_nmt_on_sync() after all PDOs have been transmitted/process...
Definition: nmt.c:1601
void co_nmt_get_sync_ind(const co_nmt_t *nmt, co_nmt_sync_ind_t **pind, void **pdata)
Retrieves the indication function invoked by co_nmt_on_sync() after all PDOs have been transmitted/pr...
Definition: nmt.c:1590
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:2098
void co_nmt_on_sync(co_nmt_t *nmt, co_unsigned8_t cnt)
Implements the default behavior after a SYNC object is received or transmitted.
Definition: nmt.c:1610
static void co_nmt_ec_fini(co_nmt_t *nmt)
Finalizes the error control services.
Definition: nmt.c:3655
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:1568
static co_unsigned32_t co_1f80_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
The download indication function for (all sub-objects of) CANopen object 1F80 (NMT startup).
Definition: nmt.c:2679
static co_unsigned32_t co_100d_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
The download indication function for CANopen object 100D (Life time factor).
Definition: nmt.c:2472
static co_nmt_state_t * co_nmt_start_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
The 'NMT command received' transition function of the 'operational' state.
Definition: nmt.c:3514
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:3104
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:3171
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:1537
void co_nmt_get_st_ind(const co_nmt_t *nmt, co_nmt_st_ind_t **pind, void **pdata)
Retrieves the indication function invoked when a state change is detected.
Definition: nmt.c:1434
#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:2915
static co_unsigned32_t co_1f25_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
The download indication function for (all sub-objects of) CANopen object 1F25 (Configuration request)...
Definition: nmt.c:2607
static void default_st_ind(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, void *data)
The default state change event handler.
Definition: nmt.c:3094
static co_nmt_state_t * co_nmt_bootup_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
The 'NMT command received' transition function of the 'boot-up' state.
Definition: nmt.c:3369
void co_nmt_get_lss_req(const co_nmt_t *nmt, co_nmt_lss_req_t **pind, void **pdata)
Retrieves the request function invoked to perform LSS when booting an NMT master.
Definition: nmt.c:1478
void co_nmt_cfg_ind(co_nmt_t *nmt, co_unsigned8_t id, co_csdo_t *sdo)
The CANopen NMT 'update configuration' indication function, invoked when a configuration request is r...
Definition: nmt.c:2375
#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:3259
void co_nmt_set_up_ind(co_nmt_t *nmt, co_nmt_sdo_ind_t *ind, void *data)
Sets the indication function used to notify the user of the progress of the current SDO upload reques...
Definition: nmt.c:1579
static void co_nmt_hb_fini(co_nmt_t *nmt)
Finalizes the heartbeat consumer services.
Definition: nmt.c:3773
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:2211
void co_nmt_on_st(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st)
Implements the default behavior when a state change is detected by the node guarding or heartbeat pro...
Definition: nmt.c:1454
static co_unsigned32_t co_1017_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
The download indication function for CANopen object 1017 (Producer heartbeat time).
Definition: nmt.c:2572
static co_nmt_state_t * co_nmt_preop_on_enter(co_nmt_t *nmt)
The entry function of the 'pre-operational' state.
Definition: nmt.c:3381
void co_nmt_get_dn_ind(const co_nmt_t *nmt, co_nmt_sdo_ind_t **pind, void **pdata)
Retrieves the indication function used to notify the user of the progress of the current SDO download...
Definition: nmt.c:1548
void co_nmt_set_hb_ind(co_nmt_t *nmt, co_nmt_hb_ind_t *ind, void *data)
Sets the indication function invoked when a heartbeat event occurs.
Definition: nmt.c:1392
co_rpdo_t * co_nmt_get_rpdo(const co_nmt_t *nmt, co_unsigned16_t n)
Returns a pointer to a Receive-PDO service.
Definition: nmt.c:2175
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:3193
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:1659
void co_nmt_get_boot_ind(const co_nmt_t *nmt, co_nmt_boot_ind_t **pind, void **pdata)
Retrieves the indication function invoked when a CANopen NMT 'boot slave' process completes.
Definition: nmt.c:1502
co_nmt_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:1489
int co_nmt_get_timeout(const co_nmt_t *nmt)
Returns the default SDO timeout used during the NMT 'boot slave' and 'check configuration' processes.
Definition: nmt.c:1790
static int co_nmt_slaves_boot(co_nmt_t *nmt)
Starts the NMT 'boot slave' processes.
Definition: nmt.c:3882
static co_nmt_hb_t * co_nmt_hb_find(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned16_t *pms)
Find the heartbeat consumer for the specified node.
Definition: nmt.c:3791
void co_nmt_on_tpdo_event_lock(co_nmt_t *nmt)
Postpones the transmission of PDOs triggered by co_nmt_on_tpdo_event() until a matching call to co_nm...
Definition: nmt.c:1690
static void co_nmt_sam_mpdo_event_ind(co_unsigned16_t n, co_unsigned16_t idx, co_unsigned8_t subidx, void *data)
The SAM-MPDO event indication function.
Definition: nmt.c:3142
static co_nmt_state_t * co_nmt_startup_slave(co_nmt_t *nmt)
The NMT slave startup procedure.
Definition: nmt.c:3626
void co_nmt_on_err(co_nmt_t *nmt, co_unsigned16_t eec, co_unsigned8_t er, const co_unsigned8_t msef[5])
Implements the default error handling behavior by generating an EMCY message with co_emcy_push() and ...
Definition: nmt.c:1636
int co_nmt_cfg_req(co_nmt_t *nmt, co_unsigned8_t id, int timeout, co_nmt_cfg_con_t *con, void *data)
Issues the NMT 'configuration request' for the specified node.
Definition: nmt.c:1979
static co_nmt_state_t * co_nmt_bootup_on_enter(co_nmt_t *nmt)
The entry function of the 'boot-up' state.
Definition: nmt.c:3346
co_lss_t * co_nmt_get_lss(const co_nmt_t *nmt)
Returns a pointer to the LSS master/slave service.
Definition: nmt.c:2282
void co_nmt_on_tpdo_event_unlock(co_nmt_t *nmt)
Undoes the effect of a single call to co_nmt_on_tpdo_event_lock() and possibly triggers the transmiss...
Definition: nmt.c:1698
co_unsigned8_t co_nmt_get_id(const co_nmt_t *nmt)
Returns the pending node-ID.
Definition: nmt.c:1743
static void co_nmt_slaves_init(co_nmt_t *nmt)
Initializes NMT slave management.
Definition: nmt.c:3813
void co_nmt_cfg_con(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned32_t ac)
The CANopen NMT 'configuration request' confirmation function, invoked when a configuration request c...
Definition: nmt.c:2389
static void co_nmt_up_ind(const co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx, size_t size, size_t nbyte, void *data)
The SDO upload progress indication function.
Definition: nmt.c:3116
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:1513
int co_nmt_is_booting(const co_nmt_t *nmt, co_unsigned8_t id)
Returns 1 if the NMT 'boot slave' process is currently running for the specified node,...
Definition: nmt.c:1929
static int co_nmt_recv_700(const struct can_msg *msg, void *data)
The CAN receive callback function for NMT error control (node guarding RTR) messages.
Definition: nmt.c:2791
static int co_nmt_ec_send_res(co_nmt_t *nmt, co_unsigned8_t st)
Sends an NMT error control response message.
Definition: nmt.c:3713
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:3448
void co_nmt_on_hb(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason)
Implements the default behavior when a heartbeat event occurs (see sections 7.2.8....
Definition: nmt.c:1401
co_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:3075
static void co_nmt_hb_init(co_nmt_t *nmt)
Initializes the heartbeat consumer services.
Definition: nmt.c:3726
void co_nmt_get_cfg_ind(const co_nmt_t *nmt, co_nmt_cfg_ind_t **pind, void **pdata)
Retrieves the indication function invoked when a CANopen NMT 'configuration request' is received.
Definition: nmt.c:1526
int co_nmt_boot_req(co_nmt_t *nmt, co_unsigned8_t id, int timeout)
Requests the NMT 'boot slave' process for the specified node.
Definition: nmt.c:1871
static int co_nmt_recv_000(const struct can_msg *msg, void *data)
The CAN receive callback function for NMT messages.
Definition: nmt.c:2766
static void co_nmt_tpdo_event_ind(co_unsigned16_t n, void *data)
The Transmit-PDO event indication function.
Definition: nmt.c:3132
#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:3561
static void co_nmt_emit_boot(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es)
Invokes the 'boot slave completed' transition function of the current state of an NMT master service.
Definition: nmt.c:3183
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:1766
int co_nmt_chk_bootup(const co_nmt_t *nmt, co_unsigned8_t id)
Checks if a boot-up message has been received from the specified node(s).
Definition: nmt.c:1946
static co_nmt_state_t * co_nmt_reset_node_on_enter(co_nmt_t *nmt)
The entry function of the 'reset application' state.
Definition: nmt.c:3218
int co_nmt_cfg_res(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned32_t ac)
Indicates the result of the 'update configuration' step of an NMT 'request configuration' request for...
Definition: nmt.c:2040
static int co_nmt_chk_bootup_slaves(const co_nmt_t *nmt)
Checks if boot-up messages have been received from all mandatory slaves.
Definition: nmt.c:3912
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:3207
static co_nmt_state_t * co_nmt_preop_on_boot(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es)
The 'boot slave completed' transition function of the 'pre-operational' state.
Definition: nmt.c:3421
static co_nmt_state_t * co_nmt_preop_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
The 'NMT command received' transition function of the 'pre-operational' state.
Definition: nmt.c:3406
void co_nmt_set_timeout(co_nmt_t *nmt, int timeout)
Sets the default SDO timeout used during the NMT 'boot slave' and 'check configuration' processes.
Definition: nmt.c:1798
static void default_ng_ind(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason, void *data)
The default node guarding event handler.
Definition: nmt.c:3065
static void co_nmt_ec_update(co_nmt_t *nmt)
Updates and (de)activates the life guarding or heartbeat production services.
Definition: nmt.c:3674
co_emcy_t * co_nmt_get_emcy(const co_nmt_t *nmt)
Returns a pointer to the EMCY producer/consumer service.
Definition: nmt.c:2268
int co_nmt_node_err_ind(co_nmt_t *nmt, co_unsigned8_t id)
Indicates the occurrence of an error event and triggers the error handling process (see Fig.
Definition: nmt.c:2132
static co_nmt_state_t * co_nmt_reset_comm_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
The 'NMT command received' transition function of the 'reset communication' state.
Definition: nmt.c:3334
int co_nmt_ng_req(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned16_t gt, co_unsigned8_t ltf)
Request the node guarding service for the specified node, even if it is not in the network list.
Definition: nmt.c:2061
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:3574
co_csdo_t * co_nmt_get_csdo(const co_nmt_t *nmt, co_unsigned8_t n)
Returns a pointer to a Client-SDO service.
Definition: nmt.c:2222
void co_nmt_set_dn_ind(co_nmt_t *nmt, co_nmt_sdo_ind_t *ind, void *data)
Sets the indication function used to notify the user of the progress of the current SDO download requ...
Definition: nmt.c:1559
void co_nmt_comm_err_ind(co_nmt_t *nmt)
Indicates the occurrence of a communication error and invokes the specified error behavior (object 10...
Definition: nmt.c:2116
int co_nmt_cs_req(co_nmt_t *nmt, co_unsigned8_t cs, co_unsigned8_t id)
Submits an NMT request to a slave.
Definition: nmt.c:1806
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:1751
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:1774
void co_nmt_get_hb_ind(const co_nmt_t *nmt, co_nmt_hb_ind_t **pind, void **pdata)
Retrieves the indication function invoked when a heartbeat event occurs.
Definition: nmt.c:1381
co_time_t * co_nmt_get_time(const co_nmt_t *nmt)
Returns a pointer to the TIME producer/consumer service.
Definition: nmt.c:2254
static co_nmt_state_t * co_nmt_stop_on_cs(co_nmt_t *nmt, co_unsigned8_t cs)
The 'NMT command received' transition function of the 'stopped' state.
Definition: nmt.c:3547
co_tpdo_t * co_nmt_get_tpdo(const co_nmt_t *nmt, co_unsigned16_t n)
Returns a pointer to a Transmit-PDO service.
Definition: nmt.c:2193
static void co_nmt_st_ind(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st)
The indication function for state change events.
Definition: nmt.c:3039
static int co_nmt_cs_timer(const struct timespec *tp, void *data)
The CAN timer callback function for sending buffered NMT messages.
Definition: nmt.c:2982
static void co_nmt_enter(co_nmt_t *nmt, co_nmt_state_t *next)
Enters the specified state of an NMT master/slave service and invokes the exit and entry functions.
Definition: nmt.c:3155
co_sync_t * co_nmt_get_sync(const co_nmt_t *nmt)
Returns a pointer to the SYNC producer/consumer service.
Definition: nmt.c:2240
void co_nmt_set_st_ind(co_nmt_t *nmt, co_nmt_st_ind_t *ind, void *data)
Sets the indication function invoked when a state change is detected.
Definition: nmt.c:1445
void co_nmt_boot_con(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es)
The CANopen NMT 'boot slave' confirmation function, invoked when the 'boot slave' process completes.
Definition: nmt.c:2297
int co_nmt_lss_con(co_nmt_t *nmt)
Confirms the completion of the process when booting an NMT master.
Definition: nmt.c:1853
static void co_nmt_ec_init(co_nmt_t *nmt)
Initializes the error control services.
Definition: nmt.c:3636
void co_nmt_hb_ind(co_nmt_t *nmt, co_unsigned8_t id, int state, int reason, co_unsigned8_t st)
The CANopen NMT heartbeat indication function, invoked when a heartbeat event occurs.
Definition: nmt.c:2420
static co_unsigned32_t co_100c_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
The download indication function for CANopen object 100C (Guard time).
Definition: nmt.c:2438
void co_nmt_on_sam_mpdo_event(co_nmt_t *nmt, co_unsigned16_t n, co_unsigned16_t idx, co_unsigned8_t subidx)
Implements the default behavior when an event is indicated for a source address mode multiplex PDO by...
Definition: nmt.c:1729
static co_unsigned32_t co_1016_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
The download indication function for CANopen object 1016 (Consumer heartbeat time).
Definition: nmt.c:2508
static co_unsigned32_t co_1f82_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
The download indication function for (all sub-objects of) CANopen object 1F80 (Request NMT).
Definition: nmt.c:2714
static void co_nmt_slaves_fini(co_nmt_t *nmt)
Finalizes NMT slave management.
Definition: nmt.c:3835
static co_nmt_state_t * co_nmt_stop_on_enter(co_nmt_t *nmt)
The entry function of the 'stopped' state.
Definition: nmt.c:3528
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:3085
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
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