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