Lely core libraries  2.3.4
nmt_boot.c
Go to the documentation of this file.
1 
24 #include "co.h"
25 
26 #if !LELY_NO_CO_MASTER && !LELY_NO_CO_NMT_BOOT
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 #if !LELY_NO_STDIO
37 #include <inttypes.h>
38 #endif
39 #include <stdlib.h>
40 
41 #ifndef LELY_CO_NMT_BOOT_WAIT_TIMEOUT
43 #define LELY_CO_NMT_BOOT_WAIT_TIMEOUT 1000
44 #endif
45 
46 #ifndef LELY_CO_NMT_BOOT_SDO_RETRY
48 #define LELY_CO_NMT_BOOT_SDO_RETRY 3
49 #endif
50 
51 #if !LELY_NO_CO_NG
52 #ifndef LELY_CO_NMT_BOOT_RTR_TIMEOUT
54 #define LELY_CO_NMT_BOOT_RTR_TIMEOUT 100
55 #endif
56 #endif
57 
58 #ifndef LELY_CO_NMT_BOOT_RESET_TIMEOUT
63 #define LELY_CO_NMT_BOOT_RESET_TIMEOUT 1000
64 #endif
65 
66 #ifndef LELY_CO_NMT_BOOT_CHECK_TIMEOUT
71 #define LELY_CO_NMT_BOOT_CHECK_TIMEOUT 100
72 #endif
73 
74 struct __co_nmt_boot_state;
76 typedef const struct __co_nmt_boot_state co_nmt_boot_state_t;
77 
79 struct __co_nmt_boot {
93  co_unsigned8_t id;
95  int timeout;
99  struct timespec start;
101  co_unsigned32_t assignment;
103  co_unsigned16_t ms;
105  struct co_sdo_req req;
107  int retry;
109  co_unsigned8_t st;
111  char es;
112 };
113 
119 static int co_nmt_boot_recv(const struct can_msg *msg, void *data);
120 
126 static int co_nmt_boot_timer(const struct timespec *tp, void *data);
127 
134 static void co_nmt_boot_dn_con(co_csdo_t *sdo, co_unsigned16_t idx,
135  co_unsigned8_t subidx, co_unsigned32_t ac, void *data);
136 
143 static void co_nmt_boot_up_con(co_csdo_t *sdo, co_unsigned16_t idx,
144  co_unsigned8_t subidx, co_unsigned32_t ac, const void *ptr,
145  size_t n, void *data);
146 
153 static void co_nmt_boot_cfg_con(co_nmt_t *nmt, co_unsigned8_t id,
154  co_unsigned32_t ac, void *data);
155 
160 static void co_nmt_boot_enter(co_nmt_boot_t *boot, co_nmt_boot_state_t *next);
161 
169 static inline void co_nmt_boot_emit_recv(
170  co_nmt_boot_t *boot, const struct can_msg *msg);
171 
179 static inline void co_nmt_boot_emit_time(
180  co_nmt_boot_t *boot, const struct timespec *tp);
181 
189 static inline void co_nmt_boot_emit_dn_con(
190  co_nmt_boot_t *boot, co_unsigned32_t ac);
191 
201 static inline void co_nmt_boot_emit_up_con(co_nmt_boot_t *boot,
202  co_unsigned32_t ac, const void *ptr, size_t n);
203 
211 static inline void co_nmt_boot_emit_cfg_con(
212  co_nmt_boot_t *boot, co_unsigned32_t ac);
213 
217  co_nmt_boot_state_t *(*on_enter)(co_nmt_boot_t *boot);
227  co_nmt_boot_state_t *(*on_recv)(
228  co_nmt_boot_t *boot, const struct can_msg *msg);
237  co_nmt_boot_state_t *(*on_time)(
238  co_nmt_boot_t *boot, const struct timespec *tp);
248  co_nmt_boot_state_t *(*on_dn_con)(
249  co_nmt_boot_t *boot, co_unsigned32_t ac);
261  co_nmt_boot_state_t *(*on_up_con)(co_nmt_boot_t *boot,
262  co_unsigned32_t ac, const void *ptr, size_t n);
272  co_nmt_boot_state_t *(*on_cfg_con)(
273  co_nmt_boot_t *boot, co_unsigned32_t ac);
275  void (*on_leave)(co_nmt_boot_t *boot);
276 };
277 
278 #define LELY_CO_DEFINE_STATE(name, ...) \
279  static co_nmt_boot_state_t *const name = \
280  &(co_nmt_boot_state_t){ __VA_ARGS__ };
281 
284  co_nmt_boot_t *boot, const struct timespec *tp);
285 
287 // clang-format off
288 LELY_CO_DEFINE_STATE(co_nmt_boot_wait_state,
289  .on_time = &co_nmt_boot_wait_on_time
290 )
291 // clang-format on
292 
295 
297 // clang-format off
298 LELY_CO_DEFINE_STATE(co_nmt_boot_abort_state,
299  .on_enter = &co_nmt_boot_abort_on_enter
300 )
301 // clang-format on
302 
305 
307 static void co_nmt_boot_error_on_leave(co_nmt_boot_t *boot);
308 
310 // clang-format off
311 LELY_CO_DEFINE_STATE(co_nmt_boot_error_state,
312  .on_enter = &co_nmt_boot_error_on_enter,
313  .on_leave = &co_nmt_boot_error_on_leave
314 )
315 // clang-format on
316 
319  co_nmt_boot_t *boot);
320 
326  co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr,
327  size_t n);
328 
330 // clang-format off
331 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_device_type_state,
334 )
335 // clang-format on
336 
339  co_nmt_boot_t *boot);
340 
346  co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr,
347  size_t n);
348 
350 // clang-format off
351 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_vendor_id_state,
354 )
355 // clang-format on
356 
359  co_nmt_boot_t *boot);
360 
366  co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr,
367  size_t n);
368 
370 // clang-format off
371 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_product_code_state,
374 )
375 // clang-format on
376 
379  co_nmt_boot_t *boot);
380 
386  co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr,
387  size_t n);
388 
390 // clang-format off
391 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_revision_state,
394 )
395 // clang-format on
396 
399  co_nmt_boot_t *boot);
400 
406  co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr,
407  size_t n);
408 
410 // clang-format off
411 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_serial_nr_state,
414 )
415 // clang-format on
416 
419 
424  co_nmt_boot_t *boot, const struct can_msg *msg);
425 
428  co_nmt_boot_t *boot, const struct timespec *tp);
429 
432 
434 // clang-format off
435 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_node_state,
436  .on_enter = &co_nmt_boot_chk_node_on_enter,
437  .on_recv = &co_nmt_boot_chk_node_on_recv,
438  .on_time = &co_nmt_boot_chk_node_on_time,
439  .on_leave = &co_nmt_boot_chk_node_on_leave
440 )
441 // clang-format on
442 
445  co_nmt_boot_t *boot);
446 
452  co_nmt_boot_t *boot, const struct can_msg *msg);
453 
456  co_nmt_boot_t *boot, const struct timespec *tp);
457 
459 // clang-format off
460 LELY_CO_DEFINE_STATE(co_nmt_boot_reset_comm_state,
461  .on_enter = &co_nmt_boot_reset_comm_on_enter,
462  .on_recv = &co_nmt_boot_reset_comm_on_recv,
464 )
465 // clang-format on
466 
469 
475  co_unsigned32_t ac, const void *ptr, size_t n);
476 
478 // clang-format off
479 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_sw_state,
480  .on_enter = &co_nmt_boot_chk_sw_on_enter,
481  .on_up_con = &co_nmt_boot_chk_sw_on_up_con
482 )
483 // clang-format on
484 
487 
493  co_nmt_boot_t *boot, co_unsigned32_t ac);
494 
500  co_unsigned32_t ac, const void *ptr, size_t n);
501 
503 // clang-format off
504 LELY_CO_DEFINE_STATE(co_nmt_boot_stop_prog_state,
505  .on_enter = &co_nmt_boot_stop_prog_on_enter,
506  .on_dn_con = &co_nmt_boot_stop_prog_on_dn_con,
507  .on_up_con = &co_nmt_boot_stop_prog_on_up_con
508 )
509 // clang-format on
510 
513  co_nmt_boot_t *boot);
514 
520  co_nmt_boot_t *boot, co_unsigned32_t ac);
521 
523 // clang-format off
524 LELY_CO_DEFINE_STATE(co_nmt_boot_clear_prog_state,
525  .on_enter = &co_nmt_boot_clear_prog_on_enter,
527 )
528 // clang-format on
529 
532  co_nmt_boot_t *boot);
533 
539  co_nmt_boot_t *boot, co_unsigned32_t ac);
540 
542 // clang-format off
543 LELY_CO_DEFINE_STATE(co_nmt_boot_blk_dn_prog_state,
546 )
547 // clang-format on
548 
551 
557  co_nmt_boot_t *boot, co_unsigned32_t ac);
558 
560 // clang-format off
561 LELY_CO_DEFINE_STATE(co_nmt_boot_dn_prog_state,
562  .on_enter = &co_nmt_boot_dn_prog_on_enter,
563  .on_dn_con = &co_nmt_boot_dn_prog_on_dn_con
564 )
565 // clang-format on
566 
569  co_nmt_boot_t *boot);
570 
573  co_nmt_boot_t *boot, const struct timespec *tp);
574 
580  co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr,
581  size_t n);
582 
584 // clang-format off
585 LELY_CO_DEFINE_STATE(co_nmt_boot_wait_flash_state,
586  .on_enter = &co_nmt_boot_wait_flash_on_enter,
587  .on_time = &co_nmt_boot_wait_flash_on_time,
589 )
590 // clang-format on
591 
594 
600  co_unsigned32_t ac, const void *ptr, size_t n);
601 
603 // clang-format off
604 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_prog_state,
605  .on_enter = &co_nmt_boot_chk_prog_on_enter,
606  .on_up_con = &co_nmt_boot_chk_prog_on_up_con
607 )
608 // clang-format on
609 
612  co_nmt_boot_t *boot);
613 
619  co_nmt_boot_t *boot, co_unsigned32_t ac);
620 
622 // clang-format off
623 LELY_CO_DEFINE_STATE(co_nmt_boot_start_prog_state,
624  .on_enter = &co_nmt_boot_start_prog_on_enter,
626 )
627 // clang-format on
628 
631 
637  co_nmt_boot_t *boot, const struct timespec *tp);
638 
644  co_unsigned32_t ac, const void *ptr, size_t n);
645 
650 // clang-format off
651 LELY_CO_DEFINE_STATE(co_nmt_boot_wait_prog_state,
652  .on_enter = &co_nmt_boot_wait_prog_on_enter,
653  .on_time = &co_nmt_boot_wait_prog_on_time,
654  .on_up_con = &co_nmt_boot_wait_prog_on_up_con
655 )
656 // clang-format on
657 
660  co_nmt_boot_t *boot);
661 
667  co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr,
668  size_t n);
669 
673 // clang-format off
674 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_cfg_date_state,
677 )
678 // clang-format on
679 
685  co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr,
686  size_t n);
687 
691 // clang-format off
692 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_cfg_time_state,
694 )
695 // clang-format on
696 
699 
705  co_nmt_boot_t *boot, co_unsigned32_t ac);
706 
710 // clang-format off
711 LELY_CO_DEFINE_STATE(co_nmt_boot_up_cfg_state,
712  .on_enter = &co_nmt_boot_up_cfg_on_enter,
713  .on_cfg_con = &co_nmt_boot_up_cfg_on_cfg_con
714 )
715 // clang-format on
716 
719 
725  co_nmt_boot_t *boot, const struct can_msg *msg);
726 
729  co_nmt_boot_t *boot, const struct timespec *tp);
730 
732 // clang-format off
733 LELY_CO_DEFINE_STATE(co_nmt_boot_ec_state,
734  .on_enter = &co_nmt_boot_ec_on_enter,
735  .on_recv = &co_nmt_boot_ec_on_recv,
736  .on_time = &co_nmt_boot_ec_on_time
737 )
738 // clang-format on
739 
740 #undef LELY_CO_DEFINE_STATE
741 
754 static int co_nmt_boot_dn(co_nmt_boot_t *boot, co_unsigned16_t idx,
755  co_unsigned8_t subidx, co_unsigned16_t type, const void *val);
756 
767 static int co_nmt_boot_up(co_nmt_boot_t *boot, co_unsigned16_t idx,
768  co_unsigned8_t subidx);
769 
783 static int co_nmt_boot_chk(co_nmt_boot_t *boot, co_unsigned16_t idx,
784  co_unsigned8_t subidx, const void *ptr, size_t n);
785 
786 #if !LELY_NO_CO_NG
792 static int co_nmt_boot_send_rtr(co_nmt_boot_t *boot);
793 #endif
794 
795 void *
796 __co_nmt_boot_alloc(void)
797 {
798  void *ptr = malloc(sizeof(struct __co_nmt_boot));
799 #if !LELY_NO_ERRNO
800  if (!ptr)
801  set_errc(errno2c(errno));
802 #endif
803  return ptr;
804 }
805 
806 void
807 __co_nmt_boot_free(void *ptr)
808 {
809  free(ptr);
810 }
811 
812 struct __co_nmt_boot *
813 __co_nmt_boot_init(struct __co_nmt_boot *boot, can_net_t *net, co_dev_t *dev,
814  co_nmt_t *nmt)
815 {
816  assert(boot);
817  assert(net);
818  assert(dev);
819  assert(nmt);
820 
821  int errc = 0;
822 
823  boot->net = net;
824  boot->dev = dev;
825  boot->nmt = nmt;
826 
827  boot->state = NULL;
828 
829  boot->recv = can_recv_create();
830  if (!boot->recv) {
831  errc = get_errc();
832  goto error_create_recv;
833  }
834  can_recv_set_func(boot->recv, &co_nmt_boot_recv, boot);
835 
836  boot->timer = can_timer_create();
837  if (!boot->timer) {
838  errc = get_errc();
839  goto error_create_timer;
840  }
842 
843  boot->id = 0;
844 
845  boot->timeout = 0;
846  boot->sdo = NULL;
847 
848  boot->start = (struct timespec){ 0, 0 };
849  can_net_get_time(boot->net, &boot->start);
850 
851  boot->assignment = 0;
852  boot->ms = 0;
853 
854  boot->st = 0;
855  boot->es = 0;
856 
857  co_sdo_req_init(&boot->req);
858  boot->retry = 0;
859 
861  return boot;
862 
863  // can_timer_destroy(boot->timer);
864 error_create_timer:
865  can_recv_destroy(boot->recv);
866 error_create_recv:
867  set_errc(errc);
868  return NULL;
869 }
870 
871 void
872 __co_nmt_boot_fini(struct __co_nmt_boot *boot)
873 {
874  assert(boot);
875 
876  co_sdo_req_fini(&boot->req);
877 
878  co_csdo_destroy(boot->sdo);
879 
880  can_timer_destroy(boot->timer);
881  can_recv_destroy(boot->recv);
882 }
883 
886 {
887  int errc = 0;
888 
889  co_nmt_boot_t *boot = __co_nmt_boot_alloc();
890  if (!boot) {
891  errc = get_errc();
892  goto error_alloc_boot;
893  }
894 
895  if (!__co_nmt_boot_init(boot, net, dev, nmt)) {
896  errc = get_errc();
897  goto error_init_boot;
898  }
899 
900  return boot;
901 
902 error_init_boot:
903  __co_nmt_boot_free(boot);
904 error_alloc_boot:
905  set_errc(errc);
906  return NULL;
907 }
908 
909 void
911 {
912  if (boot) {
913  __co_nmt_boot_fini(boot);
914  __co_nmt_boot_free(boot);
915  }
916 }
917 
918 int
919 co_nmt_boot_boot_req(co_nmt_boot_t *boot, co_unsigned8_t id, int timeout,
920  co_csdo_ind_t *dn_ind, co_csdo_ind_t *up_ind, void *data)
921 {
922  assert(boot);
923 
924  if (!id || id > CO_NUM_NODES) {
926  return -1;
927  }
928 
929  if (boot->state != co_nmt_boot_wait_state) {
931  return -1;
932  }
933 
934  boot->id = id;
935 
936  boot->timeout = timeout;
937  co_csdo_destroy(boot->sdo);
938  boot->sdo = co_csdo_create(boot->net, NULL, boot->id);
939  if (!boot->sdo)
940  return -1;
941  co_csdo_set_timeout(boot->sdo, boot->timeout);
942  co_csdo_set_dn_ind(boot->sdo, dn_ind, data);
943  co_csdo_set_up_ind(boot->sdo, up_ind, data);
944 
945  co_nmt_boot_emit_time(boot, NULL);
946 
947  return 0;
948 }
949 
950 static int
951 co_nmt_boot_recv(const struct can_msg *msg, void *data)
952 {
953  assert(msg);
954  co_nmt_boot_t *boot = data;
955  assert(boot);
956 
957  co_nmt_boot_emit_recv(boot, msg);
958 
959  return 0;
960 }
961 
962 static int
963 co_nmt_boot_timer(const struct timespec *tp, void *data)
964 {
965  assert(tp);
966  co_nmt_boot_t *boot = data;
967  assert(boot);
968 
969  co_nmt_boot_emit_time(boot, tp);
970 
971  return 0;
972 }
973 
974 static void
975 co_nmt_boot_dn_con(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx,
976  co_unsigned32_t ac, void *data)
977 {
978  (void)sdo;
979  (void)idx;
980  (void)subidx;
981  co_nmt_boot_t *boot = data;
982  assert(boot);
983 
984  co_nmt_boot_emit_dn_con(boot, ac);
985 }
986 
987 static void
988 co_nmt_boot_up_con(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx,
989  co_unsigned32_t ac, const void *ptr, size_t n, void *data)
990 {
991  (void)sdo;
992  (void)idx;
993  (void)subidx;
994  co_nmt_boot_t *boot = data;
995  assert(boot);
996 
997  co_nmt_boot_emit_up_con(boot, ac, ptr, n);
998 }
999 
1000 static void
1001 co_nmt_boot_cfg_con(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned32_t ac,
1002  void *data)
1003 {
1004  (void)nmt;
1005  (void)id;
1006  co_nmt_boot_t *boot = data;
1007  assert(boot);
1008 
1009  co_nmt_boot_emit_cfg_con(boot, ac);
1010 }
1011 
1012 static void
1014 {
1015  assert(boot);
1016 
1017  while (next) {
1018  co_nmt_boot_state_t *prev = boot->state;
1019  boot->state = next;
1020 
1021  if (prev && prev->on_leave)
1022  prev->on_leave(boot);
1023 
1024  next = next->on_enter ? next->on_enter(boot) : NULL;
1025  }
1026 }
1027 
1028 static inline void
1030 {
1031  assert(boot);
1032  assert(boot->state);
1033  assert(boot->state->on_recv);
1034 
1035  co_nmt_boot_enter(boot, boot->state->on_recv(boot, msg));
1036 }
1037 
1038 static inline void
1039 co_nmt_boot_emit_time(co_nmt_boot_t *boot, const struct timespec *tp)
1040 {
1041  assert(boot);
1042  assert(boot->state);
1043  assert(boot->state->on_time);
1044 
1045  co_nmt_boot_enter(boot, boot->state->on_time(boot, tp));
1046 }
1047 
1048 static inline void
1049 co_nmt_boot_emit_dn_con(co_nmt_boot_t *boot, co_unsigned32_t ac)
1050 {
1051  assert(boot);
1052  assert(boot->state);
1053  assert(boot->state->on_dn_con);
1054 
1055  co_nmt_boot_enter(boot, boot->state->on_dn_con(boot, ac));
1056 }
1057 
1058 static inline void
1059 co_nmt_boot_emit_up_con(co_nmt_boot_t *boot, co_unsigned32_t ac,
1060  const void *ptr, size_t n)
1061 {
1062  assert(boot);
1063  assert(boot->state);
1064  assert(boot->state->on_up_con);
1065 
1066  co_nmt_boot_enter(boot, boot->state->on_up_con(boot, ac, ptr, n));
1067 }
1068 
1069 static inline void
1070 co_nmt_boot_emit_cfg_con(co_nmt_boot_t *boot, co_unsigned32_t ac)
1071 {
1072  assert(boot);
1073  assert(boot->state);
1074  assert(boot->state->on_cfg_con);
1075 
1076  co_nmt_boot_enter(boot, boot->state->on_cfg_con(boot, ac));
1077 }
1078 
1079 static co_nmt_boot_state_t *
1080 co_nmt_boot_wait_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
1081 {
1082  (void)boot;
1083  (void)tp;
1084 
1085  boot->st = 0;
1086  boot->es = 0;
1087 
1088  // Retrieve the slave assignment for the node.
1089  boot->assignment = co_dev_get_val_u32(boot->dev, 0x1f81, boot->id);
1090 
1091  // Find the consumer heartbeat time for the node.
1092  boot->ms = 0;
1093  co_obj_t *obj_1016 = co_dev_find_obj(boot->dev, 0x1016);
1094  if (obj_1016) {
1095  co_unsigned8_t n = co_obj_get_val_u8(obj_1016, 0x00);
1096  for (size_t i = 1; i <= n; i++) {
1097  co_unsigned32_t val =
1098  co_obj_get_val_u32(obj_1016, i & 0xff);
1099  if (((val >> 16) & 0x7f) == boot->id)
1100  boot->ms = val & 0xffff;
1101  }
1102  }
1103 
1104  // Abort the 'boot slave' process if the slave is not in the network
1105  // list.
1106  if (!(boot->assignment & 0x01)) {
1107  boot->es = 'A';
1108  return co_nmt_boot_abort_state;
1109  }
1110 
1111  if (!(boot->assignment & 0x04))
1112  // Skip booting and start the error control service.
1113  return co_nmt_boot_ec_state;
1114 
1116 }
1117 
1118 static co_nmt_boot_state_t *
1120 {
1121  assert(boot);
1122 
1123  can_recv_stop(boot->recv);
1124  can_timer_stop(boot->timer);
1125 
1126  // If the node is already operational, end the 'boot slave' process with
1127  // error status L.
1128  if (!boot->es && (boot->st & ~CO_NMT_ST_TOGGLE) == CO_NMT_ST_START)
1129  boot->es = 'L';
1130 
1131  // Retry on error status B (see Fig. 4 in CiA 302-2 version 4.1.0).
1132  if (boot->es == 'B') {
1133  int wait = 1;
1134  if (boot->assignment & 0x08) {
1135  // Obtain the time (in milliseconds) the master will
1136  // wait for a mandatory slave to boot.
1137  co_unsigned32_t boot_time = co_dev_get_val_u32(
1138  boot->dev, 0x1f89, 0x00);
1139  // Check if this time has elapsed.
1140  if (boot_time) {
1141  struct timespec now = { 0, 0 };
1142  can_net_get_time(boot->net, &now);
1143  wait = timespec_diff_msec(&now, &boot->start)
1144  < boot_time;
1145  }
1146  }
1147  // If the slave is not mandatory, or the boot time has not yet
1148  // elapsed, wait asynchronously for a while and retry the 'boot
1149  // slave' process.
1150  if (wait) {
1151  can_timer_timeout(boot->timer, boot->net,
1153  return co_nmt_boot_wait_state;
1154  }
1155  }
1156 
1157  return co_nmt_boot_error_state;
1158 }
1159 
1160 static co_nmt_boot_state_t *
1162 {
1163  (void)boot;
1164 
1165  return co_nmt_boot_wait_state;
1166 }
1167 
1168 static void
1170 {
1171  assert(boot);
1172 
1173  co_nmt_boot_con(boot->nmt, boot->id, boot->st, boot->es);
1174 }
1175 
1176 static co_nmt_boot_state_t *
1178 {
1179  assert(boot);
1180 
1181  boot->es = 'B';
1182 
1183  // The device type check may follow an NMT 'reset communication'
1184  // command, in which case we may have to give the slave some time to
1185  // complete the state change. Start the first SDO request by simulating
1186  // a timeout.
1187  boot->retry = LELY_CO_NMT_BOOT_SDO_RETRY + 1;
1189  boot, CO_SDO_AC_TIMEOUT, NULL, 0);
1190 }
1191 
1192 static co_nmt_boot_state_t *
1194  const void *ptr, size_t n)
1195 {
1196  assert(boot);
1197 
1198  // Retry the SDO request on timeout (this includes the first attempt).
1199  if (ac == CO_SDO_AC_TIMEOUT && boot->retry--) {
1200  // Read the device type of the slave (object 1000).
1201  if (co_nmt_boot_up(boot, 0x1000, 0x00) == -1)
1202  return co_nmt_boot_abort_state;
1203  return NULL;
1204  } else if (ac) {
1205 #if !LELY_NO_STDIO
1206  diag(DIAG_ERROR, 0,
1207  "SDO abort code %08" PRIX32
1208  " received on upload request of object 1000 (Device type) to node %02X: %s",
1209  ac, boot->id, co_sdo_ac2str(ac));
1210 #endif
1211  return co_nmt_boot_abort_state;
1212  }
1213 
1214  // If the expected device type (sub-object 1F84:ID) is 0, skip the check
1215  // and proceed with the vendor ID.
1216  co_unsigned32_t device_type =
1217  co_dev_get_val_u32(boot->dev, 0x1f84, boot->id);
1218  if (device_type && !co_nmt_boot_chk(boot, 0x1f84, boot->id, ptr, n)) {
1219  boot->es = 'C';
1220  return co_nmt_boot_abort_state;
1221  }
1222 
1223  can_recv_stop(boot->recv);
1225 }
1226 
1227 static co_nmt_boot_state_t *
1229 {
1230  assert(boot);
1231 
1232  // If the expected vendor ID (sub-object 1F85:ID) is 0, skip the check
1233  // and proceed with the product code.
1234  co_unsigned32_t vendor_id =
1235  co_dev_get_val_u32(boot->dev, 0x1f85, boot->id);
1236  if (!vendor_id)
1238 
1239  boot->es = 'D';
1240 
1241  // Read the vendor ID of the slave (sub-object 1018:01).
1242  if (co_nmt_boot_up(boot, 0x1018, 0x01) == -1)
1243  return co_nmt_boot_abort_state;
1244 
1245  return NULL;
1246 }
1247 
1248 static co_nmt_boot_state_t *
1250  const void *ptr, size_t n)
1251 {
1252  assert(boot);
1253 
1254 #if !LELY_NO_STDIO
1255  if (ac)
1256  diag(DIAG_ERROR, 0,
1257  "SDO abort code %08" PRIX32
1258  " received on upload request of sub-object 1018:01 (Vendor-ID) to node %02X: %s",
1259  ac, boot->id, co_sdo_ac2str(ac));
1260 #endif
1261 
1262  if (ac || !co_nmt_boot_chk(boot, 0x1f85, boot->id, ptr, n))
1263  return co_nmt_boot_abort_state;
1264 
1266 }
1267 
1268 static co_nmt_boot_state_t *
1270 {
1271  assert(boot);
1272 
1273  // If the expected product code (sub-object 1F86:ID) is 0, skip the
1274  // check and proceed with the revision number.
1275  co_unsigned32_t product_code =
1276  co_dev_get_val_u32(boot->dev, 0x1f86, boot->id);
1277  if (!product_code)
1279 
1280  boot->es = 'M';
1281 
1282  // Read the product code of the slave (sub-object 1018:02).
1283  if (co_nmt_boot_up(boot, 0x1018, 0x02) == -1)
1284  return co_nmt_boot_abort_state;
1285 
1286  return NULL;
1287 }
1288 
1289 static co_nmt_boot_state_t *
1291  const void *ptr, size_t n)
1292 {
1293  assert(boot);
1294 
1295 #if !LELY_NO_STDIO
1296  if (ac)
1297  diag(DIAG_ERROR, 0,
1298  "SDO abort code %08" PRIX32
1299  " received on upload request of sub-object 1018:02 (Product code) to node %02X: %s",
1300  ac, boot->id, co_sdo_ac2str(ac));
1301 #endif
1302 
1303  if (ac || !co_nmt_boot_chk(boot, 0x1f86, boot->id, ptr, n))
1304  return co_nmt_boot_abort_state;
1305 
1307 }
1308 
1309 static co_nmt_boot_state_t *
1311 {
1312  assert(boot);
1313 
1314  // If the expected revision number (sub-object 1F87:ID) is 0, skip the
1315  // check and proceed with the serial number.
1316  co_unsigned32_t revision =
1317  co_dev_get_val_u32(boot->dev, 0x1f87, boot->id);
1318  if (!revision)
1320 
1321  boot->es = 'N';
1322 
1323  // Read the revision number of the slave (sub-object 1018:03).
1324  if (co_nmt_boot_up(boot, 0x1018, 0x03) == -1)
1325  return co_nmt_boot_abort_state;
1326 
1327  return NULL;
1328 }
1329 
1330 static co_nmt_boot_state_t *
1332  const void *ptr, size_t n)
1333 {
1334  assert(boot);
1335 
1336 #if !LELY_NO_STDIO
1337  if (ac)
1338  diag(DIAG_ERROR, 0,
1339  "SDO abort code %08" PRIX32
1340  " received on upload request of sub-object 1018:03 (Revision number) to node %02X: %s",
1341  ac, boot->id, co_sdo_ac2str(ac));
1342 #endif
1343 
1344  if (ac || !co_nmt_boot_chk(boot, 0x1f87, boot->id, ptr, n))
1345  return co_nmt_boot_abort_state;
1346 
1348 }
1349 
1350 static co_nmt_boot_state_t *
1352 {
1353  assert(boot);
1354 
1355  // If the expected serial number (sub-object 1F88:ID) is 0, skip the
1356  // check and proceed to 'check node state'.
1357  co_unsigned32_t serial_nr =
1358  co_dev_get_val_u32(boot->dev, 0x1f88, boot->id);
1359  if (!serial_nr)
1361 
1362  boot->es = 'O';
1363 
1364  // Read the serial number of the slave (sub-object 1018:04).
1365  if (co_nmt_boot_up(boot, 0x1018, 0x04) == -1)
1366  return co_nmt_boot_abort_state;
1367 
1368  return NULL;
1369 }
1370 
1371 static co_nmt_boot_state_t *
1373  const void *ptr, size_t n)
1374 {
1375  assert(boot);
1376 
1377 #if !LELY_NO_STDIO
1378  if (ac)
1379  diag(DIAG_ERROR, 0,
1380  "SDO abort code %08" PRIX32
1381  " received on upload request of sub-object 1018:04 (Serial number) to node %02X: %s",
1382  ac, boot->id, co_sdo_ac2str(ac));
1383 #endif
1384 
1385  if (ac || !co_nmt_boot_chk(boot, 0x1f88, boot->id, ptr, n))
1386  return co_nmt_boot_abort_state;
1387 
1389 }
1390 
1391 static co_nmt_boot_state_t *
1393 {
1394  assert(boot);
1395 
1396  // If the keep-alive bit is set, check the node state.
1397  if (boot->assignment & 0x10) {
1398  int ms;
1399  if (boot->ms) {
1400  boot->es = 'E';
1401  ms = boot->ms;
1402  } else {
1403  boot->es = 'F';
1404 #if LELY_NO_CO_NG
1405  return co_nmt_boot_abort_state;
1406 #else
1408  // If we're not a heartbeat consumer, start node
1409  // guarding by sending the first RTR.
1410  co_nmt_boot_send_rtr(boot);
1411 #endif
1412  }
1413 
1414  // Start the CAN frame receiver for the heartbeat or node guard
1415  // message.
1416  can_recv_start(boot->recv, boot->net, CO_NMT_EC_CANID(boot->id),
1417  0);
1418  // Start the CAN timer in case we do not receive a heartbeat
1419  // indication or a node guard confirmation.
1420  can_timer_timeout(boot->timer, boot->net, ms);
1421 
1422  return NULL;
1423  }
1424 
1425  return co_nmt_boot_chk_sw_state;
1426 }
1427 
1428 static co_nmt_boot_state_t *
1430 {
1431  assert(boot);
1432  assert(msg);
1433  assert(msg->id == (uint_least32_t)CO_NMT_EC_CANID(boot->id));
1434 
1435  can_timer_stop(boot->timer);
1436 
1437  if (msg->len >= 1) {
1438  boot->st = msg->data[0];
1439  if ((boot->st & ~CO_NMT_ST_TOGGLE) == CO_NMT_ST_START)
1440  // If the node is already operational, skip the 'check
1441  // and update software version' and 'check
1442  // configuration' steps and proceed immediately to
1443  // 'start error control service'.
1444  return co_nmt_boot_ec_state;
1445  }
1446  boot->st = 0;
1447  // If the node is not operational, send the NMT 'reset
1448  // communication' command and proceed as if the keep-alive bit
1449  // was not set.
1450  co_nmt_cs_req(boot->nmt, CO_NMT_CS_RESET_COMM, boot->id);
1452 }
1453 
1454 static co_nmt_boot_state_t *
1455 co_nmt_boot_chk_node_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
1456 {
1457  (void)boot;
1458  (void)tp;
1459 
1460  return co_nmt_boot_abort_state;
1461 }
1462 
1463 static void
1465 {
1466  assert(boot);
1467 
1468  can_recv_stop(boot->recv);
1469 }
1470 
1471 static co_nmt_boot_state_t *
1473 {
1474  assert(boot);
1475 
1476  // Start the CAN frame receiver for the boot-up message.
1477  can_recv_start(boot->recv, boot->net, CO_NMT_EC_CANID(boot->id), 0);
1478  // Wait until we receive a boot-up message.
1480  boot->timer, boot->net, LELY_CO_NMT_BOOT_RESET_TIMEOUT);
1481 
1482  return NULL;
1483 }
1484 
1485 static co_nmt_boot_state_t *
1487 {
1488  (void)boot;
1489  (void)msg;
1490 
1491  can_recv_stop(boot->recv);
1492  return co_nmt_boot_chk_sw_state;
1493 }
1494 
1495 static co_nmt_boot_state_t *
1496 co_nmt_boot_reset_comm_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
1497 {
1498  (void)boot;
1499  (void)tp;
1500 
1501  return co_nmt_boot_abort_state;
1502 }
1503 
1504 static co_nmt_boot_state_t *
1506 {
1507  assert(boot);
1508 
1509  if (boot->assignment & 0x20) {
1510  boot->es = 'G';
1511 
1512  // Abort if the expected program software identification
1513  // (sub-object 1F55:ID) is 0.
1514  co_unsigned32_t sw_id =
1515  co_dev_get_val_u32(boot->dev, 0x1f55, boot->id);
1516  if (!sw_id)
1517  return co_nmt_boot_abort_state;
1518 
1519  // The software version check may follow an NMT 'reset
1520  // communication' command, in which case we may have to give the
1521  // slave some time to complete the state change. Start the first
1522  // SDO request by simulating a timeout.
1523  boot->retry = LELY_CO_NMT_BOOT_SDO_RETRY + 1;
1525  boot, CO_SDO_AC_TIMEOUT, NULL, 0);
1526  }
1527 
1528  // Continue with the 'check configuration' step if the software version
1529  // check is not necessary.
1531 }
1532 
1533 static co_nmt_boot_state_t *
1535  const void *ptr, size_t n)
1536 {
1537  assert(boot);
1538 
1539  // Retry the SDO request on timeout (this includes the first attempt).
1540  if (ac == CO_SDO_AC_TIMEOUT && boot->retry--) {
1541  // Read the program software identification of the slave
1542  // (sub-object 1F56:01).
1543  if (co_nmt_boot_up(boot, 0x1f56, 0x01) == -1)
1544  return co_nmt_boot_abort_state;
1545  return NULL;
1546  } else if (ac) {
1547 #if !LELY_NO_STDIO
1548  diag(DIAG_ERROR, 0,
1549  "SDO abort code %08" PRIX32
1550  " received on upload request of sub-object 1F56:01 (Program software identification) to node %02X: %s",
1551  ac, boot->id, co_sdo_ac2str(ac));
1552 #endif
1553  return co_nmt_boot_abort_state;
1554  }
1555 
1556  // If the program software identification matches the expected value,
1557  // proceed to 'check configuration'.
1558  if (co_nmt_boot_chk(boot, 0x1f55, boot->id, ptr, n))
1560 
1561  // Do not update the software if software update (bit 6) is not allowed
1562  // or if the keep-alive bit (bit 4) is set.
1563  if ((boot->assignment & 0x50) != 0x40) {
1564  boot->es = 'H';
1565  return co_nmt_boot_abort_state;
1566  }
1567 
1568  boot->es = 'I';
1569 
1571 }
1572 
1573 static co_nmt_boot_state_t *
1575 {
1576  assert(boot);
1577 
1578  // Read the program control of the slave (sub-object 1F51:01).
1579  if (co_nmt_boot_up(boot, 0x1f51, 0x01) == -1)
1580  return co_nmt_boot_abort_state;
1581 
1582  return NULL;
1583 }
1584 
1585 static co_nmt_boot_state_t *
1587 {
1588 #if LELY_NO_STDIO
1589  (void)boot;
1590 #else
1591  assert(boot);
1592 #endif
1593 
1594  // The download SDO request may be unconfirmed on some devices since it
1595  // stops the program on the slave (and may cause a restart of the
1596  // bootloader). We therefore ignore timeouts.
1597  if (ac && ac != CO_SDO_AC_TIMEOUT) {
1598 #if !LELY_NO_STDIO
1599  diag(DIAG_ERROR, 0,
1600  "SDO abort code %08" PRIX32
1601  " received on download request of sub-object 1F51:01 (Program control) to node %02X: %s",
1602  ac, boot->id, co_sdo_ac2str(ac));
1603 #endif
1604  return co_nmt_boot_abort_state;
1605  }
1606 
1608 }
1609 
1610 static co_nmt_boot_state_t *
1612  const void *ptr, size_t n)
1613 {
1614  assert(boot);
1615 
1616  // If the value is already 0 (Program stopped), do not write a 0 (Stop
1617  // program), but skip to the 'clear program' state.
1618  co_unsigned8_t val = 0;
1619  // clang-format off
1620  if (!ac && co_val_read(CO_DEFTYPE_UNSIGNED8, &val, ptr,
1621  (const uint_least8_t *)ptr + n) && !val)
1622  // clang-format on
1624 
1625  // Write a 0 (Stop program) to the program control of the slave
1626  // (sub-object 1F51:01).
1627  // clang-format off
1628  if (co_nmt_boot_dn(boot, 0x1f51, 0x01, CO_DEFTYPE_UNSIGNED8,
1629  &(co_unsigned8_t){ 0 }) == -1)
1630  // clang-format on
1631  return co_nmt_boot_abort_state;
1632 
1633  return NULL;
1634 }
1635 
1636 static co_nmt_boot_state_t *
1638 {
1639  assert(boot);
1640 
1641  // The 'clear program' command follows the 'stop program' command, which
1642  // may have triggered a reboot of the slave. In that case we may have to
1643  // give the slave some time to finish booting. Start the first SDO
1644  // request by simulating a timeout.
1645  boot->retry = LELY_CO_NMT_BOOT_SDO_RETRY + 1;
1647 }
1648 
1649 static co_nmt_boot_state_t *
1651 {
1652  assert(boot);
1653 
1654  // Retry the SDO request on timeout.
1655  if (ac == CO_SDO_AC_TIMEOUT && boot->retry--) {
1656  // Write a 3 (Clear program) to the program control of the slave
1657  // (sub-object 1F51:01).
1658  // clang-format off
1659  if (co_nmt_boot_dn(boot, 0x1f51, 0x01, CO_DEFTYPE_UNSIGNED8,
1660  &(co_unsigned8_t){ 3 }) == -1)
1661  // clang-format on
1662  return co_nmt_boot_abort_state;
1663  return NULL;
1664  } else if (ac) {
1665 #if !LELY_NO_STDIO
1666  diag(DIAG_ERROR, 0,
1667  "SDO abort code %08" PRIX32
1668  " received on download request of sub-object 1F51:01 (Program control) to node %02X: %s",
1669  ac, boot->id, co_sdo_ac2str(ac));
1670 #endif
1671  return co_nmt_boot_abort_state;
1672  }
1673 
1675 }
1676 
1677 static co_nmt_boot_state_t *
1679 {
1680  assert(boot);
1681 
1682  co_sub_t *sub = co_dev_find_sub(boot->dev, 0x1f58, boot->id);
1683  if (!sub)
1684  return co_nmt_boot_abort_state;
1685 
1686  // Upload the program data.
1687  struct co_sdo_req *req = &boot->req;
1688  co_sdo_req_clear(req);
1689  co_unsigned32_t ac = co_sub_up_ind(sub, req);
1690  if (ac || !co_sdo_req_first(req) || !co_sdo_req_last(req)) {
1691 #if !LELY_NO_STDIO
1692  if (ac)
1693  diag(DIAG_ERROR, 0,
1694  "SDO abort code %08" PRIX32
1695  " on upload request of object 1F58:%02X (Program data): %s",
1696  ac, boot->id, co_sdo_ac2str(ac));
1697 #endif
1698  return co_nmt_boot_abort_state;
1699  }
1700 
1701  // The 'clear program' step may take some time to complete, causing an
1702  // immediate 'download program' to generate a timeout. Start the first
1703  // attempt by simulating a timeout.
1704  boot->retry = LELY_CO_NMT_BOOT_SDO_RETRY + 1;
1706 }
1707 
1708 static co_nmt_boot_state_t *
1710 {
1711  (void)boot;
1712 
1713  // Retry the SDO request on timeout (this includes the first attempt).
1714  if (ac == CO_SDO_AC_TIMEOUT && boot->retry--) {
1715  struct co_sdo_req *req = &boot->req;
1716  // Write the program data (sub-object 1F58:ID) to the program
1717  // data of the slave (sub-object 1F50:01) using SDO block
1718  // transfer.
1719  // clang-format off
1720  if (co_csdo_blk_dn_req(boot->sdo, 0x1f50, 0x01, req->buf,
1721  req->size, &co_nmt_boot_dn_con, boot) == -1)
1722  // clang-format on
1723  return co_nmt_boot_abort_state;
1724  return NULL;
1725  } else if (ac) {
1726  // If SDO block transfer is not supported, fall back to SDO
1727  // segmented transfer.
1729  }
1730 
1732 }
1733 
1734 static co_nmt_boot_state_t *
1736 {
1737  assert(boot);
1738 
1739  // If SDO block transfer is not supported, we may still have to wait for
1740  // the 'clear program' step to complete before successfully doing a
1741  // segmented SDO transfer. Start the first attempt by simulating a
1742  // timeout.
1743  boot->retry = LELY_CO_NMT_BOOT_SDO_RETRY + 1;
1745 }
1746 
1747 static co_nmt_boot_state_t *
1749 {
1750  assert(boot);
1751 
1752  // Retry the SDO request on timeout (this includes the first attempt).
1753  if (ac == CO_SDO_AC_TIMEOUT && boot->retry--) {
1754  struct co_sdo_req *req = &boot->req;
1755  // Write the program data (sub-object 1F58:ID) to the program
1756  // data of the slave (sub-object 1F50:01) using SDO segmented
1757  // transfer.
1758  // clang-format off
1759  if (co_csdo_dn_req(boot->sdo, 0x1f50, 0x01, req->buf, req->size,
1760  &co_nmt_boot_dn_con, boot) == -1)
1761  // clang-format on
1762  return co_nmt_boot_abort_state;
1763  return NULL;
1764  } else if (ac) {
1765 #if !LELY_NO_STDIO
1766  diag(DIAG_ERROR, 0,
1767  "SDO abort code %08" PRIX32
1768  " received on download request of sub-object 1F50:01 (Program data) to node %02X: %s",
1769  ac, boot->id, co_sdo_ac2str(ac));
1770 #endif
1771  return co_nmt_boot_abort_state;
1772  }
1773 
1775 }
1776 
1777 static co_nmt_boot_state_t *
1779 {
1780  assert(boot);
1781 
1782  // Wait for a while before checking the flash status indication.
1784  boot->timer, boot->net, LELY_CO_NMT_BOOT_CHECK_TIMEOUT);
1785 
1786  return NULL;
1787 }
1788 
1789 static co_nmt_boot_state_t *
1790 co_nmt_boot_wait_flash_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
1791 {
1792  assert(boot);
1793  (void)tp;
1794 
1795  // Read the flash status indication of the slave (sub-object 1F57:01).
1796  if (co_nmt_boot_up(boot, 0x1f57, 0x01) == -1)
1797  return co_nmt_boot_abort_state;
1798 
1799  return NULL;
1800 }
1801 
1802 static co_nmt_boot_state_t *
1804  const void *ptr, size_t n)
1805 {
1806 #if LELY_NO_STDIO
1807  (void)boot;
1808  (void)ac;
1809 #else
1810  assert(boot);
1811 
1812  if (ac)
1813  diag(DIAG_ERROR, 0,
1814  "SDO abort code %08" PRIX32
1815  " received on upload request of sub-object 1F57:01 (Flash status indication) to node %02X: %s",
1816  ac, boot->id, co_sdo_ac2str(ac));
1817 #endif
1818 
1819  // If the flash status indication is not valid, try again.
1820  co_unsigned32_t val = 0;
1821  // clang-format off
1822  if (!co_val_read(CO_DEFTYPE_UNSIGNED32, &val, ptr,
1823  (const uint_least8_t *)ptr + n) || (val & 0x01))
1824  // clang-format on
1826 
1827  co_unsigned8_t st = (val >> 1) & 0x7f;
1828  switch (st) {
1829  case 0: return co_nmt_boot_chk_prog_state;
1830  case 1:
1831  diag(DIAG_ERROR, 0,
1832  "flash status identification %d: No valid program available",
1833  st);
1834  break;
1835  case 2:
1836  diag(DIAG_ERROR, 0,
1837  "flash status identification %d: Data format unknown",
1838  st);
1839  break;
1840  case 3:
1841  diag(DIAG_ERROR, 0,
1842  "flash status identification %d: Data format error or data CRC error",
1843  st);
1844  break;
1845  case 4:
1846  diag(DIAG_ERROR, 0,
1847  "flash status identification %d: Flash not cleared before write",
1848  st);
1849  break;
1850  case 5:
1851  diag(DIAG_ERROR, 0,
1852  "flash status identification %d: Flash write error",
1853  st);
1854  break;
1855  case 6:
1856  diag(DIAG_ERROR, 0,
1857  "flash status identification %d: General address error",
1858  st);
1859  break;
1860  case 7:
1861  diag(DIAG_ERROR, 0,
1862  "flash status identification %d: Flash secured (= write access currently forbidden)",
1863  st);
1864  break;
1865  case 63:
1866  diag(DIAG_ERROR, 0,
1867  "flash status identification %d: Unspecified error",
1868  st);
1869  break;
1870  default:
1871 #if !LELY_NO_STDIO
1872  if (st > 63)
1873  diag(DIAG_ERROR, 0,
1874  "flash status identification %d: Manufacturer-specific error: 0x%08" PRIX32
1875  "",
1876  st, (val >> 16) & 0xffff);
1877 #endif
1878  break;
1879  }
1880 
1881  return co_nmt_boot_abort_state;
1882 }
1883 
1884 static co_nmt_boot_state_t *
1886 {
1887  assert(boot);
1888 
1889  // Read the program software identification of the slave (sub-object
1890  // 1F56:01).
1891  if (co_nmt_boot_up(boot, 0x1f56, 0x01) == -1)
1892  return co_nmt_boot_abort_state;
1893 
1894  return NULL;
1895 }
1896 
1897 static co_nmt_boot_state_t *
1899  const void *ptr, size_t n)
1900 {
1901  assert(boot);
1902 
1903 #if !LELY_NO_STDIO
1904  if (ac)
1905  diag(DIAG_ERROR, 0,
1906  "SDO abort code %08" PRIX32
1907  " received on upload request of sub-object 1F56:01 (Program software identification) to node %02X: %s",
1908  ac, boot->id, co_sdo_ac2str(ac));
1909 #endif
1910 
1911  if (ac || !co_nmt_boot_chk(boot, 0x1f55, boot->id, ptr, n))
1912  return co_nmt_boot_abort_state;
1913 
1915 }
1916 
1917 static co_nmt_boot_state_t *
1919 {
1920  assert(boot);
1921 
1922  // Write a 1 (Start program) to the program control of the slave
1923  // (sub-object 1F51:01).
1924  // clang-format off
1925  if (co_nmt_boot_dn(boot, 0x1f51, 0x01, CO_DEFTYPE_UNSIGNED8,
1926  &(co_unsigned8_t){ 1 }) == -1)
1927  // clang-format on
1928  return co_nmt_boot_abort_state;
1929 
1930  return NULL;
1931 }
1932 
1933 static co_nmt_boot_state_t *
1935 {
1936 #if LELY_NO_STDIO
1937  (void)boot;
1938 #else
1939  assert(boot);
1940 #endif
1941 
1942  // The download SDO request may be unconfirmed on some devices since it
1943  // starts the program on the slave (and may cause a restart of the
1944  // program). We therefore ignore timeouts.
1945  if (ac && ac != CO_SDO_AC_TIMEOUT) {
1946 #if !LELY_NO_STDIO
1947  diag(DIAG_ERROR, 0,
1948  "SDO abort code %08" PRIX32
1949  " received on download request of sub-object 1F51:01 (Program control) to node %02X: %s",
1950  ac, boot->id, co_sdo_ac2str(ac));
1951 #endif
1952  return co_nmt_boot_abort_state;
1953  }
1954 
1956 }
1957 
1958 static co_nmt_boot_state_t *
1960 {
1961  assert(boot);
1962 
1963  // Wait for a while before checking the program control.
1965  boot->timer, boot->net, LELY_CO_NMT_BOOT_CHECK_TIMEOUT);
1966 
1967  return NULL;
1968 }
1969 
1970 static co_nmt_boot_state_t *
1971 co_nmt_boot_wait_prog_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
1972 {
1973  assert(boot);
1974  (void)tp;
1975 
1976  // The 'start program' step may take some time to complete, causing an
1977  // immediate SDO upload request to generate a timeout. Start the first
1978  // attempt by simulating a timeout.
1979  boot->retry = LELY_CO_NMT_BOOT_SDO_RETRY + 1;
1981  boot, CO_SDO_AC_TIMEOUT, NULL, 0);
1982 
1983  return NULL;
1984 }
1985 
1986 static co_nmt_boot_state_t *
1988  const void *ptr, size_t n)
1989 {
1990  assert(boot);
1991 
1992  // Retry the SDO request on timeout (this includes the first attempt).
1993  if (ac == CO_SDO_AC_TIMEOUT && boot->retry--) {
1994  // Read the program control of the slave (sub-object 1F51:01).
1995  if (co_nmt_boot_up(boot, 0x1f51, 0x01) == -1)
1996  return co_nmt_boot_abort_state;
1997  return NULL;
1998  } else if (ac) {
1999 #if !LELY_NO_STDIO
2000  diag(DIAG_ERROR, 0,
2001  "SDO abort code %08" PRIX32
2002  " received on upload request of sub-object 1F51:01 (Program control) to node %02X: %s",
2003  ac, boot->id, co_sdo_ac2str(ac));
2004 #endif
2005  return co_nmt_boot_abort_state;
2006  }
2007 
2008  // If the program control differs from 'Program started', try again.
2009  co_unsigned8_t val = 0;
2010  const uint_least8_t *const begin = ptr;
2011  const uint_least8_t *const end = begin != NULL ? begin + n : NULL;
2012  // clang-format off
2013  if (!co_val_read(CO_DEFTYPE_UNSIGNED8, &val, begin, end) || val != 1)
2014  // clang-format on
2016 
2018 }
2019 
2020 static co_nmt_boot_state_t *
2022 {
2023  assert(boot);
2024 
2025  boot->es = 'J';
2026 
2027  // If the expected configuration date (sub-object 1F26:ID) or time
2028  // (sub-object 1F27:ID) are not configured, proceed to 'update
2029  // configuration'.
2030  co_unsigned32_t cfg_date =
2031  co_dev_get_val_u32(boot->dev, 0x1f26, boot->id);
2032  co_unsigned32_t cfg_time =
2033  co_dev_get_val_u32(boot->dev, 0x1f27, boot->id);
2034  if (!cfg_date || !cfg_time)
2035  return co_nmt_boot_up_cfg_state;
2036 
2037  // The configuration check may follow an NMT 'reset communication'
2038  // command (if the 'check software version' step was skipped), in which
2039  // case we may have to give the slave some time to complete the state
2040  // change. Start the first SDO request by simulating a timeout.
2041  boot->retry = LELY_CO_NMT_BOOT_SDO_RETRY + 1;
2043  boot, CO_SDO_AC_TIMEOUT, NULL, 0);
2044 }
2045 
2046 static co_nmt_boot_state_t *
2048  const void *ptr, size_t n)
2049 {
2050  assert(boot);
2051 
2052  // Retry the SDO request on timeout (this includes the first attempt).
2053  if (ac == CO_SDO_AC_TIMEOUT && boot->retry--) {
2054  // Read the configuration date of the slave (sub-object
2055  // 1020:01).
2056  if (co_nmt_boot_up(boot, 0x1020, 0x01) == -1)
2057  return co_nmt_boot_abort_state;
2058  return NULL;
2059 #if !LELY_NO_STDIO
2060  } else if (ac) {
2061  diag(DIAG_ERROR, 0,
2062  "SDO abort code %08" PRIX32
2063  " received on upload request of sub-object 1020:01 (Configuration date) to node %02X: %s",
2064  ac, boot->id, co_sdo_ac2str(ac));
2065 #endif
2066  }
2067 
2068  // If the configuration date does not match the expected value, skip
2069  // checking the time and proceed to 'update configuration'.
2070  if (ac || !co_nmt_boot_chk(boot, 0x1f26, boot->id, ptr, n))
2071  return co_nmt_boot_up_cfg_state;
2072 
2073  // Read the configuration time of the slave (sub-object 1020:02).
2074  if (co_nmt_boot_up(boot, 0x1020, 0x02) == -1)
2075  return co_nmt_boot_abort_state;
2076 
2078 }
2079 
2080 static co_nmt_boot_state_t *
2082  const void *ptr, size_t n)
2083 {
2084  assert(boot);
2085 
2086 #if !LELY_NO_STDIO
2087  if (ac)
2088  diag(DIAG_ERROR, 0,
2089  "SDO abort code %08" PRIX32
2090  " received on upload request of sub-object 1020:02 (Configuration time) to node %02X: %s",
2091  ac, boot->id, co_sdo_ac2str(ac));
2092 #endif
2093 
2094  // If the configuration time does not match the expected value, proceed
2095  // to 'update configuration'.
2096  if (ac || !co_nmt_boot_chk(boot, 0x1f27, boot->id, ptr, n))
2097  return co_nmt_boot_up_cfg_state;
2098 
2099  return co_nmt_boot_ec_state;
2100 }
2101 
2102 static co_nmt_boot_state_t *
2104 {
2105  assert(boot);
2106 
2107  boot->es = 'J';
2108 
2109  // clang-format off
2110  if (co_nmt_cfg_req(boot->nmt, boot->id, boot->timeout,
2111  &co_nmt_boot_cfg_con, boot) == -1)
2112  // clang-format on
2113  return co_nmt_boot_abort_state;
2114 
2115  return NULL;
2116 }
2117 
2118 static co_nmt_boot_state_t *
2120 {
2121 #if LELY_NO_STDIO
2122  (void)boot;
2123 #else
2124  assert(boot);
2125 #endif
2126 
2127  if (ac) {
2128 #if !LELY_NO_STDIO
2129  diag(DIAG_ERROR, 0,
2130  "SDO abort code %08" PRIX32
2131  " received while updating the configuration of node %02X: %s",
2132  ac, boot->id, co_sdo_ac2str(ac));
2133 #endif
2134  return co_nmt_boot_abort_state;
2135  }
2136 
2137  return co_nmt_boot_ec_state;
2138 }
2139 
2140 static co_nmt_boot_state_t *
2142 {
2143  assert(boot);
2144 
2145  if (boot->ms) {
2146  boot->es = 'K';
2147  // Start the CAN frame receiver for heartbeat messages.
2148  can_recv_start(boot->recv, boot->net, CO_NMT_EC_CANID(boot->id),
2149  0);
2150  // Wait for the first heartbeat indication.
2151  can_timer_timeout(boot->timer, boot->net, boot->ms);
2152  return NULL;
2153 #if !LELY_NO_CO_NG
2154  } else if (boot->assignment & 0x01) {
2155  // If the guard time is non-zero, start node guarding by sending
2156  // the first RTR, but do not wait for the response.
2157  co_unsigned16_t gt = (boot->assignment >> 16) & 0xffff;
2158  if (gt)
2159  co_nmt_boot_send_rtr(boot);
2160 #endif
2161  }
2162 
2163  boot->es = 0;
2164  return co_nmt_boot_abort_state;
2165 }
2166 
2167 static co_nmt_boot_state_t *
2169 {
2170  assert(boot);
2171  assert(msg);
2172  assert(msg->id == (uint_least32_t)CO_NMT_EC_CANID(boot->id));
2173 
2174  if (msg->len >= 1) {
2175  co_unsigned8_t st = msg->data[0];
2176  // Do not consider a boot-up message to be a heartbeat message.
2177  if (st == CO_NMT_ST_BOOTUP)
2178  return NULL;
2179  boot->st = st;
2180  boot->es = 0;
2181  }
2182 
2183  return co_nmt_boot_abort_state;
2184 }
2185 
2186 static co_nmt_boot_state_t *
2187 co_nmt_boot_ec_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
2188 {
2189  (void)boot;
2190  (void)tp;
2191 
2192  return co_nmt_boot_abort_state;
2193 }
2194 
2195 static int
2196 co_nmt_boot_dn(co_nmt_boot_t *boot, co_unsigned16_t idx, co_unsigned8_t subidx,
2197  co_unsigned16_t type, const void *val)
2198 {
2199  assert(boot);
2200 
2201  return co_csdo_dn_val_req(boot->sdo, idx, subidx, type, val,
2202  &co_nmt_boot_dn_con, boot);
2203 }
2204 
2205 static int
2206 co_nmt_boot_up(co_nmt_boot_t *boot, co_unsigned16_t idx, co_unsigned8_t subidx)
2207 {
2208  assert(boot);
2209 
2210  return co_csdo_up_req(
2211  boot->sdo, idx, subidx, &co_nmt_boot_up_con, boot);
2212 }
2213 
2214 static int
2215 co_nmt_boot_chk(co_nmt_boot_t *boot, co_unsigned16_t idx, co_unsigned8_t subidx,
2216  const void *ptr, size_t n)
2217 {
2218  assert(boot);
2219 
2220  co_sub_t *sub = co_dev_find_sub(boot->dev, idx, subidx);
2221  if (!sub)
2222  return 0;
2223  co_unsigned16_t type = co_sub_get_type(sub);
2224  assert(!co_type_is_array(type));
2225 
2226  union co_val val;
2227  if (!co_val_read(type, &val, ptr, (const uint_least8_t *)ptr + n))
2228  return 0;
2229 
2230  return !co_val_cmp(type, &val, co_sub_get_val(sub));
2231 }
2232 
2233 #if !LELY_NO_CO_NG
2234 static int
2236 {
2237  assert(boot);
2238 
2239  struct can_msg msg = CAN_MSG_INIT;
2240  msg.id = CO_NMT_EC_CANID(boot->id);
2241  msg.flags |= CAN_FLAG_RTR;
2242 
2243  return can_net_send(boot->net, &msg);
2244 }
2245 #endif
2246 
2247 #endif // !LELY_NO_CO_MASTER && !LELY_NO_CO_NMT_BOOT
@ CAN_FLAG_RTR
The Remote Transmission Request (RTR) flag (unavailable in CAN FD format frames).
Definition: msg.h:48
#define CAN_MSG_INIT
The static initializer for can_msg.
Definition: msg.h:113
This header file is part of the CANopen library; it contains the device description declarations.
co_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
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
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:997
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:1294
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:1219
void co_csdo_set_timeout(co_csdo_t *sdo, int timeout)
Sets the timeout of a Client-SDO.
Definition: csdo.c:1142
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:1313
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:1184
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
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:1164
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:1241
void co_csdo_destroy(co_csdo_t *sdo)
Destroys a CANopen Client-SDO service.
Definition: csdo.c:1024
This header file is part of the utilities library; it contains the diagnostic declarations.
@ DIAG_ERROR
An error.
Definition: diag.h:57
void diag(enum diag_severity severity, int errc, const char *format,...)
Emits a diagnostic message.
Definition: diag.c:171
@ ERRNUM_INVAL
Invalid argument.
Definition: errnum.h:132
@ ERRNUM_INPROGRESS
Operation in progress.
Definition: errnum.h:128
int get_errc(void)
Returns the last (thread-specific) native error code set by a system call or library function.
Definition: errnum.c:932
void set_errc(int errc)
Sets the current (thread-specific) native error code to errc.
Definition: errnum.c:944
int errno2c(int errnum)
Transforms a standard C error number to a native error code.
Definition: errnum.c:46
void set_errnum(errnum_t errnum)
Sets the current (thread-specific) platform-independent error number to errnum.
Definition: errnum.h:424
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:343
void co_sdo_req_fini(struct co_sdo_req *req)
Finalizes a CANopen SDO upload/download request.
Definition: sdo.c:121
void co_sdo_req_clear(struct co_sdo_req *req)
Clears a CANopen SDO upload/download request, including its buffer.
Definition: sdo.c:129
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:349
const char * co_sdo_ac2str(co_unsigned32_t ac)
Returns a string describing an SDO abort code.
Definition: sdo.c:57
void co_sdo_req_init(struct co_sdo_req *req)
Initializes a CANopen SDO upload/download request.
Definition: sdo.c:109
#define CO_SDO_AC_TIMEOUT
SDO abort code: SDO protocol timed out.
Definition: sdo.h:66
void can_timer_stop(can_timer_t *timer)
Stops a CAN timer and unregisters it with a network interface.
Definition: net.c:462
int can_net_send(can_net_t *net, const struct can_msg *msg)
Sends a CAN frame from a network interface.
Definition: net.c:300
void can_net_get_time(const can_net_t *net, struct timespec *tp)
Retrieves the current time of a CAN network interface.
Definition: net.c:196
void can_timer_set_func(can_timer_t *timer, can_timer_func_t *func, void *data)
Sets the callback function invoked when a CAN timer is triggered.
Definition: net.c:422
can_recv_t * can_recv_create(void)
Creates a new CAN frame receiver.
Definition: net.c:533
void can_recv_stop(can_recv_t *recv)
Stops a CAN frame receiver from processing frames and unregisters it with the network interface.
Definition: net.c:609
void can_recv_set_func(can_recv_t *recv, can_recv_func_t *func, void *data)
Sets the callback function used to process CAN frames with a receiver.
Definition: net.c:578
void can_recv_destroy(can_recv_t *recv)
Destroys a CAN frame receiver.
Definition: net.c:558
can_timer_t * can_timer_create(void)
Creates a new CAN timer.
Definition: net.c:376
void can_timer_timeout(can_timer_t *timer, can_net_t *net, int timeout)
Starts a CAN timer and registers it with a network interface.
Definition: net.c:478
void can_recv_start(can_recv_t *recv, can_net_t *net, uint_least32_t id, uint_least8_t flags)
Registers a CAN frame receiver with a network interface and starts processing frames.
Definition: net.c:587
void can_timer_destroy(can_timer_t *timer)
Destroys a CAN timer.
Definition: net.c:401
void co_nmt_boot_con(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned8_t st, char es)
The CANopen NMT 'boot slave' confirmation function, invoked when the 'boot slave' process completes.
Definition: nmt.c:2297
#define CO_NMT_ST_BOOTUP
The NMT state 'boot-up'.
Definition: nmt.h:55
#define CO_NMT_EC_CANID(id)
The CAN identifier used for both node guarding and heartbeat monitoring.
Definition: nmt.h:76
int co_nmt_cfg_req(co_nmt_t *nmt, co_unsigned8_t id, int timeout, co_nmt_cfg_con_t *con, void *data)
Issues the NMT 'configuration request' for the specified node.
Definition: nmt.c:1979
#define CO_NMT_ST_START
The NMT state 'operational'.
Definition: nmt.h:61
#define CO_NMT_ST_TOGGLE
The mask to get/set the toggle bit from an NMT state.
Definition: nmt.h:73
int co_nmt_cs_req(co_nmt_t *nmt, co_unsigned8_t cs, co_unsigned8_t id)
Submits an NMT request to a slave.
Definition: nmt.c:1806
#define CO_NMT_CS_RESET_COMM
The NMT command specifier 'reset communication'.
Definition: nmt.h:52
static co_nmt_boot_state_t * co_nmt_boot_reset_comm_on_recv(co_nmt_boot_t *boot, const struct can_msg *msg)
The 'CAN frame received' transition function of the 'reset communication' state.
Definition: nmt_boot.c:1486
static co_nmt_boot_state_t * co_nmt_boot_up_cfg_on_cfg_con(co_nmt_boot_t *boot, co_unsigned32_t ac)
The 'configuration request confirmation' transition unction of the 'update configuration' state.
Definition: nmt_boot.c:2119
static co_nmt_boot_state_t *const co_nmt_boot_chk_serial_nr_state
The 'check serial number' state (see Fig. 5 in CiA 302-2 version 4.1.0).
Definition: nmt_boot.c:414
static co_nmt_boot_state_t *const co_nmt_boot_stop_prog_state
The 'stop program' state (see Fig. 3 in CiA 302-3 version 4.1.0).
Definition: nmt_boot.c:508
static co_nmt_boot_state_t *const co_nmt_boot_chk_cfg_time_state
The 'check configuration time' state (see Fig.
Definition: nmt_boot.c:694
static void co_nmt_boot_emit_dn_con(co_nmt_boot_t *boot, co_unsigned32_t ac)
Invokes the 'SDO download confirmation' transition function of the current state of a 'boot slave' se...
Definition: nmt_boot.c:1049
static co_nmt_boot_state_t * co_nmt_boot_error_on_enter(co_nmt_boot_t *boot)
The entry function of the 'error' state.
Definition: nmt_boot.c:1161
static co_nmt_boot_state_t * co_nmt_boot_chk_revision_on_enter(co_nmt_boot_t *boot)
The entry function of the 'check revision number' state.
Definition: nmt_boot.c:1310
static co_nmt_boot_state_t * co_nmt_boot_wait_prog_on_enter(co_nmt_boot_t *boot)
The entry function of the 'wait till program is started' state.
Definition: nmt_boot.c:1959
static co_nmt_boot_state_t * co_nmt_boot_chk_node_on_enter(co_nmt_boot_t *boot)
The entry function of the 'check node state' state.
Definition: nmt_boot.c:1392
static co_nmt_boot_state_t *const co_nmt_boot_wait_flash_state
The 'check flashing' state (see Fig. 3 in CiA 302-3 version 4.1.0).
Definition: nmt_boot.c:589
static co_nmt_boot_state_t * co_nmt_boot_ec_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
The 'timeout' transition function of the 'start error control' state.
Definition: nmt_boot.c:2187
static co_nmt_boot_state_t * co_nmt_boot_start_prog_on_enter(co_nmt_boot_t *boot)
The entry function of the 'start program' state.
Definition: nmt_boot.c:1918
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 'SDO upload confirmation' transition function of the 'check program SW ID' state.
Definition: nmt_boot.c:1898
static co_nmt_boot_state_t *const co_nmt_boot_blk_dn_prog_state
The 'download program' state (see Fig. 3 in CiA 302-3 version 4.1.0).
Definition: nmt_boot.c:546
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:2215
static co_nmt_boot_state_t *const co_nmt_boot_up_cfg_state
The 'update configuration' state (see Fig.
Definition: nmt_boot.c:714
static co_nmt_boot_state_t * co_nmt_boot_ec_on_recv(co_nmt_boot_t *boot, const struct can_msg *msg)
The 'CAN frame received' transition function of the 'start error control' state.
Definition: nmt_boot.c:2168
#define LELY_CO_NMT_BOOT_RTR_TIMEOUT
The timeout (in milliseconds) after sending a node guarding RTR.
Definition: nmt_boot.c:54
static void co_nmt_boot_error_on_leave(co_nmt_boot_t *boot)
The exit function of the 'error' state.
Definition: nmt_boot.c:1169
static void co_nmt_boot_emit_cfg_con(co_nmt_boot_t *boot, co_unsigned32_t ac)
Invokes the 'configuration request confirmation' transition function of the current state of a 'boot ...
Definition: nmt_boot.c:1070
static co_nmt_boot_state_t * co_nmt_boot_clear_prog_on_dn_con(co_nmt_boot_t *boot, co_unsigned32_t ac)
The 'SDO download confirmation' transition function of the 'clear program' state.
Definition: nmt_boot.c:1650
static co_nmt_boot_state_t * co_nmt_boot_blk_dn_prog_on_enter(co_nmt_boot_t *boot)
The entry function of the 'download program' state.
Definition: nmt_boot.c:1678
static co_nmt_boot_state_t * co_nmt_boot_chk_cfg_date_on_enter(co_nmt_boot_t *boot)
The entry function of the 'check configuration date' state.
Definition: nmt_boot.c:2021
static co_nmt_boot_state_t * co_nmt_boot_chk_device_type_on_enter(co_nmt_boot_t *boot)
The entry function of the 'check device type' state.
Definition: nmt_boot.c:1177
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 'SDO upload confirmation' transition function of the 'check configuration time' state.
Definition: nmt_boot.c:2081
static co_nmt_boot_state_t * co_nmt_boot_wait_flash_on_enter(co_nmt_boot_t *boot)
The entry function of the 'wait for end of flashing' state.
Definition: nmt_boot.c:1778
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 'SDO upload confirmation' transition function of the 'wait till program is started' state.
Definition: nmt_boot.c:1987
static void co_nmt_boot_enter(co_nmt_boot_t *boot, co_nmt_boot_state_t *next)
Enters the specified state of a 'boot slave' service and invokes the exit and entry functions.
Definition: nmt_boot.c:1013
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 'boot slave' service.
Definition: nmt_boot.c:988
static co_nmt_boot_state_t * co_nmt_boot_dn_prog_on_dn_con(co_nmt_boot_t *boot, co_unsigned32_t ac)
The 'SDO download confirmation' transition function of the 'download program' state.
Definition: nmt_boot.c:1748
static co_nmt_boot_state_t *const co_nmt_boot_wait_prog_state
The 'wait till program is started' state (see Fig.
Definition: nmt_boot.c:655
static co_nmt_boot_state_t * co_nmt_boot_wait_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
The 'timeout' transition function of the 'wait asynchronously' state.
Definition: nmt_boot.c:1080
void co_nmt_boot_destroy(co_nmt_boot_t *boot)
Destroys a CANopen NMT 'boot slave' service.
Definition: nmt_boot.c:910
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 'SDO upload confirmation' transition function of the 'check serial number' state.
Definition: nmt_boot.c:1372
static void co_nmt_boot_cfg_con(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned32_t ac, void *data)
The CANopen NMT 'configuration request' confirmation callback function for a 'boot slave' service.
Definition: nmt_boot.c:1001
static co_nmt_boot_state_t * co_nmt_boot_clear_prog_on_enter(co_nmt_boot_t *boot)
The entry function of the 'clear program' state.
Definition: nmt_boot.c:1637
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 'SDO upload confirmation' transition function of the 'stop program' state.
Definition: nmt_boot.c:1611
static co_nmt_boot_state_t *const co_nmt_boot_chk_product_code_state
The 'check product code' state (see Fig. 5 in CiA 302-2 version 4.1.0).
Definition: nmt_boot.c:374
static co_nmt_boot_state_t * co_nmt_boot_chk_prog_on_enter(co_nmt_boot_t *boot)
The entry function of the 'check program SW ID state.
Definition: nmt_boot.c:1885
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 'SDO upload confirmation' transition function of the 'check software' state.
Definition: nmt_boot.c:1534
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 'SDO upload confirmation' transition function of the 'check configuration date' state.
Definition: nmt_boot.c:2047
static co_nmt_boot_state_t * co_nmt_boot_ec_on_enter(co_nmt_boot_t *boot)
The entry function of the 'start error control' state.
Definition: nmt_boot.c:2141
#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:71
static co_nmt_boot_state_t *const co_nmt_boot_chk_node_state
The 'check node state' state (see Fig. 6 in CiA 302-2 version 4.1.0).
Definition: nmt_boot.c:440
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 'SDO download confirmation' transition function of the 'download program' state.
Definition: nmt_boot.c:1709
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 'boot slave' service.
Definition: nmt_boot.c:975
static co_nmt_boot_state_t * co_nmt_boot_chk_product_code_on_enter(co_nmt_boot_t *boot)
The entry function of the 'check product code' state.
Definition: nmt_boot.c:1269
static int co_nmt_boot_timer(const struct timespec *tp, void *data)
The CAN timer callback function for a 'boot slave' service.
Definition: nmt_boot.c:963
static void co_nmt_boot_emit_time(co_nmt_boot_t *boot, const struct timespec *tp)
Invokes the 'timeout' transition function of the current state of a 'boot slave' service.
Definition: nmt_boot.c:1039
co_nmt_boot_t * co_nmt_boot_create(can_net_t *net, co_dev_t *dev, co_nmt_t *nmt)
Creates a new CANopen NMT 'boot slave' service.
Definition: nmt_boot.c:885
static co_nmt_boot_state_t *const co_nmt_boot_chk_revision_state
The 'check revision number' state (see Fig. 5 in CiA 302-2 version 4.1.0).
Definition: nmt_boot.c:394
static co_nmt_boot_state_t * co_nmt_boot_chk_node_on_recv(co_nmt_boot_t *boot, const struct can_msg *msg)
The 'CAN frame received' transition function of the 'check node state' state.
Definition: nmt_boot.c:1429
static co_nmt_boot_state_t *const co_nmt_boot_ec_state
The 'start error control' state (see Fig. 11 in CiA 302-2 version 4.1.0).
Definition: nmt_boot.c:737
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 'SDO upload confirmation' transition function of the 'check vendor ID' state.
Definition: nmt_boot.c:1249
static co_nmt_boot_state_t * co_nmt_boot_stop_prog_on_dn_con(co_nmt_boot_t *boot, co_unsigned32_t ac)
The 'SDO download confirmation' transition function of the 'stop program' state.
Definition: nmt_boot.c:1586
int co_nmt_boot_boot_req(co_nmt_boot_t *boot, co_unsigned8_t id, int timeout, co_csdo_ind_t *dn_ind, co_csdo_ind_t *up_ind, void *data)
Starts a CANopen NMT 'boot slave' service.
Definition: nmt_boot.c:919
static co_nmt_boot_state_t *const co_nmt_boot_chk_vendor_id_state
The 'check vendor ID' state (see Fig. 5 in CiA 302-2 version 4.1.0).
Definition: nmt_boot.c:354
#define LELY_CO_NMT_BOOT_WAIT_TIMEOUT
The timeout (in milliseconds) before trying to boot the slave again.
Definition: nmt_boot.c:43
#define LELY_CO_NMT_BOOT_RESET_TIMEOUT
The timeout (in milliseconds) after sending the NMT 'reset communication' command.
Definition: nmt_boot.c:63
static int co_nmt_boot_send_rtr(co_nmt_boot_t *boot)
Sends a node guarding RTR to the slave.
Definition: nmt_boot.c:2235
static co_nmt_boot_state_t * co_nmt_boot_start_prog_on_dn_con(co_nmt_boot_t *boot, co_unsigned32_t ac)
The 'SDO download confirmation' transition function of the 'start program' state.
Definition: nmt_boot.c:1934
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:2196
static co_nmt_boot_state_t *const co_nmt_boot_start_prog_state
The 'start program' state (see Fig. 3 in CiA 302-3 version 4.1.0).
Definition: nmt_boot.c:626
static co_nmt_boot_state_t *const co_nmt_boot_wait_state
The 'wait asynchronously' state.
Definition: nmt_boot.c:290
static co_nmt_boot_state_t *const co_nmt_boot_chk_prog_state
The 'check program SW ID' state (see Fig. 8 in CiA 302-2 version 4.1.0).
Definition: nmt_boot.c:607
static co_nmt_boot_state_t * co_nmt_boot_wait_flash_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
The 'timeout' transition function of the 'wait for end of flashing' state.
Definition: nmt_boot.c:1790
static co_nmt_boot_state_t *const co_nmt_boot_dn_prog_state
The 'download program' state (see Fig. 3 in CiA 302-3 version 4.1.0).
Definition: nmt_boot.c:564
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 'SDO upload confirmation' transition function of the 'check device type' state.
Definition: nmt_boot.c:1193
static co_nmt_boot_state_t * co_nmt_boot_dn_prog_on_enter(co_nmt_boot_t *boot)
The entry function of the 'download program' state.
Definition: nmt_boot.c:1735
static co_nmt_boot_state_t *const co_nmt_boot_reset_comm_state
The 'reset communication' state (see Fig. 6 in CiA 302-2 version 4.1.0).
Definition: nmt_boot.c:464
static void co_nmt_boot_chk_node_on_leave(co_nmt_boot_t *boot)
The exit function of the 'check node state' state.
Definition: nmt_boot.c:1464
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:2206
static int co_nmt_boot_recv(const struct can_msg *msg, void *data)
The CAN receive callback function for a 'boot slave' service.
Definition: nmt_boot.c:951
static co_nmt_boot_state_t * co_nmt_boot_chk_serial_nr_on_enter(co_nmt_boot_t *boot)
The entry function of the 'check serial number' state.
Definition: nmt_boot.c:1351
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 'SDO upload confirmation' transition function of the current state of a 'boot slave' serv...
Definition: nmt_boot.c:1059
static co_nmt_boot_state_t * co_nmt_boot_chk_vendor_id_on_enter(co_nmt_boot_t *boot)
The entry function of the 'check vendor ID' state.
Definition: nmt_boot.c:1228
static co_nmt_boot_state_t * co_nmt_boot_stop_prog_on_enter(co_nmt_boot_t *boot)
The entry function of the 'stop program' state.
Definition: nmt_boot.c:1574
static co_nmt_boot_state_t *const co_nmt_boot_error_state
The 'error' state.
Definition: nmt_boot.c:314
#define LELY_CO_NMT_BOOT_SDO_RETRY
The number of times an SDO request is retried after a timeout.
Definition: nmt_boot.c:48
static co_nmt_boot_state_t * co_nmt_boot_chk_node_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
The 'timeout' transition function of the 'check node state' state.
Definition: nmt_boot.c:1455
static co_nmt_boot_state_t *const co_nmt_boot_chk_device_type_state
The 'check device type' state (see Fig. 5 in CiA 302-2 version 4.1.0).
Definition: nmt_boot.c:334
static co_nmt_boot_state_t *const co_nmt_boot_abort_state
The 'abort' state.
Definition: nmt_boot.c:300
static co_nmt_boot_state_t *const co_nmt_boot_chk_cfg_date_state
The 'check configuration date' state (see Fig.
Definition: nmt_boot.c:677
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 'SDO upload confirmation' transition function of the 'check product code' state.
Definition: nmt_boot.c:1290
static co_nmt_boot_state_t *const co_nmt_boot_chk_sw_state
The 'check software' state (see Fig. 6 in CiA 302-2 version 4.1.0).
Definition: nmt_boot.c:482
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 'SDO upload confirmation' transition function of the 'check revision number' state.
Definition: nmt_boot.c:1331
static co_nmt_boot_state_t * co_nmt_boot_chk_sw_on_enter(co_nmt_boot_t *boot)
The entry function of the 'check software' state.
Definition: nmt_boot.c:1505
static co_nmt_boot_state_t *const co_nmt_boot_clear_prog_state
The 'clear program' state (see Fig. 3 in CiA 302-3 version 4.1.0).
Definition: nmt_boot.c:527
static co_nmt_boot_state_t * co_nmt_boot_up_cfg_on_enter(co_nmt_boot_t *boot)
The entry function of the 'update configuration' state.
Definition: nmt_boot.c:2103
static co_nmt_boot_state_t * co_nmt_boot_wait_prog_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
The 'timeout' transition function of the 'wait till program is started' state.
Definition: nmt_boot.c:1971
static co_nmt_boot_state_t * co_nmt_boot_abort_on_enter(co_nmt_boot_t *boot)
The entry function of the 'abort' state.
Definition: nmt_boot.c:1119
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 'SDO upload confirmation' transition function of the 'wait for end of flashing' state.
Definition: nmt_boot.c:1803
static void co_nmt_boot_emit_recv(co_nmt_boot_t *boot, const struct can_msg *msg)
Invokes the 'CAN frame received' transition function of the current state of a 'boot slave' service.
Definition: nmt_boot.c:1029
static co_nmt_boot_state_t * co_nmt_boot_reset_comm_on_enter(co_nmt_boot_t *boot)
The entry function of the 'reset communication' state.
Definition: nmt_boot.c:1472
static co_nmt_boot_state_t * co_nmt_boot_reset_comm_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
The 'timeout' transition function of the 'reset communication' state.
Definition: nmt_boot.c:1496
This is the internal header file of the NMT 'boot slave' declarations.
This header file is part of the CANopen library; it contains the object dictionary declarations.
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:712
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:1066
co_unsigned16_t co_sub_get_type(const co_sub_t *sub)
Returns the data type of a CANopen sub-object.
Definition: obj.c:603
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:86
A CAN timer.
Definition: net.c:63
A CANopen Client-SDO.
Definition: csdo.c:71
A CANopen device.
Definition: dev.h:30
A CANopen NMT 'boot slave' state.
Definition: nmt_boot.c:215
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:237
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:227
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:261
void(* on_leave)(co_nmt_boot_t *boot)
A pointer to the function invoked when the current state is left.
Definition: nmt_boot.c:275
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:217
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 'configuration request' completes.
Definition: nmt_boot.c:272
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:248
A CANopen NMT 'boot slave' service.
Definition: nmt_boot.c:79
can_recv_t * recv
A pointer to the CAN frame receiver.
Definition: nmt_boot.c:89
struct co_sdo_req req
The CANopen SDO upload request used for reading sub-objects.
Definition: nmt_boot.c:105
can_timer_t * timer
A pointer to the CAN timer.
Definition: nmt_boot.c:91
co_nmt_t * nmt
A pointer to an NMT master service.
Definition: nmt_boot.c:85
can_net_t * net
A pointer to a CAN network interface.
Definition: nmt_boot.c:81
co_nmt_boot_state_t * state
A pointer to the current state.
Definition: nmt_boot.c:87
co_unsigned32_t assignment
The NMT slave assignment (object 1F81).
Definition: nmt_boot.c:101
co_unsigned16_t ms
The consumer heartbeat time (in milliseconds).
Definition: nmt_boot.c:103
int retry
The number of SDO retries remaining.
Definition: nmt_boot.c:107
co_csdo_t * sdo
A pointer to the Client-SDO used to access slave objects.
Definition: nmt_boot.c:97
co_unsigned8_t id
The node-ID.
Definition: nmt_boot.c:93
co_dev_t * dev
A pointer to a CANopen device.
Definition: nmt_boot.c:83
char es
The error status.
Definition: nmt_boot.c:111
struct timespec start
The time at which the 'boot slave' request was received.
Definition: nmt_boot.c:99
int timeout
The SDO timeout (in milliseconds).
Definition: nmt_boot.c:95
co_unsigned8_t st
The state of the node (including the toggle bit).
Definition: nmt_boot.c:109
A CANopen NMT master/slave service.
Definition: nmt.c:148
A CANopen object.
Definition: obj.h:31
A CANopen sub-object.
Definition: obj.h:53
A CAN or CAN FD format frame.
Definition: msg.h:87
uint_least8_t data[CAN_MSG_MAX_LEN]
The frame payload (in case of a data frame).
Definition: msg.h:102
uint_least32_t id
The identifier (11 or 29 bits, depending on the CAN_FLAG_IDE flag).
Definition: msg.h:89
uint_least8_t flags
The flags (any combination of CAN_FLAG_IDE, CAN_FLAG_RTR, CAN_FLAG_FDF, CAN_FLAG_BRS and CAN_FLAG_ESI...
Definition: msg.h:94
uint_least8_t len
The number of bytes in data (or the requested number of bytes in case of a remote frame).
Definition: msg.h:100
A CANopen SDO upload/download request.
Definition: sdo.h:181
size_t size
The total size (in bytes) of the value to be uploaded/downloaded.
Definition: sdo.h:187
const void * buf
A pointer to the next bytes to be uploaded/downloaded.
Definition: sdo.h:189
int co_type_is_array(co_unsigned16_t type)
Returns 1 if the specified (static) data type is an array, and 0 if not.
Definition: type.c:40
#define CO_DEFTYPE_UNSIGNED8
The data type (and object index) of an 8-bit unsigned integer.
Definition: type.h:44
#define CO_DEFTYPE_UNSIGNED32
The data type (and object index) of a 32-bit unsigned integer.
Definition: type.h:50
A union of the CANopen static data types.
Definition: val.h:273
This header file is part of the utilities library; it contains the time function declarations.
int_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:221
This header file is part of the CANopen library; it contains the CANopen value declarations.
size_t co_val_read(co_unsigned16_t type, void *val, const uint_least8_t *begin, const uint_least8_t *end)
Reads a value of the specified data type from a memory buffer.
Definition: val.c:451
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:369