Lely core libraries  1.9.2
nmt_boot.c
Go to the documentation of this file.
1 
24 #include "co.h"
25 
26 #ifndef LELY_NO_CO_MASTER
27 
28 #include "nmt_boot.h"
29 #include <lely/co/dev.h>
30 #include <lely/co/obj.h>
31 #include <lely/co/val.h>
32 #include <lely/util/diag.h>
33 #include <lely/util/time.h>
34 
35 #include <assert.h>
36 #include <inttypes.h>
37 #include <stdlib.h>
38 
39 #ifndef LELY_CO_NMT_BOOT_WAIT_TIMEOUT
40 #define LELY_CO_NMT_BOOT_WAIT_TIMEOUT 1000
42 #endif
43 
44 #ifndef LELY_CO_NMT_BOOT_SDO_RETRY
45 #define LELY_CO_NMT_BOOT_SDO_RETRY 3
47 #endif
48 
49 #ifndef LELY_CO_NMT_BOOT_RTR_TIMEOUT
50 #define LELY_CO_NMT_BOOT_RTR_TIMEOUT 100
52 #endif
53 
54 #ifndef LELY_CO_NMT_BOOT_CHECK_TIMEOUT
55 
59 #define LELY_CO_NMT_BOOT_CHECK_TIMEOUT 100
60 #endif
61 
62 struct __co_nmt_boot_state;
65 
67 struct __co_nmt_boot {
81  co_unsigned8_t id;
83  int timeout;
87  struct timespec start;
89  co_unsigned32_t assignment;
91  co_unsigned16_t ms;
93  struct co_sdo_req req;
95  int retry;
97  co_unsigned8_t st;
99  char es;
100 };
101 
107 static int co_nmt_boot_recv(const struct can_msg *msg, void *data);
108 
114 static int co_nmt_boot_timer(const struct timespec *tp, void *data);
115 
122 static void co_nmt_boot_dn_con(co_csdo_t *sdo, co_unsigned16_t idx,
123  co_unsigned8_t subidx, co_unsigned32_t ac, void *data);
124 
131 static void co_nmt_boot_up_con(co_csdo_t *sdo, co_unsigned16_t idx,
132  co_unsigned8_t subidx, co_unsigned32_t ac, const void *ptr,
133  size_t n, void *data);
134 
141 static void co_nmt_boot_cfg_con(co_nmt_t *nmt, co_unsigned8_t id,
142  co_unsigned32_t ac, void *data);
143 
148 static void co_nmt_boot_enter(co_nmt_boot_t *boot, co_nmt_boot_state_t *next);
149 
157 static inline void co_nmt_boot_emit_time(
158  co_nmt_boot_t *boot, const struct timespec *tp);
159 
167 static inline void co_nmt_boot_emit_recv(
168  co_nmt_boot_t *boot, const struct can_msg *msg);
169 
177 static inline void co_nmt_boot_emit_dn_con(
178  co_nmt_boot_t *boot, co_unsigned32_t ac);
179 
189 static inline void co_nmt_boot_emit_up_con(co_nmt_boot_t *boot,
190  co_unsigned32_t ac, const void *ptr, size_t n);
191 
199 static inline void co_nmt_boot_emit_cfg_con(
200  co_nmt_boot_t *boot, co_unsigned32_t ac);
201 
205  co_nmt_boot_state_t *(*on_enter)(co_nmt_boot_t *boot);
214  co_nmt_boot_state_t *(*on_time)(
215  co_nmt_boot_t *boot, const struct timespec *tp);
225  co_nmt_boot_state_t *(*on_recv)(
226  co_nmt_boot_t *boot, const struct can_msg *msg);
236  co_nmt_boot_state_t *(*on_dn_con)(
237  co_nmt_boot_t *boot, co_unsigned32_t ac);
249  co_nmt_boot_state_t *(*on_up_con)(co_nmt_boot_t *boot,
250  co_unsigned32_t ac, const void *ptr, size_t n);
260  co_nmt_boot_state_t *(*on_cfg_con)(
261  co_nmt_boot_t *boot, co_unsigned32_t ac);
263  void (*on_leave)(co_nmt_boot_t *boot);
264 };
265 
266 #define LELY_CO_DEFINE_STATE(name, ...) \
267  static co_nmt_boot_state_t *const name = \
268  &(co_nmt_boot_state_t){ __VA_ARGS__ };
269 
272  co_nmt_boot_t *boot, const struct timespec *tp);
273 
275 // clang-format off
276 LELY_CO_DEFINE_STATE(co_nmt_boot_wait_state,
277  .on_time = &co_nmt_boot_wait_on_time
278 )
279 // clang-format on
280 
283 
285 // clang-format off
286 LELY_CO_DEFINE_STATE(co_nmt_boot_abort_state,
287  .on_enter = &co_nmt_boot_abort_on_enter
288 )
289 // clang-format on
290 
293 
295 static void co_nmt_boot_error_on_leave(co_nmt_boot_t *boot);
296 
298 // clang-format off
299 LELY_CO_DEFINE_STATE(co_nmt_boot_error_state,
300  .on_enter = &co_nmt_boot_error_on_enter,
301  .on_leave = &co_nmt_boot_error_on_leave
302 )
303 // clang-format on
304 
307  co_nmt_boot_t *boot);
308 
314  co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr,
315  size_t n);
316 
318 // clang-format off
319 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_device_type_state,
322 )
323 // clang-format on
324 
327  co_nmt_boot_t *boot);
328 
334  co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr,
335  size_t n);
336 
338 // clang-format off
339 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_vendor_id_state,
342 )
343 // clang-format on
344 
347  co_nmt_boot_t *boot);
348 
354  co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr,
355  size_t n);
356 
358 // clang-format off
359 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_product_code_state,
362 )
363 // clang-format on
364 
367  co_nmt_boot_t *boot);
368 
374  co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr,
375  size_t n);
376 
378 // clang-format off
379 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_revision_state,
382 )
383 // clang-format on
384 
387  co_nmt_boot_t *boot);
388 
394  co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr,
395  size_t n);
396 
398 // clang-format off
399 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_serial_nr_state,
402 )
403 // clang-format on
404 
407 
410  co_nmt_boot_t *boot, const struct timespec *tp);
411 
416  co_nmt_boot_t *boot, const struct can_msg *msg);
417 
419 // clang-format off
420 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_node_state,
421  .on_enter = &co_nmt_boot_chk_node_on_enter,
422  .on_time = &co_nmt_boot_chk_node_on_time,
423  .on_recv = &co_nmt_boot_chk_node_on_recv
424 )
425 // clang-format on
426 
429 
435  co_unsigned32_t ac, const void *ptr, size_t n);
436 
438 // clang-format off
439 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_sw_state,
440  .on_enter = &co_nmt_boot_chk_sw_on_enter,
441  .on_up_con = &co_nmt_boot_chk_sw_on_up_con
442 )
443 // clang-format on
444 
447 
453  co_nmt_boot_t *boot, co_unsigned32_t ac);
454 
460  co_unsigned32_t ac, const void *ptr, size_t n);
461 
463 // clang-format off
464 LELY_CO_DEFINE_STATE(co_nmt_boot_stop_prog_state,
465  .on_enter = &co_nmt_boot_stop_prog_on_enter,
466  .on_dn_con = &co_nmt_boot_stop_prog_on_dn_con,
467  .on_up_con = &co_nmt_boot_stop_prog_on_up_con
468 )
469 // clang-format on
470 
473  co_nmt_boot_t *boot);
474 
480  co_nmt_boot_t *boot, co_unsigned32_t ac);
481 
483 // clang-format off
484 LELY_CO_DEFINE_STATE(co_nmt_boot_clear_prog_state,
485  .on_enter = &co_nmt_boot_clear_prog_on_enter,
487 )
488 // clang-format on
489 
492  co_nmt_boot_t *boot);
493 
499  co_nmt_boot_t *boot, co_unsigned32_t ac);
500 
502 // clang-format off
503 LELY_CO_DEFINE_STATE(co_nmt_boot_blk_dn_prog_state,
506 )
507 // clang-format on
508 
511 
517  co_nmt_boot_t *boot, co_unsigned32_t ac);
518 
520 // clang-format off
521 LELY_CO_DEFINE_STATE(co_nmt_boot_dn_prog_state,
522  .on_enter = &co_nmt_boot_dn_prog_on_enter,
523  .on_dn_con = &co_nmt_boot_dn_prog_on_dn_con
524 )
525 // clang-format on
526 
529  co_nmt_boot_t *boot);
530 
533  co_nmt_boot_t *boot, const struct timespec *tp);
534 
540  co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr,
541  size_t n);
542 
544 // clang-format off
545 LELY_CO_DEFINE_STATE(co_nmt_boot_wait_flash_state,
546  .on_enter = &co_nmt_boot_wait_flash_on_enter,
547  .on_time = &co_nmt_boot_wait_flash_on_time,
549 )
550 // clang-format on
551 
554 
560  co_unsigned32_t ac, const void *ptr, size_t n);
561 
563 // clang-format off
564 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_prog_state,
565  .on_enter = &co_nmt_boot_chk_prog_on_enter,
566  .on_up_con = &co_nmt_boot_chk_prog_on_up_con
567 )
568 // clang-format on
569 
572  co_nmt_boot_t *boot);
573 
579  co_nmt_boot_t *boot, co_unsigned32_t ac);
580 
582 // clang-format off
583 LELY_CO_DEFINE_STATE(co_nmt_boot_start_prog_state,
584  .on_enter = &co_nmt_boot_start_prog_on_enter,
586 )
587 // clang-format on
588 
591 
597  co_nmt_boot_t *boot, const struct timespec *tp);
598 
604  co_unsigned32_t ac, const void *ptr, size_t n);
605 
610 // clang-format off
611 LELY_CO_DEFINE_STATE(co_nmt_boot_wait_prog_state,
612  .on_enter = &co_nmt_boot_wait_prog_on_enter,
613  .on_time = &co_nmt_boot_wait_prog_on_time,
614  .on_up_con = &co_nmt_boot_wait_prog_on_up_con
615 )
616 // clang-format on
617 
620  co_nmt_boot_t *boot);
621 
627  co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr,
628  size_t n);
629 
633 // clang-format off
634 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_cfg_date_state,
637 )
638 // clang-format on
639 
645  co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr,
646  size_t n);
647 
651 // clang-format off
652 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_cfg_time_state,
654 )
655 // clang-format on
656 
659 
665  co_nmt_boot_t *boot, co_unsigned32_t ac);
666 
670 // clang-format off
671 LELY_CO_DEFINE_STATE(co_nmt_boot_up_cfg_state,
672  .on_enter = &co_nmt_boot_up_cfg_on_enter,
673  .on_cfg_con = &co_nmt_boot_up_cfg_on_cfg_con
674 )
675 // clang-format on
676 
679 
682  co_nmt_boot_t *boot, const struct timespec *tp);
683 
689  co_nmt_boot_t *boot, const struct can_msg *msg);
690 
692 // clang-format off
693 LELY_CO_DEFINE_STATE(co_nmt_boot_ec_state,
694  .on_enter = &co_nmt_boot_ec_on_enter,
695  .on_time = &co_nmt_boot_ec_on_time,
696  .on_recv = &co_nmt_boot_ec_on_recv
697 )
698 // clang-format on
699 
700 #undef LELY_CO_DEFINE_STATE
701 
714 static int co_nmt_boot_dn(co_nmt_boot_t *boot, co_unsigned16_t idx,
715  co_unsigned8_t subidx, co_unsigned16_t type, const void *val);
716 
727 static int co_nmt_boot_up(co_nmt_boot_t *boot, co_unsigned16_t idx,
728  co_unsigned8_t subidx);
729 
743 static int co_nmt_boot_chk(co_nmt_boot_t *boot, co_unsigned16_t idx,
744  co_unsigned8_t subidx, const void *ptr, size_t n);
745 
751 static int co_nmt_boot_send_rtr(co_nmt_boot_t *boot);
752 
753 void *
754 __co_nmt_boot_alloc(void)
755 {
756  void *ptr = malloc(sizeof(struct __co_nmt_boot));
757  if (__unlikely(!ptr))
758  set_errc(errno2c(errno));
759  return ptr;
760 }
761 
762 void
763 __co_nmt_boot_free(void *ptr)
764 {
765  free(ptr);
766 }
767 
768 struct __co_nmt_boot *
769 __co_nmt_boot_init(struct __co_nmt_boot *boot, can_net_t *net, co_dev_t *dev,
770  co_nmt_t *nmt)
771 {
772  assert(boot);
773  assert(net);
774  assert(dev);
775  assert(nmt);
776 
777  int errc = 0;
778 
779  boot->net = net;
780  boot->dev = dev;
781  boot->nmt = nmt;
782 
783  boot->state = NULL;
784 
785  boot->recv = can_recv_create();
786  if (__unlikely(!boot->recv)) {
787  errc = get_errc();
788  goto error_create_recv;
789  }
790  can_recv_set_func(boot->recv, &co_nmt_boot_recv, boot);
791 
792  boot->timer = can_timer_create();
793  if (__unlikely(!boot->timer)) {
794  errc = get_errc();
795  goto error_create_timer;
796  }
798 
799  boot->id = 0;
800 
801  boot->timeout = 0;
802  boot->sdo = NULL;
803 
804  boot->start = (struct timespec){ 0, 0 };
805  can_net_get_time(boot->net, &boot->start);
806 
807  boot->assignment = 0;
808  boot->ms = 0;
809 
810  boot->st = 0;
811  boot->es = 0;
812 
813  co_sdo_req_init(&boot->req);
814  boot->retry = 0;
815 
817  return boot;
818 
819  can_timer_destroy(boot->timer);
820 error_create_timer:
821  can_recv_destroy(boot->recv);
822 error_create_recv:
823  set_errc(errc);
824  return NULL;
825 }
826 
827 void
828 __co_nmt_boot_fini(struct __co_nmt_boot *boot)
829 {
830  assert(boot);
831 
832  co_sdo_req_fini(&boot->req);
833 
834  co_csdo_destroy(boot->sdo);
835 
836  can_timer_destroy(boot->timer);
837  can_recv_destroy(boot->recv);
838 }
839 
842 {
843  int errc = 0;
844 
845  co_nmt_boot_t *boot = __co_nmt_boot_alloc();
846  if (__unlikely(!boot)) {
847  errc = get_errc();
848  goto error_alloc_boot;
849  }
850 
851  if (__unlikely(!__co_nmt_boot_init(boot, net, dev, nmt))) {
852  errc = get_errc();
853  goto error_init_boot;
854  }
855 
856  return boot;
857 
858 error_init_boot:
859  __co_nmt_boot_free(boot);
860 error_alloc_boot:
861  set_errc(errc);
862  return NULL;
863 }
864 
865 void
867 {
868  if (boot) {
869  __co_nmt_boot_fini(boot);
870  __co_nmt_boot_free(boot);
871  }
872 }
873 
874 int
875 co_nmt_boot_boot_req(co_nmt_boot_t *boot, co_unsigned8_t id, int timeout,
876  co_csdo_ind_t *dn_ind, co_csdo_ind_t *up_ind, void *data)
877 {
878  assert(boot);
879 
880  if (__unlikely(!id || id > CO_NUM_NODES)) {
882  return -1;
883  }
884 
885  if (__unlikely(boot->state != co_nmt_boot_wait_state)) {
887  return -1;
888  }
889 
890  boot->id = id;
891 
892  boot->timeout = timeout;
893  co_csdo_destroy(boot->sdo);
894  boot->sdo = co_csdo_create(boot->net, NULL, boot->id);
895  if (__unlikely(!boot->sdo))
896  return -1;
897  co_csdo_set_timeout(boot->sdo, boot->timeout);
898  co_csdo_set_dn_ind(boot->sdo, dn_ind, data);
899  co_csdo_set_up_ind(boot->sdo, up_ind, data);
900 
901  co_nmt_boot_emit_time(boot, NULL);
902 
903  return 0;
904 }
905 
906 static int
907 co_nmt_boot_recv(const struct can_msg *msg, void *data)
908 {
909  assert(msg);
910  co_nmt_boot_t *boot = data;
911  assert(boot);
912 
913  co_nmt_boot_emit_recv(boot, msg);
914 
915  return 0;
916 }
917 
918 static int
919 co_nmt_boot_timer(const struct timespec *tp, void *data)
920 {
921  assert(tp);
922  co_nmt_boot_t *boot = data;
923  assert(boot);
924 
925  co_nmt_boot_emit_time(boot, tp);
926 
927  return 0;
928 }
929 
930 static void
931 co_nmt_boot_dn_con(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx,
932  co_unsigned32_t ac, void *data)
933 {
934  (void)sdo;
935  (void)idx;
936  (void)subidx;
937  co_nmt_boot_t *boot = data;
938  assert(boot);
939 
940  co_nmt_boot_emit_dn_con(boot, ac);
941 }
942 
943 static void
944 co_nmt_boot_up_con(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx,
945  co_unsigned32_t ac, const void *ptr, size_t n, void *data)
946 {
947  (void)sdo;
948  (void)idx;
949  (void)subidx;
950  co_nmt_boot_t *boot = data;
951  assert(boot);
952 
953  co_nmt_boot_emit_up_con(boot, ac, ptr, n);
954 }
955 
956 static void
957 co_nmt_boot_cfg_con(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned32_t ac,
958  void *data)
959 {
960  (void)nmt;
961  (void)id;
962  co_nmt_boot_t *boot = data;
963  assert(boot);
964 
965  co_nmt_boot_emit_cfg_con(boot, ac);
966 }
967 
968 static void
970 {
971  assert(boot);
972 
973  while (next) {
974  co_nmt_boot_state_t *prev = boot->state;
975  boot->state = next;
976 
977  if (prev && prev->on_leave)
978  prev->on_leave(boot);
979 
980  next = next->on_enter ? next->on_enter(boot) : NULL;
981  }
982 }
983 
984 static inline void
985 co_nmt_boot_emit_time(co_nmt_boot_t *boot, const struct timespec *tp)
986 {
987  assert(boot);
988  assert(boot->state);
989  assert(boot->state->on_time);
990 
991  co_nmt_boot_enter(boot, boot->state->on_time(boot, tp));
992 }
993 
994 static inline void
995 co_nmt_boot_emit_recv(co_nmt_boot_t *boot, const struct can_msg *msg)
996 {
997  assert(boot);
998  assert(boot->state);
999  assert(boot->state->on_recv);
1000 
1001  co_nmt_boot_enter(boot, boot->state->on_recv(boot, msg));
1002 }
1003 
1004 static inline void
1005 co_nmt_boot_emit_dn_con(co_nmt_boot_t *boot, co_unsigned32_t ac)
1006 {
1007  assert(boot);
1008  assert(boot->state);
1009  assert(boot->state->on_dn_con);
1010 
1011  co_nmt_boot_enter(boot, boot->state->on_dn_con(boot, ac));
1012 }
1013 
1014 static inline void
1015 co_nmt_boot_emit_up_con(co_nmt_boot_t *boot, co_unsigned32_t ac,
1016  const void *ptr, size_t n)
1017 {
1018  assert(boot);
1019  assert(boot->state);
1020  assert(boot->state->on_up_con);
1021 
1022  co_nmt_boot_enter(boot, boot->state->on_up_con(boot, ac, ptr, n));
1023 }
1024 
1025 static inline void
1026 co_nmt_boot_emit_cfg_con(co_nmt_boot_t *boot, co_unsigned32_t ac)
1027 {
1028  assert(boot);
1029  assert(boot->state);
1030  assert(boot->state->on_cfg_con);
1031 
1032  co_nmt_boot_enter(boot, boot->state->on_cfg_con(boot, ac));
1033 }
1034 
1035 static co_nmt_boot_state_t *
1036 co_nmt_boot_wait_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
1037 {
1038  (void)boot;
1039  (void)tp;
1040 
1041  boot->st = 0;
1042  boot->es = 0;
1043 
1044  // Retrieve the slave assignment for the node.
1045  boot->assignment = co_dev_get_val_u32(boot->dev, 0x1f81, boot->id);
1046 
1047  // Find the consumer heartbeat time for the node.
1048  boot->ms = 0;
1049  co_obj_t *obj_1016 = co_dev_find_obj(boot->dev, 0x1016);
1050  if (obj_1016) {
1051  co_unsigned8_t n = co_obj_get_val_u8(obj_1016, 0x00);
1052  for (size_t i = 1; i <= n; i++) {
1053  co_unsigned32_t val =
1054  co_obj_get_val_u32(obj_1016, i & 0xff);
1055  if (((val >> 16) & 0x7f) == boot->id)
1056  boot->ms = val & 0xffff;
1057  }
1058  }
1059 
1060  // Abort the 'boot slave' process if the slave is not in the network
1061  // list.
1062  if (!(boot->assignment & 0x01)) {
1063  boot->es = 'A';
1064  return co_nmt_boot_abort_state;
1065  }
1066 
1067  if (!(boot->assignment & 0x04))
1068  // Skip booting and start the error control service.
1069  return co_nmt_boot_ec_state;
1070 
1072 }
1073 
1074 static co_nmt_boot_state_t *
1076 {
1077  assert(boot);
1078 
1079  can_recv_stop(boot->recv);
1080  can_timer_stop(boot->timer);
1081 
1082  // If the node is already operational, end the 'boot slave' process with
1083  // error status L.
1084  if (!boot->es && (boot->st & ~CO_NMT_ST_TOGGLE) == CO_NMT_ST_START)
1085  boot->es = 'L';
1086 
1087  // Retry on error status B (see Fig. 4 in CiA 302-2 version 4.1.0).
1088  if (boot->es == 'B') {
1089  int wait = 1;
1090  if (boot->assignment & 0x08) {
1091  // Obtain the time (in milliseconds) the master will
1092  // wait for a mandatory slave to boot.
1093  co_unsigned32_t boot_time = co_dev_get_val_u32(
1094  boot->dev, 0x1f89, 0x00);
1095  // Check if this time has elapsed.
1096  if (boot_time) {
1097  struct timespec now = { 0, 0 };
1098  can_net_get_time(boot->net, &now);
1099  wait = timespec_diff_msec(&now, &boot->start)
1100  < boot_time;
1101  }
1102  }
1103  // If the slave is not mandatory, or the boot time has not yet
1104  // elapsed, wait asynchronously for a while and retry the 'boot
1105  // slave' process.
1106  if (wait) {
1107  can_timer_timeout(boot->timer, boot->net,
1109  return co_nmt_boot_wait_state;
1110  }
1111  }
1112 
1113  return co_nmt_boot_error_state;
1114 }
1115 
1116 static co_nmt_boot_state_t *
1118 {
1119  (void)boot;
1120 
1121  return co_nmt_boot_wait_state;
1122 }
1123 
1124 static void
1126 {
1127  assert(boot);
1128 
1129  co_nmt_boot_con(boot->nmt, boot->id, boot->st, boot->es);
1130 }
1131 
1132 static co_nmt_boot_state_t *
1134 {
1135  assert(boot);
1136 
1137  boot->es = 'B';
1138 
1139  // The device type check may follow an NMT 'reset communication'
1140  // command, in which case we may have to give the slave some time to
1141  // complete the state change. Start the first SDO request by simulating
1142  // a timeout.
1143  boot->retry = LELY_CO_NMT_BOOT_SDO_RETRY + 1;
1145  boot, CO_SDO_AC_TIMEOUT, NULL, 0);
1146 }
1147 
1148 static co_nmt_boot_state_t *
1150  const void *ptr, size_t n)
1151 {
1152  assert(boot);
1153 
1154  // Retry the SDO request on timeout (this includes the first attempt).
1155  if (ac == CO_SDO_AC_TIMEOUT && boot->retry--) {
1156  // Read the device type of the slave (object 1000).
1157  if (__unlikely(co_nmt_boot_up(boot, 0x1000, 0x00) == -1))
1158  return co_nmt_boot_abort_state;
1159  return NULL;
1160  } else if (__unlikely(ac)) {
1161  diag(DIAG_ERROR, 0,
1162  "SDO abort code %08" PRIX32
1163  " received on upload request of object 1000 (Device type) to node %02X: %s",
1164  ac, boot->id, co_sdo_ac2str(ac));
1165  return co_nmt_boot_abort_state;
1166  }
1167 
1168  // If the expected device type (sub-object 1F84:ID) is 0, skip the check
1169  // and proceed with the vendor ID.
1170  co_unsigned32_t device_type =
1171  co_dev_get_val_u32(boot->dev, 0x1f84, boot->id);
1172  // clang-format off
1173  if (__unlikely(device_type && !co_nmt_boot_chk(boot, 0x1f84, boot->id,
1174  ptr, n))) {
1175  // clang-format on
1176  boot->es = 'C';
1177  return co_nmt_boot_abort_state;
1178  }
1179 
1181 }
1182 
1183 static co_nmt_boot_state_t *
1185 {
1186  assert(boot);
1187 
1188  // If the expected vendor ID (sub-object 1F85:ID) is 0, skip the check
1189  // and proceed with the product code.
1190  co_unsigned32_t vendor_id =
1191  co_dev_get_val_u32(boot->dev, 0x1f85, boot->id);
1192  if (!vendor_id)
1194 
1195  boot->es = 'D';
1196 
1197  // Read the vendor ID of the slave (sub-object 1018:01).
1198  if (__unlikely(co_nmt_boot_up(boot, 0x1018, 0x01) == -1))
1199  return co_nmt_boot_abort_state;
1200 
1201  return NULL;
1202 }
1203 
1204 static co_nmt_boot_state_t *
1206  const void *ptr, size_t n)
1207 {
1208  assert(boot);
1209 
1210  if (__unlikely(ac))
1211  diag(DIAG_ERROR, 0,
1212  "SDO abort code %08" PRIX32
1213  " received on upload request of sub-object 1018:01 (Vendor-ID) to node %02X: %s",
1214  ac, boot->id, co_sdo_ac2str(ac));
1215 
1216  if (__unlikely(ac || !co_nmt_boot_chk(boot, 0x1f85, boot->id, ptr, n)))
1217  return co_nmt_boot_abort_state;
1218 
1220 }
1221 
1222 static co_nmt_boot_state_t *
1224 {
1225  assert(boot);
1226 
1227  // If the expected product code (sub-object 1F86:ID) is 0, skip the
1228  // check and proceed with the revision number.
1229  co_unsigned32_t product_code =
1230  co_dev_get_val_u32(boot->dev, 0x1f86, boot->id);
1231  if (!product_code)
1233 
1234  boot->es = 'M';
1235 
1236  // Read the product code of the slave (sub-object 1018:02).
1237  if (__unlikely(co_nmt_boot_up(boot, 0x1018, 0x02) == -1))
1238  return co_nmt_boot_abort_state;
1239 
1240  return NULL;
1241 }
1242 
1243 static co_nmt_boot_state_t *
1245  const void *ptr, size_t n)
1246 {
1247  assert(boot);
1248 
1249  if (__unlikely(ac))
1250  diag(DIAG_ERROR, 0,
1251  "SDO abort code %08" PRIX32
1252  " received on upload request of sub-object 1018:02 (Product code) to node %02X: %s",
1253  ac, boot->id, co_sdo_ac2str(ac));
1254 
1255  if (__unlikely(ac || !co_nmt_boot_chk(boot, 0x1f86, boot->id, ptr, n)))
1256  return co_nmt_boot_abort_state;
1257 
1259 }
1260 
1261 static co_nmt_boot_state_t *
1263 {
1264  assert(boot);
1265 
1266  // If the expected revision number (sub-object 1F87:ID) is 0, skip the
1267  // check and proceed with the serial number.
1268  co_unsigned32_t revision =
1269  co_dev_get_val_u32(boot->dev, 0x1f87, boot->id);
1270  if (!revision)
1272 
1273  boot->es = 'N';
1274 
1275  // Read the revision number of the slave (sub-object 1018:03).
1276  if (__unlikely(co_nmt_boot_up(boot, 0x1018, 0x03) == -1))
1277  return co_nmt_boot_abort_state;
1278 
1279  return NULL;
1280 }
1281 
1282 static co_nmt_boot_state_t *
1284  const void *ptr, size_t n)
1285 {
1286  assert(boot);
1287 
1288  if (__unlikely(ac))
1289  diag(DIAG_ERROR, 0,
1290  "SDO abort code %08" PRIX32
1291  " received on upload request of sub-object 1018:03 (Revision number) to node %02X: %s",
1292  ac, boot->id, co_sdo_ac2str(ac));
1293 
1294  if (__unlikely(ac || !co_nmt_boot_chk(boot, 0x1f87, boot->id, ptr, n)))
1295  return co_nmt_boot_abort_state;
1296 
1298 }
1299 
1300 static co_nmt_boot_state_t *
1302 {
1303  assert(boot);
1304 
1305  // If the expected serial number (sub-object 1F88:ID) is 0, skip the
1306  // check and proceed to 'check node state'.
1307  co_unsigned32_t serial_nr =
1308  co_dev_get_val_u32(boot->dev, 0x1f88, boot->id);
1309  if (!serial_nr)
1311 
1312  boot->es = 'O';
1313 
1314  // Read the serial number of the slave (sub-object 1018:04).
1315  if (__unlikely(co_nmt_boot_up(boot, 0x1018, 0x04) == -1))
1316  return co_nmt_boot_abort_state;
1317 
1318  return NULL;
1319 }
1320 
1321 static co_nmt_boot_state_t *
1323  const void *ptr, size_t n)
1324 {
1325  assert(boot);
1326 
1327  if (__unlikely(ac))
1328  diag(DIAG_ERROR, 0,
1329  "SDO abort code %08" PRIX32
1330  " received on upload request of sub-object 1018:04 (Serial number) to node %02X: %s",
1331  ac, boot->id, co_sdo_ac2str(ac));
1332 
1333  if (__unlikely(ac || !co_nmt_boot_chk(boot, 0x1f88, boot->id, ptr, n)))
1334  return co_nmt_boot_abort_state;
1335 
1337 }
1338 
1339 static co_nmt_boot_state_t *
1341 {
1342  assert(boot);
1343 
1344  // If the keep-alive bit is set, check the node state.
1345  if (boot->assignment & 0x10) {
1346  int ms;
1347  if (boot->ms) {
1348  ms = boot->ms;
1349  boot->es = 'E';
1350  } else {
1352  boot->es = 'F';
1353  // If we're not a heartbeat consumer, start node
1354  // guarding by sending the first RTR.
1355  co_nmt_boot_send_rtr(boot);
1356  }
1357 
1358  // Start the CAN frame receiver for the heartbeat or node guard
1359  // message.
1360  can_recv_start(boot->recv, boot->net, CO_NMT_EC_CANID(boot->id),
1361  0);
1362  // Start the CAN timer in case we do not receive a heartbeat
1363  // indication or a node guard confirmation.
1364  can_timer_timeout(boot->timer, boot->net, ms);
1365 
1366  return NULL;
1367  }
1368 
1369  return co_nmt_boot_chk_sw_state;
1370 }
1371 
1372 static co_nmt_boot_state_t *
1373 co_nmt_boot_chk_node_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
1374 {
1375  (void)boot;
1376  (void)tp;
1377 
1378  return co_nmt_boot_abort_state;
1379 }
1380 
1381 static co_nmt_boot_state_t *
1383 {
1384  assert(boot);
1385 
1386  can_recv_stop(boot->recv);
1387  can_timer_stop(boot->timer);
1388 
1389  assert(msg);
1390  assert(msg->len >= 1);
1391  boot->st = msg->data[0];
1392 
1393  if ((boot->st & ~CO_NMT_ST_TOGGLE) == CO_NMT_ST_START) {
1394  // If the node is already operational, skip the 'check and
1395  // update software version' and 'check configuration' steps and
1396  // proceed immediately to 'start error control service'.
1397  return co_nmt_boot_ec_state;
1398  } else {
1399  boot->st = 0;
1400  // If the node is not operational, send the NMT 'reset
1401  // communication' command and proceed as if the keep-alive bit
1402  // was not set.
1403  co_nmt_cs_req(boot->nmt, CO_NMT_CS_RESET_COMM, boot->id);
1404  return co_nmt_boot_chk_sw_state;
1405  }
1406 }
1407 
1408 static co_nmt_boot_state_t *
1410 {
1411  assert(boot);
1412 
1413  if (boot->assignment & 0x20) {
1414  boot->es = 'G';
1415 
1416  // Abort if the expected program software identification
1417  // (sub-object 1F55:ID) is 0.
1418  co_unsigned32_t sw_id =
1419  co_dev_get_val_u32(boot->dev, 0x1f55, boot->id);
1420  if (!sw_id)
1421  return co_nmt_boot_abort_state;
1422 
1423  // The software version check may follow an NMT 'reset
1424  // communication' command, in which case we may have to give the
1425  // slave some time to complete the state change. Start the first
1426  // SDO request by simulating a timeout.
1427  boot->retry = LELY_CO_NMT_BOOT_SDO_RETRY + 1;
1429  boot, CO_SDO_AC_TIMEOUT, NULL, 0);
1430  }
1431 
1432  // Continue with the 'check configuration' step if the software version
1433  // check is not necessary.
1435 }
1436 
1437 static co_nmt_boot_state_t *
1439  const void *ptr, size_t n)
1440 {
1441  assert(boot);
1442 
1443  // Retry the SDO request on timeout (this includes the first attempt).
1444  if (ac == CO_SDO_AC_TIMEOUT && boot->retry--) {
1445  // Read the program software identification of the slave
1446  // (sub-object 1F56:01).
1447  if (__unlikely(co_nmt_boot_up(boot, 0x1f56, 0x01) == -1))
1448  return co_nmt_boot_abort_state;
1449  return NULL;
1450  } else if (__unlikely(ac)) {
1451  diag(DIAG_ERROR, 0,
1452  "SDO abort code %08" PRIX32
1453  " received on upload request of sub-object 1F56:01 (Program software identification) to node %02X: %s",
1454  ac, boot->id, co_sdo_ac2str(ac));
1455  return co_nmt_boot_abort_state;
1456  }
1457 
1458  // If the program software identification matches the expected value,
1459  // proceed to 'check configuration'.
1460  if (co_nmt_boot_chk(boot, 0x1f55, boot->id, ptr, n))
1462 
1463  // Do not update the software if software update (bit 6) is not allowed
1464  // or if the keep-alive bit (bit 4) is set.
1465  if ((boot->assignment & 0x50) != 0x40) {
1466  boot->es = 'H';
1467  return co_nmt_boot_abort_state;
1468  }
1469 
1470  boot->es = 'I';
1471 
1473 }
1474 
1475 static co_nmt_boot_state_t *
1477 {
1478  assert(boot);
1479 
1480  // Read the program control of the slave (sub-object 1F51:01).
1481  if (__unlikely(co_nmt_boot_up(boot, 0x1f51, 0x01) == -1))
1482  return co_nmt_boot_abort_state;
1483 
1484  return NULL;
1485 }
1486 
1487 static co_nmt_boot_state_t *
1489 {
1490  assert(boot);
1491 
1492  // The download SDO request may be unconfirmed on some devices since it
1493  // stops the program on the slave (and may cause a restart of the
1494  // bootloader). We therefore ignore timeouts.
1495  if (__unlikely(ac && ac != CO_SDO_AC_TIMEOUT)) {
1496  diag(DIAG_ERROR, 0,
1497  "SDO abort code %08" PRIX32
1498  " received on download request of sub-object 1F51:01 (Program control) to node %02X: %s",
1499  ac, boot->id, co_sdo_ac2str(ac));
1500  return co_nmt_boot_abort_state;
1501  }
1502 
1504 }
1505 
1506 static co_nmt_boot_state_t *
1508  const void *ptr, size_t n)
1509 {
1510  assert(boot);
1511 
1512  // If the value is already 0 (Program stopped), do not write a 0 (Stop
1513  // program), but skip to the 'clear program' state.
1514  co_unsigned8_t val = 0;
1515  // clang-format off
1516  if (!ac && co_val_read(CO_DEFTYPE_UNSIGNED8, &val, ptr,
1517  (const uint8_t *)ptr + n) && !val)
1518  // clang-format on
1520 
1521  // Write a 0 (Stop program) to the program control of the slave
1522  // (sub-object 1F51:01).
1523  // clang-format off
1524  if (__unlikely(co_nmt_boot_dn(boot, 0x1f51, 0x01, CO_DEFTYPE_UNSIGNED8,
1525  &(co_unsigned8_t){ 0 }) == -1))
1526  // clang-format on
1527  return co_nmt_boot_abort_state;
1528 
1529  return NULL;
1530 }
1531 
1532 static co_nmt_boot_state_t *
1534 {
1535  assert(boot);
1536 
1537  // The 'clear program' command follows the 'stop program' command, which
1538  // may have triggered a reboot of the slave. In that case we may have to
1539  // give the slave some time to finish booting. Start the first SDO
1540  // request by simulating a timeout.
1541  boot->retry = LELY_CO_NMT_BOOT_SDO_RETRY + 1;
1543 }
1544 
1545 static co_nmt_boot_state_t *
1547 {
1548  assert(boot);
1549 
1550  // Retry the SDO request on timeout.
1551  if (ac == CO_SDO_AC_TIMEOUT && boot->retry--) {
1552  // Write a 3 (Clear program) to the program control of the slave
1553  // (sub-object 1F51:01).
1554  // clang-format off
1555  if (__unlikely(co_nmt_boot_dn(boot, 0x1f51, 0x01,
1556  CO_DEFTYPE_UNSIGNED8, &(co_unsigned8_t){ 3 })
1557  == -1))
1558  // clang-format on
1559  return co_nmt_boot_abort_state;
1560  return NULL;
1561  } else if (__unlikely(ac)) {
1562  diag(DIAG_ERROR, 0,
1563  "SDO abort code %08" PRIX32
1564  " received on download request of sub-object 1F51:01 (Program control) to node %02X: %s",
1565  ac, boot->id, co_sdo_ac2str(ac));
1566  return co_nmt_boot_abort_state;
1567  }
1568 
1570 }
1571 
1572 static co_nmt_boot_state_t *
1574 {
1575  assert(boot);
1576 
1577  co_sub_t *sub = co_dev_find_sub(boot->dev, 0x1f58, boot->id);
1578  if (__unlikely(!sub))
1579  return co_nmt_boot_abort_state;
1580 
1581  // Upload the program data.
1582  struct co_sdo_req *req = &boot->req;
1583  co_sdo_req_clear(req);
1584  co_unsigned32_t ac = co_sub_up_ind(sub, req);
1585  if (__unlikely(ac || !co_sdo_req_first(req) || !co_sdo_req_last(req))) {
1586  if (ac)
1587  diag(DIAG_ERROR, 0,
1588  "SDO abort code %08" PRIX32
1589  " on upload request of object 1F58:%02X (Program data): %s",
1590  ac, boot->id, co_sdo_ac2str(ac));
1591  return co_nmt_boot_abort_state;
1592  }
1593 
1594  // The 'clear program' step may take some time to complete, causing an
1595  // immediate 'download program' to generate a timeout. Start the first
1596  // attempt by simulating a timeout.
1597  boot->retry = LELY_CO_NMT_BOOT_SDO_RETRY + 1;
1599 }
1600 
1601 static co_nmt_boot_state_t *
1603 {
1604  (void)boot;
1605 
1606  // Retry the SDO request on timeout (this includes the first attempt).
1607  if (ac == CO_SDO_AC_TIMEOUT && boot->retry--) {
1608  struct co_sdo_req *req = &boot->req;
1609  // Write the program data (sub-object 1F58:ID) to the program
1610  // data of the slave (sub-object 1F50:01) using SDO block
1611  // transfer.
1612  // clang-format off
1613  if (__unlikely(co_csdo_blk_dn_req(boot->sdo, 0x1f50, 0x01,
1614  req->buf, req->size, &co_nmt_boot_dn_con, boot)
1615  == -1))
1616  // clang-format on
1617  return co_nmt_boot_abort_state;
1618  return NULL;
1619  } else if (__unlikely(ac)) {
1620  // If SDO block transfer is not supported, fall back to SDO
1621  // segmented transfer.
1623  }
1624 
1626 }
1627 
1628 static co_nmt_boot_state_t *
1630 {
1631  assert(boot);
1632 
1633  // If SDO block transfer is not supported, we may still have to wait for
1634  // the 'clear program' step to complete before successfully doing a
1635  // segmented SDO transfer. Start the first attempt by simulating a
1636  // timeout.
1637  boot->retry = LELY_CO_NMT_BOOT_SDO_RETRY + 1;
1639 }
1640 
1641 static co_nmt_boot_state_t *
1643 {
1644  assert(boot);
1645 
1646  // Retry the SDO request on timeout (this includes the first attempt).
1647  if (ac == CO_SDO_AC_TIMEOUT && boot->retry--) {
1648  struct co_sdo_req *req = &boot->req;
1649  // Write the program data (sub-object 1F58:ID) to the program
1650  // data of the slave (sub-object 1F50:01) using SDO segmented
1651  // transfer.
1652  // clang-format off
1653  if (__unlikely(co_csdo_dn_req(boot->sdo, 0x1f50, 0x01, req->buf,
1654  req->size, &co_nmt_boot_dn_con,
1655  boot) == -1))
1656  // clang-format on
1657  return co_nmt_boot_abort_state;
1658  return NULL;
1659  } else if (__unlikely(ac)) {
1660  diag(DIAG_ERROR, 0,
1661  "SDO abort code %08" PRIX32
1662  " received on download request of sub-object 1F50:01 (Program data) to node %02X: %s",
1663  ac, boot->id, co_sdo_ac2str(ac));
1664  return co_nmt_boot_abort_state;
1665  }
1666 
1668 }
1669 
1670 static co_nmt_boot_state_t *
1672 {
1673  assert(boot);
1674 
1675  // Wait for a while before checking the flash status indication.
1677  boot->timer, boot->net, LELY_CO_NMT_BOOT_CHECK_TIMEOUT);
1678 
1679  return NULL;
1680 }
1681 
1682 static co_nmt_boot_state_t *
1683 co_nmt_boot_wait_flash_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
1684 {
1685  assert(boot);
1686  (void)tp;
1687 
1688  // Read the flash status indication of the slave (sub-object 1F57:01).
1689  if (__unlikely(co_nmt_boot_up(boot, 0x1f57, 0x01) == -1))
1690  return co_nmt_boot_abort_state;
1691 
1692  return NULL;
1693 }
1694 
1695 static co_nmt_boot_state_t *
1697  const void *ptr, size_t n)
1698 {
1699  assert(boot);
1700 
1701  if (__unlikely(ac))
1702  diag(DIAG_ERROR, 0,
1703  "SDO abort code %08" PRIX32
1704  " received on upload request of sub-object 1F57:01 (Flash status indication) to node %02X: %s",
1705  ac, boot->id, co_sdo_ac2str(ac));
1706 
1707  // If the flash status indication is not valid, try again.
1708  co_unsigned32_t val = 0;
1709  // clang-format off
1711  (const uint8_t *)ptr + n) || (val & 0x01)))
1712  // clang-format on
1714 
1715  co_unsigned8_t st = (val >> 1) & 0x7f;
1716  switch (st) {
1717  case 0: return co_nmt_boot_chk_prog_state;
1718  case 1:
1719  diag(DIAG_ERROR, 0,
1720  "flash status identification %d: No valid program available",
1721  st);
1722  break;
1723  case 2:
1724  diag(DIAG_ERROR, 0,
1725  "flash status identification %d: Data format unknown",
1726  st);
1727  break;
1728  case 3:
1729  diag(DIAG_ERROR, 0,
1730  "flash status identification %d: Data format error or data CRC error",
1731  st);
1732  break;
1733  case 4:
1734  diag(DIAG_ERROR, 0,
1735  "flash status identification %d: Flash not cleared before write",
1736  st);
1737  break;
1738  case 5:
1739  diag(DIAG_ERROR, 0,
1740  "flash status identification %d: Flash write error",
1741  st);
1742  break;
1743  case 6:
1744  diag(DIAG_ERROR, 0,
1745  "flash status identification %d: General address error",
1746  st);
1747  break;
1748  case 7:
1749  diag(DIAG_ERROR, 0,
1750  "flash status identification %d: Flash secured (= write access currently forbidden)",
1751  st);
1752  break;
1753  case 63:
1754  diag(DIAG_ERROR, 0,
1755  "flash status identification %d: Unspecified error",
1756  st);
1757  break;
1758  default:
1759  if (st > 63)
1760  diag(DIAG_ERROR, 0,
1761  "flash status identification %d: Manufacturer-specific error: 0x%08" PRIX32
1762  "",
1763  st, (val >> 16) & 0xffff);
1764  break;
1765  }
1766 
1767  return co_nmt_boot_abort_state;
1768 }
1769 
1770 static co_nmt_boot_state_t *
1772 {
1773  assert(boot);
1774 
1775  // Read the program software identification of the slave (sub-object
1776  // 1F56:01).
1777  if (__unlikely(co_nmt_boot_up(boot, 0x1f56, 0x01) == -1))
1778  return co_nmt_boot_abort_state;
1779 
1780  return NULL;
1781 }
1782 
1783 static co_nmt_boot_state_t *
1785  const void *ptr, size_t n)
1786 {
1787  assert(boot);
1788 
1789  if (__unlikely(ac))
1790  diag(DIAG_ERROR, 0,
1791  "SDO abort code %08" PRIX32
1792  " received on upload request of sub-object 1F56:01 (Program software identification) to node %02X: %s",
1793  ac, boot->id, co_sdo_ac2str(ac));
1794 
1795  if (__unlikely(ac || !co_nmt_boot_chk(boot, 0x1f55, boot->id, ptr, n)))
1796  return co_nmt_boot_abort_state;
1797 
1799 }
1800 
1801 static co_nmt_boot_state_t *
1803 {
1804  assert(boot);
1805 
1806  // Write a 1 (Start program) to the program control of the slave
1807  // (sub-object 1F51:01).
1808  // clang-format off
1809  if (__unlikely(co_nmt_boot_dn(boot, 0x1f51, 0x01, CO_DEFTYPE_UNSIGNED8,
1810  &(co_unsigned8_t){ 1 }) == -1))
1811  // clang-format on
1812  return co_nmt_boot_abort_state;
1813 
1814  return NULL;
1815 }
1816 
1817 static co_nmt_boot_state_t *
1819 {
1820  assert(boot);
1821 
1822  if (__unlikely(ac)) {
1823  diag(DIAG_ERROR, 0,
1824  "SDO abort code %08" PRIX32
1825  " received on download request of sub-object 1F51:01 (Program control) to node %02X: %s",
1826  ac, boot->id, co_sdo_ac2str(ac));
1827  return co_nmt_boot_abort_state;
1828  }
1829 
1831 }
1832 
1833 static co_nmt_boot_state_t *
1835 {
1836  assert(boot);
1837 
1838  // Wait for a while before checking the program control.
1840  boot->timer, boot->net, LELY_CO_NMT_BOOT_CHECK_TIMEOUT);
1841 
1842  return NULL;
1843 }
1844 
1845 static co_nmt_boot_state_t *
1846 co_nmt_boot_wait_prog_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
1847 {
1848  assert(boot);
1849  (void)tp;
1850 
1851  // The 'start program' step may take some time to complete, causing an
1852  // immediate SDO upload request to generate a timeout. Start the first
1853  // attempt by simulating a timeout.
1854  boot->retry = LELY_CO_NMT_BOOT_SDO_RETRY + 1;
1856  boot, CO_SDO_AC_TIMEOUT, NULL, 0);
1857 
1858  return NULL;
1859 }
1860 
1861 static co_nmt_boot_state_t *
1863  const void *ptr, size_t n)
1864 {
1865  assert(boot);
1866 
1867  // Retry the SDO request on timeout (this includes the first attempt).
1868  if (ac == CO_SDO_AC_TIMEOUT && boot->retry--) {
1869  // Read the program control of the slave (sub-object 1F51:01).
1870  if (__unlikely(co_nmt_boot_up(boot, 0x1f51, 0x01) == -1))
1871  return co_nmt_boot_abort_state;
1872  return NULL;
1873  } else if (__unlikely(ac)) {
1874  diag(DIAG_ERROR, 0,
1875  "SDO abort code %08" PRIX32
1876  " received on upload request of sub-object 1F51:01 (Program control) to node %02X: %s",
1877  ac, boot->id, co_sdo_ac2str(ac));
1878  return co_nmt_boot_abort_state;
1879  }
1880 
1881  // If the program control differs from 'Program started', try again.
1882  co_unsigned8_t val = 0;
1883  // clang-format off
1885  (const uint8_t *)ptr + n) || val != 1))
1886  // clang-format on
1888 
1890 }
1891 
1892 static co_nmt_boot_state_t *
1894 {
1895  assert(boot);
1896 
1897  boot->es = 'J';
1898 
1899  // If the expected configuration date (sub-object 1F26:ID) or time
1900  // (sub-object 1F27:ID) are not configured, proceed to 'update
1901  // configuration'.
1902  co_unsigned32_t cfg_date =
1903  co_dev_get_val_u32(boot->dev, 0x1f26, boot->id);
1904  co_unsigned32_t cfg_time =
1905  co_dev_get_val_u32(boot->dev, 0x1f27, boot->id);
1906  if (!cfg_date || !cfg_time)
1907  return co_nmt_boot_up_cfg_state;
1908 
1909  // The configuration check may follow an NMT 'reset communication'
1910  // command (if the 'check software version' step was skipped), in which
1911  // case we may have to give the slave some time to complete the state
1912  // change. Start the first SDO request by simulating a timeout.
1913  boot->retry = LELY_CO_NMT_BOOT_SDO_RETRY + 1;
1915  boot, CO_SDO_AC_TIMEOUT, NULL, 0);
1916 }
1917 
1918 static co_nmt_boot_state_t *
1920  const void *ptr, size_t n)
1921 {
1922  assert(boot);
1923 
1924  // Retry the SDO request on timeout (this includes the first attempt).
1925  if (ac == CO_SDO_AC_TIMEOUT && boot->retry--) {
1926  // Read the configuration date of the slave (sub-object
1927  // 1020:01).
1928  if (__unlikely(co_nmt_boot_up(boot, 0x1020, 0x01) == -1))
1929  return co_nmt_boot_abort_state;
1930  return NULL;
1931  } else if (__unlikely(ac)) {
1932  diag(DIAG_ERROR, 0,
1933  "SDO abort code %08" PRIX32
1934  " received on upload request of sub-object 1020:01 (Configuration date) to node %02X: %s",
1935  ac, boot->id, co_sdo_ac2str(ac));
1936  }
1937 
1938  // If the configuration date does not match the expected value, skip
1939  // checking the time and proceed to 'update configuration'.
1940  if (__unlikely(ac || !co_nmt_boot_chk(boot, 0x1f26, boot->id, ptr, n)))
1941  return co_nmt_boot_up_cfg_state;
1942 
1943  // Read the configuration time of the slave (sub-object 1020:02).
1944  if (__unlikely(co_nmt_boot_up(boot, 0x1020, 0x02) == -1))
1945  return co_nmt_boot_abort_state;
1946 
1948 }
1949 
1950 static co_nmt_boot_state_t *
1952  const void *ptr, size_t n)
1953 {
1954  assert(boot);
1955 
1956  if (__unlikely(ac))
1957  diag(DIAG_ERROR, 0,
1958  "SDO abort code %08" PRIX32
1959  " received on upload request of sub-object 1020:02 (Configuration time) to node %02X: %s",
1960  ac, boot->id, co_sdo_ac2str(ac));
1961 
1962  // If the configuration time does not match the expected value, proceed
1963  // to 'update configuration'.
1964  if (__unlikely(ac || !co_nmt_boot_chk(boot, 0x1f27, boot->id, ptr, n)))
1965  return co_nmt_boot_up_cfg_state;
1966 
1967  return co_nmt_boot_ec_state;
1968 }
1969 
1970 static co_nmt_boot_state_t *
1972 {
1973  assert(boot);
1974 
1975  boot->es = 'J';
1976 
1977  // clang-format off
1978  if (__unlikely(co_nmt_cfg_req(boot->nmt, boot->id, boot->timeout,
1979  &co_nmt_boot_cfg_con, boot) == -1))
1980  // clang-format on
1981  return co_nmt_boot_abort_state;
1982 
1983  return NULL;
1984 }
1985 
1986 static co_nmt_boot_state_t *
1988 {
1989  assert(boot);
1990 
1991  if (__unlikely(ac)) {
1992  diag(DIAG_ERROR, 0,
1993  "SDO abort code %08" PRIX32
1994  " received while updating the configuration of node %02X: %s",
1995  ac, boot->id, co_sdo_ac2str(ac));
1996  return co_nmt_boot_abort_state;
1997  }
1998 
1999  return co_nmt_boot_ec_state;
2000 }
2001 
2002 static co_nmt_boot_state_t *
2004 {
2005  assert(boot);
2006 
2007  if (boot->ms) {
2008  boot->es = 'K';
2009  // Start the CAN frame receiver for heartbeat messages.
2010  can_recv_start(boot->recv, boot->net, CO_NMT_EC_CANID(boot->id),
2011  0);
2012  // Wait for the first heartbeat indication.
2013  can_timer_timeout(boot->timer, boot->net, boot->ms);
2014  return NULL;
2015  } else if (boot->assignment & 0x01) {
2016  // If the guard time is non-zero, start node guarding by sending
2017  // the first RTR, but do not wait for the response.
2018  co_unsigned16_t gt = (boot->assignment >> 16) & 0xffff;
2019  if (gt)
2020  co_nmt_boot_send_rtr(boot);
2021  }
2022 
2023  boot->es = 0;
2024  return co_nmt_boot_abort_state;
2025 }
2026 
2027 static co_nmt_boot_state_t *
2028 co_nmt_boot_ec_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
2029 {
2030  (void)boot;
2031  (void)tp;
2032 
2033  return co_nmt_boot_abort_state;
2034 }
2035 
2036 static co_nmt_boot_state_t *
2038 {
2039  assert(boot);
2040  assert(msg);
2041 
2042  if (msg->len >= 1) {
2043  co_unsigned8_t st = msg->data[0];
2044  // Do not consider a boot-up message to be a heartbeat message.
2045  if (st == CO_NMT_ST_BOOTUP)
2046  return NULL;
2047  boot->st = st;
2048  boot->es = 0;
2049  }
2050 
2051  return co_nmt_boot_abort_state;
2052 }
2053 
2054 static int
2055 co_nmt_boot_dn(co_nmt_boot_t *boot, co_unsigned16_t idx, co_unsigned8_t subidx,
2056  co_unsigned16_t type, const void *val)
2057 {
2058  assert(boot);
2059 
2060  return co_csdo_dn_val_req(boot->sdo, idx, subidx, type, val,
2061  &co_nmt_boot_dn_con, boot);
2062 }
2063 
2064 static int
2065 co_nmt_boot_up(co_nmt_boot_t *boot, co_unsigned16_t idx, co_unsigned8_t subidx)
2066 {
2067  assert(boot);
2068 
2069  return co_csdo_up_req(
2070  boot->sdo, idx, subidx, &co_nmt_boot_up_con, boot);
2071 }
2072 
2073 static int
2074 co_nmt_boot_chk(co_nmt_boot_t *boot, co_unsigned16_t idx, co_unsigned8_t subidx,
2075  const void *ptr, size_t n)
2076 {
2077  assert(boot);
2078 
2079  co_sub_t *sub = co_dev_find_sub(boot->dev, idx, subidx);
2080  if (__unlikely(!sub))
2081  return 0;
2082  co_unsigned16_t type = co_sub_get_type(sub);
2083 
2084  union co_val val;
2085  if (__unlikely(!co_val_read(type, &val, ptr, (const uint8_t *)ptr + n)))
2086  return 0;
2087 
2088  int eq = !co_val_cmp(type, &val, co_sub_get_val(sub));
2089  co_val_fini(type, &val);
2090  return eq;
2091 }
2092 
2093 static int
2095 {
2096  assert(boot);
2097 
2098  struct can_msg msg = CAN_MSG_INIT;
2099  msg.id = CO_NMT_EC_CANID(boot->id);
2100  msg.flags |= CAN_FLAG_RTR;
2101 
2102  return can_net_send(boot->net, &msg);
2103 }
2104 
2105 #endif // !LELY_NO_CO_MASTER
static co_nmt_boot_state_t * co_nmt_boot_stop_prog_on_enter(co_nmt_boot_t *boot)
The entry function of the &#39;stop program&#39; state.
Definition: nmt_boot.c:1476
static co_nmt_boot_state_t *const co_nmt_boot_abort_state
The &#39;abort&#39; state.
Definition: nmt_boot.c:288
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
A CANopen SDO upload/download request.
Definition: sdo.h:178
A CAN or CAN FD format frame.
Definition: msg.h:88
static void co_nmt_boot_emit_dn_con(co_nmt_boot_t *boot, co_unsigned32_t ac)
Invokes the &#39;SDO download confirmation&#39; transition function of the current state of a &#39;boot slave&#39; se...
Definition: nmt_boot.c:1005
void co_csdo_set_timeout(co_csdo_t *sdo, int timeout)
Sets the timeout of a Client-SDO.
Definition: csdo.c:944
uint_least32_t id
The identifier (11 or 29 bits, depending on the CAN_FLAG_IDE flag).
Definition: msg.h:90
A CANopen NMT &#39;boot slave&#39; state.
Definition: nmt_boot.c:203
A CAN network interface.
Definition: net.c:37
static co_nmt_boot_state_t * co_nmt_boot_chk_product_code_on_up_con(co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr, size_t n)
The &#39;SDO upload confirmation&#39; transition function of the &#39;check product code&#39; state.
Definition: nmt_boot.c:1244
A CANopen NMT &#39;boot slave&#39; service.
Definition: nmt_boot.c:67
static void co_nmt_boot_error_on_leave(co_nmt_boot_t *boot)
The exit function of the &#39;error&#39; state.
Definition: nmt_boot.c:1125
#define CO_NMT_ST_START
The NMT state &#39;operational&#39;.
Definition: nmt.h:61
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
static int co_nmt_boot_chk(co_nmt_boot_t *boot, co_unsigned16_t idx, co_unsigned8_t subidx, const void *ptr, size_t n)
Compares the result of an SDO upload request to the value of a local sub-object.
Definition: nmt_boot.c:2074
size_t size
The total size (in bytes) of the value to be uploaded/downloaded.
Definition: sdo.h:184
static void co_nmt_boot_emit_time(co_nmt_boot_t *boot, const struct timespec *tp)
Invokes the &#39;timeout&#39; transition function of the current state of a &#39;boot slave&#39; service.
Definition: nmt_boot.c:985
A CANopen sub-object.
Definition: obj.h:54
static void co_nmt_boot_dn_con(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx, co_unsigned32_t ac, void *data)
The CANopen SDO download confirmation callback function for a &#39;boot slave&#39; service.
Definition: nmt_boot.c:931
static co_nmt_boot_state_t * co_nmt_boot_up_cfg_on_cfg_con(co_nmt_boot_t *boot, co_unsigned32_t ac)
The &#39;configuration request confirmation&#39; transition unction of the &#39;update configuration&#39; state...
Definition: nmt_boot.c:1987
static co_nmt_boot_state_t * co_nmt_boot_chk_node_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
The &#39;timeout&#39; transition function of the &#39;check node state&#39; state.
Definition: nmt_boot.c:1373
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 &#39;configuration request&#39; for the specified node.
Definition: nmt.c:1588
static co_nmt_boot_state_t * co_nmt_boot_chk_device_type_on_enter(co_nmt_boot_t *boot)
The entry function of the &#39;check device type&#39; state.
Definition: nmt_boot.c:1133
static co_nmt_boot_state_t * co_nmt_boot_chk_serial_nr_on_up_con(co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr, size_t n)
The &#39;SDO upload confirmation&#39; transition function of the &#39;check serial number&#39; state.
Definition: nmt_boot.c:1322
void(* on_leave)(co_nmt_boot_t *boot)
A pointer to the function invoked when the current state is left.
Definition: nmt_boot.c:263
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
static co_nmt_boot_state_t *const co_nmt_boot_chk_product_code_state
The &#39;check product code&#39; state (see Fig. 5 in CiA 302-2 version 4.1.0).
Definition: nmt_boot.c:362
void co_nmt_boot_con(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es)
The CANopen NMT &#39;boot slave&#39; confirmation function, invoked when the &#39;boot slave&#39; process completes...
Definition: nmt.c:1865
void co_nmt_boot_destroy(co_nmt_boot_t *boot)
Destroys a CANopen NMT &#39;boot slave&#39; service.
Definition: nmt_boot.c:866
co_nmt_boot_state_t *(* on_recv)(co_nmt_boot_t *boot, const struct can_msg *msg)
A pointer to the transition function invoked when a CAN frame has been received.
Definition: nmt_boot.c:225
void co_sdo_req_clear(struct co_sdo_req *req)
Clears a CANopen SDO upload/download request, including its buffer.
Definition: sdo.c:129
#define LELY_CO_NMT_BOOT_WAIT_TIMEOUT
The timeout (in milliseconds) before trying to boot the slave again.
Definition: nmt_boot.c:41
#define CO_NMT_ST_TOGGLE
The mask to get/set the toggle bit from an NMT state.
Definition: nmt.h:73
#define CAN_MSG_INIT
The static initializer for can_msg.
Definition: msg.h:114
void set_errnum(errnum_t errnum)
Sets the current (thread-specific) platform-independent error number to errnum.
Definition: errnum.h:375
co_unsigned16_t ms
The consumer heartbeat time (in milliseconds).
Definition: nmt_boot.c:91
void diag(enum diag_severity severity, int errc, const char *format,...)
Emits a diagnostic message.
Definition: diag.c:156
static co_nmt_boot_state_t * co_nmt_boot_chk_serial_nr_on_enter(co_nmt_boot_t *boot)
The entry function of the &#39;check serial number&#39; state.
Definition: nmt_boot.c:1301
co_nmt_boot_state_t *(* on_cfg_con)(co_nmt_boot_t *boot, co_unsigned32_t ac)
A pointer to the transition function invoked when an NMT &#39;configuration request&#39; completes.
Definition: nmt_boot.c:260
static int co_nmt_boot_up(co_nmt_boot_t *boot, co_unsigned16_t idx, co_unsigned8_t subidx)
Issues an SDO upload request to the slave.
Definition: nmt_boot.c:2065
A union of the CANopen static data types.
Definition: val.h:163
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
static co_nmt_boot_state_t * co_nmt_boot_chk_device_type_on_up_con(co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr, size_t n)
The &#39;SDO upload confirmation&#39; transition function of the &#39;check device type&#39; state.
Definition: nmt_boot.c:1149
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 &#39;boot slave&#39; service.
Definition: nmt_boot.c:875
static co_nmt_boot_state_t * co_nmt_boot_chk_sw_on_enter(co_nmt_boot_t *boot)
The entry function of the &#39;check software&#39; state.
Definition: nmt_boot.c:1409
static co_nmt_boot_state_t *const co_nmt_boot_wait_prog_state
The &#39;wait till program is started&#39; state (see Fig.
Definition: nmt_boot.c:615
static int co_nmt_boot_recv(const struct can_msg *msg, void *data)
The CAN receive callback function for a &#39;boot slave&#39; service.
Definition: nmt_boot.c:907
co_nmt_boot_state_t *(* on_dn_con)(co_nmt_boot_t *boot, co_unsigned32_t ac)
A pointer to the transition function invoked when an SDO download request completes.
Definition: nmt_boot.c:236
This is the internal header file of the NMT &#39;boot slave&#39; declarations.
static co_nmt_boot_state_t * co_nmt_boot_chk_revision_on_enter(co_nmt_boot_t *boot)
The entry function of the &#39;check revision number&#39; state.
Definition: nmt_boot.c:1262
co_dev_t * dev
A pointer to a CANopen device.
Definition: nmt_boot.c:71
#define LELY_CO_NMT_BOOT_RTR_TIMEOUT
The timeout (in milliseconds) after sending a node guarding RTR.
Definition: nmt_boot.c:51
int co_csdo_dn_val_req(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx, co_unsigned16_t type, const void *val, co_csdo_dn_con_t *con, void *data)
Submits a download request to a remote Server-SDO.
Definition: csdo.c:1036
static co_nmt_boot_state_t *const co_nmt_boot_chk_cfg_time_state
The &#39;check configuration time&#39; state (see Fig.
Definition: nmt_boot.c:654
static co_nmt_boot_state_t * co_nmt_boot_wait_flash_on_up_con(co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr, size_t n)
The &#39;SDO upload confirmation&#39; transition function of the &#39;wait for end of flashing&#39; state...
Definition: nmt_boot.c:1696
static co_nmt_boot_state_t * co_nmt_boot_dn_prog_on_enter(co_nmt_boot_t *boot)
The entry function of the &#39;download program&#39; state.
Definition: nmt_boot.c:1629
static co_nmt_boot_state_t * co_nmt_boot_chk_node_on_enter(co_nmt_boot_t *boot)
The entry function of the &#39;check node state&#39; state.
Definition: nmt_boot.c:1340
static co_nmt_boot_state_t *const co_nmt_boot_ec_state
The &#39;start error control&#39; state (see Fig. 11 in CiA 302-2 version 4.1.0).
Definition: nmt_boot.c:697
static co_nmt_boot_state_t *const co_nmt_boot_chk_node_state
The &#39;check node state&#39; state (see Fig. 6 in CiA 302-2 version 4.1.0).
Definition: nmt_boot.c:424
static co_nmt_boot_state_t * co_nmt_boot_chk_revision_on_up_con(co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr, size_t n)
The &#39;SDO upload confirmation&#39; transition function of the &#39;check revision number&#39; state.
Definition: nmt_boot.c:1283
static co_nmt_boot_state_t *const co_nmt_boot_chk_vendor_id_state
The &#39;check vendor ID&#39; state (see Fig. 5 in CiA 302-2 version 4.1.0).
Definition: nmt_boot.c:342
This header file is part of the CANopen library; it contains the CANopen value declarations.
void set_errc(int errc)
Sets the current (thread-specific) native error code to errc.
Definition: errnum.c:957
static co_nmt_boot_state_t *const co_nmt_boot_start_prog_state
The &#39;start program&#39; state (see Fig. 3 in CiA 302-3 version 4.1.0).
Definition: nmt_boot.c:586
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
int retry
The number of SDO retries remaining.
Definition: nmt_boot.c:95
can_recv_t * can_recv_create(void)
Creates a new CAN frame receiver.
Definition: net.c:537
static co_nmt_boot_state_t * co_nmt_boot_chk_vendor_id_on_enter(co_nmt_boot_t *boot)
The entry function of the &#39;check vendor ID&#39; state.
Definition: nmt_boot.c:1184
static co_nmt_boot_state_t * co_nmt_boot_wait_prog_on_up_con(co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr, size_t n)
The &#39;SDO upload confirmation&#39; transition function of the &#39;wait till program is started&#39; state...
Definition: nmt_boot.c:1862
static co_nmt_boot_state_t *const co_nmt_boot_error_state
The &#39;error&#39; state.
Definition: nmt_boot.c:302
#define CO_DEFTYPE_UNSIGNED32
The data type (and object index) of a 32-bit unsigned integer.
Definition: type.h:50
uint_least8_t data[CAN_MSG_MAX_LEN]
The frame payload (in case of a data frame).
Definition: msg.h:103
static co_nmt_boot_state_t *const co_nmt_boot_up_cfg_state
The &#39;update configuration&#39; state (see Fig.
Definition: nmt_boot.c:674
This header file is part of the utilities library; it contains the time function declarations.
int co_sdo_req_first(const struct co_sdo_req *req)
Returns 1 if the specified request includes the first segment, and 0 otherwise.
Definition: sdo.h:340
void can_timer_stop(can_timer_t *timer)
Stops a CAN timer and unregisters it with a network interface.
Definition: net.c:468
static co_nmt_boot_state_t * co_nmt_boot_stop_prog_on_dn_con(co_nmt_boot_t *boot, co_unsigned32_t ac)
The &#39;SDO download confirmation&#39; transition function of the &#39;stop program&#39; state.
Definition: nmt_boot.c:1488
co_nmt_boot_state_t *(* on_up_con)(co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr, size_t n)
A pointer to the transition function invoked when an SDO upload request completes.
Definition: nmt_boot.c:249
can_recv_t * recv
A pointer to the CAN frame receiver.
Definition: nmt_boot.c:77
void can_recv_destroy(can_recv_t *recv)
Destroys a CAN frame receiver.
Definition: net.c:562
#define CO_SDO_AC_TIMEOUT
SDO abort code: SDO protocol timed out.
Definition: sdo.h:66
const void * co_sub_get_val(const co_sub_t *sub)
Returns a pointer to the current value of a CANopen sub-object.
Definition: obj.c:613
static co_nmt_boot_state_t * co_nmt_boot_chk_node_on_recv(co_nmt_boot_t *boot, const struct can_msg *msg)
The &#39;CAN frame received&#39; transition function of the &#39;check node state&#39; state.
Definition: nmt_boot.c:1382
This is the internal header file of the CANopen library.
static co_nmt_boot_state_t * co_nmt_boot_chk_prog_on_up_con(co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr, size_t n)
The &#39;SDO upload confirmation&#39; transition function of the &#39;check program SW ID&#39; state.
Definition: nmt_boot.c:1784
#define LELY_CO_NMT_BOOT_SDO_RETRY
The number of times an SDO request is retried after a timeout.
Definition: nmt_boot.c:46
co_nmt_boot_state_t * state
A pointer to the current state.
Definition: nmt_boot.c:75
static co_nmt_boot_state_t * co_nmt_boot_chk_vendor_id_on_up_con(co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr, size_t n)
The &#39;SDO upload confirmation&#39; transition function of the &#39;check vendor ID&#39; state. ...
Definition: nmt_boot.c:1205
Operation in progress.
Definition: errnum.h:125
co_unsigned32_t co_sub_up_ind(const co_sub_t *sub, struct co_sdo_req *req)
Invokes the upload indication function of a CANopen sub-object, registered with co_sub_set_up_ind().
Definition: obj.c:890
#define CO_NMT_ST_BOOTUP
The NMT state &#39;boot-up&#39;.
Definition: nmt.h:55
static void co_nmt_boot_cfg_con(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned32_t ac, void *data)
The CANopen NMT &#39;configuration request&#39; confirmation callback function for a &#39;boot slave&#39; service...
Definition: nmt_boot.c:957
int get_errc(void)
Returns the last (thread-specific) native error code set by a system call or library function...
Definition: errnum.c:947
static co_nmt_boot_state_t * co_nmt_boot_blk_dn_prog_on_enter(co_nmt_boot_t *boot)
The entry function of the &#39;download program&#39; state.
Definition: nmt_boot.c:1573
A CAN timer.
Definition: net.c:63
static co_nmt_boot_state_t * co_nmt_boot_abort_on_enter(co_nmt_boot_t *boot)
The entry function of the &#39;abort&#39; state.
Definition: nmt_boot.c:1075
static co_nmt_boot_state_t *const co_nmt_boot_chk_revision_state
The &#39;check revision number&#39; state (see Fig. 5 in CiA 302-2 version 4.1.0).
Definition: nmt_boot.c:382
A CANopen Client-SDO.
Definition: csdo.c:45
#define CO_NMT_EC_CANID(id)
The CAN identifier used for both node guarding and heartbeat monitoring.
Definition: nmt.h:76
void can_timer_destroy(can_timer_t *timer)
Destroys a CAN timer.
Definition: net.c:407
void co_csdo_set_dn_ind(co_csdo_t *sdo, co_csdo_ind_t *ind, void *data)
Sets the indication function used to notify the user of the progress of the current SDO download requ...
Definition: csdo.c:966
A CANopen NMT master/slave service.
Definition: nmt.c:104
static co_nmt_boot_state_t * co_nmt_boot_start_prog_on_dn_con(co_nmt_boot_t *boot, co_unsigned32_t ac)
The &#39;SDO download confirmation&#39; transition function of the &#39;start program&#39; state. ...
Definition: nmt_boot.c:1818
#define CO_DEFTYPE_UNSIGNED8
The data type (and object index) of an 8-bit unsigned integer.
Definition: type.h:44
static co_nmt_boot_state_t *const co_nmt_boot_chk_serial_nr_state
The &#39;check serial number&#39; state (see Fig. 5 in CiA 302-2 version 4.1.0).
Definition: nmt_boot.c:402
#define CO_NUM_NODES
The maximum number of nodes in a CANopen network.
Definition: dev.h:56
static void co_nmt_boot_up_con(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx, co_unsigned32_t ac, const void *ptr, size_t n, void *data)
The CANopen SDO upload confirmation callback function for a &#39;boot slave&#39; service. ...
Definition: nmt_boot.c:944
static int co_nmt_boot_timer(const struct timespec *tp, void *data)
The CAN timer callback function for a &#39;boot slave&#39; service.
Definition: nmt_boot.c:919
An error.
Definition: diag.h:49
static co_nmt_boot_state_t * co_nmt_boot_chk_prog_on_enter(co_nmt_boot_t *boot)
The entry function of the &#39;check program SW ID state.
Definition: nmt_boot.c:1771
Invalid argument.
Definition: errnum.h:129
static co_nmt_boot_state_t *const co_nmt_boot_chk_prog_state
The &#39;check program SW ID&#39; state (see Fig. 8 in CiA 302-2 version 4.1.0).
Definition: nmt_boot.c:567
static co_nmt_boot_state_t * co_nmt_boot_stop_prog_on_up_con(co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr, size_t n)
The &#39;SDO upload confirmation&#39; transition function of the &#39;stop program&#39; state.
Definition: nmt_boot.c:1507
static co_nmt_boot_state_t * co_nmt_boot_clear_prog_on_dn_con(co_nmt_boot_t *boot, co_unsigned32_t ac)
The &#39;SDO download confirmation&#39; transition function of the &#39;clear program&#39; state. ...
Definition: nmt_boot.c:1546
char es
The error status.
Definition: nmt_boot.c:99
int errno2c(int errnum)
Transforms a standard C error number to a native error code.
Definition: errnum.c:43
This header file is part of the utilities library; it contains the diagnostic declarations.
#define __unlikely(x)
Indicates to the compiler that the expression is most-likely false.
Definition: features.h:286
static co_nmt_boot_state_t * co_nmt_boot_chk_product_code_on_enter(co_nmt_boot_t *boot)
The entry function of the &#39;check product code&#39; state.
Definition: nmt_boot.c:1223
void co_csdo_ind_t(const co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx, size_t size, size_t nbyte, void *data)
The type of a CANopen Client-SDO request progress indication function, used to notify the user of the...
Definition: csdo.h:79
static co_nmt_boot_state_t * co_nmt_boot_wait_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
The &#39;timeout&#39; transition function of the &#39;wait asynchronously&#39; state.
Definition: nmt_boot.c:1036
int timeout
The SDO timeout (in milliseconds).
Definition: nmt_boot.c:83
co_unsigned8_t id
The node-ID.
Definition: nmt_boot.c:81
static void co_nmt_boot_emit_up_con(co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr, size_t n)
Invokes the &#39;SDO upload confirmation&#39; transition function of the current state of a &#39;boot slave&#39; serv...
Definition: nmt_boot.c:1015
static co_nmt_boot_state_t * co_nmt_boot_clear_prog_on_enter(co_nmt_boot_t *boot)
The entry function of the &#39;clear program&#39; state.
Definition: nmt_boot.c:1533
int co_val_cmp(co_unsigned16_t type, const void *v1, const void *v2)
Compares two values of the specified data type.
Definition: val.c:386
#define LELY_CO_NMT_BOOT_CHECK_TIMEOUT
The timeout (in milliseconds) before checking the flash status indication or the program control of a...
Definition: nmt_boot.c:59
A CANopen device.
Definition: dev.c:38
int co_csdo_up_req(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx, co_csdo_up_con_t *con, void *data)
Submits an upload request to a remote Server-SDO.
Definition: csdo.c:1080
co_csdo_t * co_csdo_create(can_net_t *net, co_dev_t *dev, co_unsigned8_t num)
Creates a new CANopen Client-SDO service.
Definition: csdo.c:867
static co_nmt_boot_state_t *const co_nmt_boot_clear_prog_state
The &#39;clear program&#39; state (see Fig. 3 in CiA 302-3 version 4.1.0).
Definition: nmt_boot.c:487
const char * co_sdo_ac2str(co_unsigned32_t ac)
Returns a string describing an SDO abort code.
Definition: sdo.c:57
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
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
static co_nmt_boot_state_t * co_nmt_boot_chk_cfg_date_on_up_con(co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr, size_t n)
The &#39;SDO upload confirmation&#39; transition function of the &#39;check configuration date&#39; state...
Definition: nmt_boot.c:1919
static co_nmt_boot_state_t *const co_nmt_boot_blk_dn_prog_state
The &#39;download program&#39; state (see Fig. 3 in CiA 302-3 version 4.1.0).
Definition: nmt_boot.c:506
static co_nmt_boot_state_t * co_nmt_boot_ec_on_recv(co_nmt_boot_t *boot, const struct can_msg *msg)
The &#39;CAN frame received&#39; transition function of the &#39;start error control&#39; state.
Definition: nmt_boot.c:2037
static co_nmt_boot_state_t *const co_nmt_boot_chk_sw_state
The &#39;check software&#39; state (see Fig. 6 in CiA 302-2 version 4.1.0).
Definition: nmt_boot.c:442
int_least64_t timespec_diff_msec(const struct timespec *t1, const struct timespec *t2)
Returns the time difference (in milliseconds) between *t1 and *t2.
Definition: time.h:203
can_timer_t * can_timer_create(void)
Creates a new CAN timer.
Definition: net.c:382
struct timespec start
The time at which the &#39;boot slave&#39; request was received.
Definition: nmt_boot.c:87
void co_csdo_destroy(co_csdo_t *sdo)
Destroys a CANopen Client-SDO service.
Definition: csdo.c:894
co_csdo_t * sdo
A pointer to the Client-SDO used to access slave objects.
Definition: nmt_boot.c:85
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
This header file is part of the CANopen library; it contains the device description declarations...
static co_nmt_boot_state_t *const co_nmt_boot_stop_prog_state
The &#39;stop program&#39; state (see Fig. 3 in CiA 302-3 version 4.1.0).
Definition: nmt_boot.c:468
static void co_nmt_boot_emit_recv(co_nmt_boot_t *boot, const struct can_msg *msg)
Invokes the &#39;CAN frame received&#39; transition function of the current state of a &#39;boot slave&#39; service...
Definition: nmt_boot.c:995
static co_nmt_boot_state_t * co_nmt_boot_chk_cfg_time_on_up_con(co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr, size_t n)
The &#39;SDO upload confirmation&#39; transition function of the &#39;check configuration time&#39; state...
Definition: nmt_boot.c:1951
static co_nmt_boot_state_t * co_nmt_boot_wait_flash_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
The &#39;timeout&#39; transition function of the &#39;wait for end of flashing&#39; state.
Definition: nmt_boot.c:1683
void co_val_fini(co_unsigned16_t type, void *val)
Finalizes a value of the specified data type.
Definition: val.c:273
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
static co_nmt_boot_state_t * co_nmt_boot_wait_prog_on_enter(co_nmt_boot_t *boot)
The entry function of the &#39;wait till program is started&#39; state.
Definition: nmt_boot.c:1834
static co_nmt_boot_state_t *const co_nmt_boot_wait_flash_state
The &#39;check flashing&#39; state (see Fig. 3 in CiA 302-3 version 4.1.0).
Definition: nmt_boot.c:549
static co_nmt_boot_state_t * co_nmt_boot_blk_dn_prog_on_dn_con(co_nmt_boot_t *boot, co_unsigned32_t ac)
The &#39;SDO download confirmation&#39; transition function of the &#39;download program&#39; state.
Definition: nmt_boot.c:1602
static co_nmt_boot_state_t *const co_nmt_boot_dn_prog_state
The &#39;download program&#39; state (see Fig. 3 in CiA 302-3 version 4.1.0).
Definition: nmt_boot.c:524
struct co_sdo_req req
The CANopen SDO upload request used for reading sub-objects.
Definition: nmt_boot.c:93
co_nmt_t * nmt
A pointer to an NMT master service.
Definition: nmt_boot.c:73
static void co_nmt_boot_emit_cfg_con(co_nmt_boot_t *boot, co_unsigned32_t ac)
Invokes the &#39;configuration request confirmation&#39; transition function of the current state of a &#39;boot ...
Definition: nmt_boot.c:1026
can_timer_t * timer
A pointer to the CAN timer.
Definition: nmt_boot.c:79
void co_sdo_req_init(struct co_sdo_req *req)
Initializes a CANopen SDO upload/download request.
Definition: sdo.c:109
co_unsigned8_t st
The state of the node (including the toggle bit).
Definition: nmt_boot.c:97
int co_csdo_dn_req(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx, const void *ptr, size_t n, co_csdo_dn_con_t *con, void *data)
Submits a download request to a remote Server-SDO.
Definition: csdo.c:1011
const void * buf
A pointer to the next bytes to be uploaded/downloaded.
Definition: sdo.h:186
static co_nmt_boot_state_t * co_nmt_boot_chk_sw_on_up_con(co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr, size_t n)
The &#39;SDO upload confirmation&#39; transition function of the &#39;check software&#39; state.
Definition: nmt_boot.c:1438
can_net_t * net
A pointer to a CAN network interface.
Definition: nmt_boot.c:69
static void co_nmt_boot_enter(co_nmt_boot_t *boot, co_nmt_boot_state_t *next)
Enters the specified state of a &#39;boot slave&#39; service and invokes the exit and entry functions...
Definition: nmt_boot.c:969
static co_nmt_boot_state_t * co_nmt_boot_ec_on_enter(co_nmt_boot_t *boot)
The entry function of the &#39;start error control&#39; state.
Definition: nmt_boot.c:2003
#define CAN_FLAG_RTR
The Remote Transmission Request (RTR) flag (unavailable in CAN FD format frames). ...
Definition: msg.h:47
static co_nmt_boot_state_t * co_nmt_boot_wait_flash_on_enter(co_nmt_boot_t *boot)
The entry function of the &#39;wait for end of flashing&#39; state.
Definition: nmt_boot.c:1671
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib.h> and defines any missing functionality.
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
A CAN frame receiver.
Definition: net.c:99
void co_sdo_req_fini(struct co_sdo_req *req)
Finalizes a CANopen SDO upload/download request.
Definition: sdo.c:121
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
static co_nmt_boot_state_t *const co_nmt_boot_chk_device_type_state
The &#39;check device type&#39; state (see Fig. 5 in CiA 302-2 version 4.1.0).
Definition: nmt_boot.c:322
static co_nmt_boot_state_t *const co_nmt_boot_chk_cfg_date_state
The &#39;check configuration date&#39; state (see Fig.
Definition: nmt_boot.c:637
co_nmt_boot_state_t *(* on_time)(co_nmt_boot_t *boot, const struct timespec *tp)
A pointer to the transition function invoked when a timeout occurs.
Definition: nmt_boot.c:214
static co_nmt_boot_state_t * co_nmt_boot_chk_cfg_date_on_enter(co_nmt_boot_t *boot)
The entry function of the &#39;check configuration date&#39; state.
Definition: nmt_boot.c:1893
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 &#39;boot slave&#39; service.
Definition: nmt_boot.c:841
static int co_nmt_boot_send_rtr(co_nmt_boot_t *boot)
Sends a node guarding RTR to the slave.
Definition: nmt_boot.c:2094
#define CO_NMT_CS_RESET_COMM
The NMT command specifier &#39;reset communication&#39;.
Definition: nmt.h:52
static co_nmt_boot_state_t * co_nmt_boot_error_on_enter(co_nmt_boot_t *boot)
The entry function of the &#39;error&#39; state.
Definition: nmt_boot.c:1117
void co_csdo_set_up_ind(co_csdo_t *sdo, co_csdo_ind_t *ind, void *data)
Sets the indication function used to notify the user of the progress of the current SDO upload reques...
Definition: csdo.c:986
A CANopen object.
Definition: obj.h:32
This header file is part of the CANopen library; it contains the object dictionary declarations...
static co_nmt_boot_state_t * co_nmt_boot_up_cfg_on_enter(co_nmt_boot_t *boot)
The entry function of the &#39;update configuration&#39; state.
Definition: nmt_boot.c:1971
void can_recv_start(can_recv_t *recv, can_net_t *net, uint_least32_t id, uint_least8_t flags)
Registers a CAN frame receiver with a network interface and starts processing frames.
Definition: net.c:591
co_unsigned32_t assignment
The NMT slave assignment (object 1F81).
Definition: nmt_boot.c:89
static co_nmt_boot_state_t *const co_nmt_boot_wait_state
The &#39;wait asynchronously&#39; state.
Definition: nmt_boot.c:278
int co_sdo_req_last(const struct co_sdo_req *req)
Returns 1 if the specified request includes the last segment, and 0 otherwise.
Definition: sdo.h:346
static co_nmt_boot_state_t * co_nmt_boot_ec_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
The &#39;timeout&#39; transition function of the &#39;start error control&#39; state.
Definition: nmt_boot.c:2028
static co_nmt_boot_state_t * co_nmt_boot_dn_prog_on_dn_con(co_nmt_boot_t *boot, co_unsigned32_t ac)
The &#39;SDO download confirmation&#39; transition function of the &#39;download program&#39; state.
Definition: nmt_boot.c:1642
int co_csdo_blk_dn_req(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx, const void *ptr, size_t n, co_csdo_dn_con_t *con, void *data)
Submits a block download request to a remote Server-SDO.
Definition: csdo.c:1099
static co_nmt_boot_state_t * co_nmt_boot_start_prog_on_enter(co_nmt_boot_t *boot)
The entry function of the &#39;start program&#39; state.
Definition: nmt_boot.c:1802
static co_nmt_boot_state_t * co_nmt_boot_wait_prog_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
The &#39;timeout&#39; transition function of the &#39;wait till program is started&#39; state.
Definition: nmt_boot.c:1846
co_sub_t * co_dev_find_sub(const co_dev_t *dev, co_unsigned16_t idx, co_unsigned8_t subidx)
Finds a sub-object in the object dictionary of a CANopen device.
Definition: dev.c:290
co_nmt_boot_state_t *(* on_enter)(co_nmt_boot_t *boot)
A pointer to the function invoked when a new state is entered.
Definition: nmt_boot.c:205
size_t co_val_read(co_unsigned16_t type, void *val, const uint8_t *begin, const uint8_t *end)
Reads a value of the specified data type from a memory buffer.
Definition: val.c:470
static int co_nmt_boot_dn(co_nmt_boot_t *boot, co_unsigned16_t idx, co_unsigned8_t subidx, co_unsigned16_t type, const void *val)
Issues an SDO download request to the slave.
Definition: nmt_boot.c:2055