Lely core libraries  2.2.5
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
41 #define LELY_CO_NMT_BOOT_WAIT_TIMEOUT 1000
42 #endif
43 
44 #ifndef LELY_CO_NMT_BOOT_SDO_RETRY
46 #define LELY_CO_NMT_BOOT_SDO_RETRY 3
47 #endif
48 
49 #ifndef LELY_CO_NMT_BOOT_RTR_TIMEOUT
51 #define LELY_CO_NMT_BOOT_RTR_TIMEOUT 100
52 #endif
53 
54 #ifndef LELY_CO_NMT_BOOT_RESET_TIMEOUT
59 #define LELY_CO_NMT_BOOT_RESET_TIMEOUT 1000
60 #endif
61 
62 #ifndef LELY_CO_NMT_BOOT_CHECK_TIMEOUT
67 #define LELY_CO_NMT_BOOT_CHECK_TIMEOUT 100
68 #endif
69 
70 struct __co_nmt_boot_state;
72 typedef const struct __co_nmt_boot_state co_nmt_boot_state_t;
73 
75 struct __co_nmt_boot {
89  co_unsigned8_t id;
91  int timeout;
95  struct timespec start;
97  co_unsigned32_t assignment;
99  co_unsigned16_t ms;
101  struct co_sdo_req req;
103  int retry;
105  co_unsigned8_t st;
107  char es;
108 };
109 
115 static int co_nmt_boot_recv(const struct can_msg *msg, void *data);
116 
122 static int co_nmt_boot_timer(const struct timespec *tp, void *data);
123 
130 static void co_nmt_boot_dn_con(co_csdo_t *sdo, co_unsigned16_t idx,
131  co_unsigned8_t subidx, co_unsigned32_t ac, void *data);
132 
139 static void co_nmt_boot_up_con(co_csdo_t *sdo, co_unsigned16_t idx,
140  co_unsigned8_t subidx, co_unsigned32_t ac, const void *ptr,
141  size_t n, void *data);
142 
149 static void co_nmt_boot_cfg_con(co_nmt_t *nmt, co_unsigned8_t id,
150  co_unsigned32_t ac, void *data);
151 
156 static void co_nmt_boot_enter(co_nmt_boot_t *boot, co_nmt_boot_state_t *next);
157 
165 static inline void co_nmt_boot_emit_recv(
166  co_nmt_boot_t *boot, const struct can_msg *msg);
167 
175 static inline void co_nmt_boot_emit_time(
176  co_nmt_boot_t *boot, const struct timespec *tp);
177 
185 static inline void co_nmt_boot_emit_dn_con(
186  co_nmt_boot_t *boot, co_unsigned32_t ac);
187 
197 static inline void co_nmt_boot_emit_up_con(co_nmt_boot_t *boot,
198  co_unsigned32_t ac, const void *ptr, size_t n);
199 
207 static inline void co_nmt_boot_emit_cfg_con(
208  co_nmt_boot_t *boot, co_unsigned32_t ac);
209 
213  co_nmt_boot_state_t *(*on_enter)(co_nmt_boot_t *boot);
223  co_nmt_boot_state_t *(*on_recv)(
224  co_nmt_boot_t *boot, const struct can_msg *msg);
233  co_nmt_boot_state_t *(*on_time)(
234  co_nmt_boot_t *boot, const struct timespec *tp);
244  co_nmt_boot_state_t *(*on_dn_con)(
245  co_nmt_boot_t *boot, co_unsigned32_t ac);
257  co_nmt_boot_state_t *(*on_up_con)(co_nmt_boot_t *boot,
258  co_unsigned32_t ac, const void *ptr, size_t n);
268  co_nmt_boot_state_t *(*on_cfg_con)(
269  co_nmt_boot_t *boot, co_unsigned32_t ac);
271  void (*on_leave)(co_nmt_boot_t *boot);
272 };
273 
274 #define LELY_CO_DEFINE_STATE(name, ...) \
275  static co_nmt_boot_state_t *const name = \
276  &(co_nmt_boot_state_t){ __VA_ARGS__ };
277 
280  co_nmt_boot_t *boot, const struct timespec *tp);
281 
283 // clang-format off
284 LELY_CO_DEFINE_STATE(co_nmt_boot_wait_state,
285  .on_time = &co_nmt_boot_wait_on_time
286 )
287 // clang-format on
288 
291 
293 // clang-format off
294 LELY_CO_DEFINE_STATE(co_nmt_boot_abort_state,
295  .on_enter = &co_nmt_boot_abort_on_enter
296 )
297 // clang-format on
298 
301 
303 static void co_nmt_boot_error_on_leave(co_nmt_boot_t *boot);
304 
306 // clang-format off
307 LELY_CO_DEFINE_STATE(co_nmt_boot_error_state,
308  .on_enter = &co_nmt_boot_error_on_enter,
309  .on_leave = &co_nmt_boot_error_on_leave
310 )
311 // clang-format on
312 
315  co_nmt_boot_t *boot);
316 
322  co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr,
323  size_t n);
324 
326 // clang-format off
327 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_device_type_state,
330 )
331 // clang-format on
332 
335  co_nmt_boot_t *boot);
336 
342  co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr,
343  size_t n);
344 
346 // clang-format off
347 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_vendor_id_state,
350 )
351 // clang-format on
352 
355  co_nmt_boot_t *boot);
356 
362  co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr,
363  size_t n);
364 
366 // clang-format off
367 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_product_code_state,
370 )
371 // clang-format on
372 
375  co_nmt_boot_t *boot);
376 
382  co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr,
383  size_t n);
384 
386 // clang-format off
387 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_revision_state,
390 )
391 // clang-format on
392 
395  co_nmt_boot_t *boot);
396 
402  co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr,
403  size_t n);
404 
406 // clang-format off
407 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_serial_nr_state,
410 )
411 // clang-format on
412 
415 
420  co_nmt_boot_t *boot, const struct can_msg *msg);
421 
424  co_nmt_boot_t *boot, const struct timespec *tp);
425 
428 
430 // clang-format off
431 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_node_state,
432  .on_enter = &co_nmt_boot_chk_node_on_enter,
433  .on_recv = &co_nmt_boot_chk_node_on_recv,
434  .on_time = &co_nmt_boot_chk_node_on_time,
435  .on_leave = &co_nmt_boot_chk_node_on_leave
436 )
437 // clang-format on
438 
441  co_nmt_boot_t *boot);
442 
448  co_nmt_boot_t *boot, const struct can_msg *msg);
449 
452  co_nmt_boot_t *boot, const struct timespec *tp);
453 
455 // clang-format off
456 LELY_CO_DEFINE_STATE(co_nmt_boot_reset_comm_state,
457  .on_enter = &co_nmt_boot_reset_comm_on_enter,
458  .on_recv = &co_nmt_boot_reset_comm_on_recv,
460 )
461 // clang-format on
462 
465 
471  co_unsigned32_t ac, const void *ptr, size_t n);
472 
474 // clang-format off
475 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_sw_state,
476  .on_enter = &co_nmt_boot_chk_sw_on_enter,
477  .on_up_con = &co_nmt_boot_chk_sw_on_up_con
478 )
479 // clang-format on
480 
483 
489  co_nmt_boot_t *boot, co_unsigned32_t ac);
490 
496  co_unsigned32_t ac, const void *ptr, size_t n);
497 
499 // clang-format off
500 LELY_CO_DEFINE_STATE(co_nmt_boot_stop_prog_state,
501  .on_enter = &co_nmt_boot_stop_prog_on_enter,
502  .on_dn_con = &co_nmt_boot_stop_prog_on_dn_con,
503  .on_up_con = &co_nmt_boot_stop_prog_on_up_con
504 )
505 // clang-format on
506 
509  co_nmt_boot_t *boot);
510 
516  co_nmt_boot_t *boot, co_unsigned32_t ac);
517 
519 // clang-format off
520 LELY_CO_DEFINE_STATE(co_nmt_boot_clear_prog_state,
521  .on_enter = &co_nmt_boot_clear_prog_on_enter,
523 )
524 // clang-format on
525 
528  co_nmt_boot_t *boot);
529 
535  co_nmt_boot_t *boot, co_unsigned32_t ac);
536 
538 // clang-format off
539 LELY_CO_DEFINE_STATE(co_nmt_boot_blk_dn_prog_state,
542 )
543 // clang-format on
544 
547 
553  co_nmt_boot_t *boot, co_unsigned32_t ac);
554 
556 // clang-format off
557 LELY_CO_DEFINE_STATE(co_nmt_boot_dn_prog_state,
558  .on_enter = &co_nmt_boot_dn_prog_on_enter,
559  .on_dn_con = &co_nmt_boot_dn_prog_on_dn_con
560 )
561 // clang-format on
562 
565  co_nmt_boot_t *boot);
566 
569  co_nmt_boot_t *boot, const struct timespec *tp);
570 
576  co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr,
577  size_t n);
578 
580 // clang-format off
581 LELY_CO_DEFINE_STATE(co_nmt_boot_wait_flash_state,
582  .on_enter = &co_nmt_boot_wait_flash_on_enter,
583  .on_time = &co_nmt_boot_wait_flash_on_time,
585 )
586 // clang-format on
587 
590 
596  co_unsigned32_t ac, const void *ptr, size_t n);
597 
599 // clang-format off
600 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_prog_state,
601  .on_enter = &co_nmt_boot_chk_prog_on_enter,
602  .on_up_con = &co_nmt_boot_chk_prog_on_up_con
603 )
604 // clang-format on
605 
608  co_nmt_boot_t *boot);
609 
615  co_nmt_boot_t *boot, co_unsigned32_t ac);
616 
618 // clang-format off
619 LELY_CO_DEFINE_STATE(co_nmt_boot_start_prog_state,
620  .on_enter = &co_nmt_boot_start_prog_on_enter,
622 )
623 // clang-format on
624 
627 
633  co_nmt_boot_t *boot, const struct timespec *tp);
634 
640  co_unsigned32_t ac, const void *ptr, size_t n);
641 
646 // clang-format off
647 LELY_CO_DEFINE_STATE(co_nmt_boot_wait_prog_state,
648  .on_enter = &co_nmt_boot_wait_prog_on_enter,
649  .on_time = &co_nmt_boot_wait_prog_on_time,
650  .on_up_con = &co_nmt_boot_wait_prog_on_up_con
651 )
652 // clang-format on
653 
656  co_nmt_boot_t *boot);
657 
663  co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr,
664  size_t n);
665 
669 // clang-format off
670 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_cfg_date_state,
673 )
674 // clang-format on
675 
681  co_nmt_boot_t *boot, co_unsigned32_t ac, const void *ptr,
682  size_t n);
683 
687 // clang-format off
688 LELY_CO_DEFINE_STATE(co_nmt_boot_chk_cfg_time_state,
690 )
691 // clang-format on
692 
695 
701  co_nmt_boot_t *boot, co_unsigned32_t ac);
702 
706 // clang-format off
707 LELY_CO_DEFINE_STATE(co_nmt_boot_up_cfg_state,
708  .on_enter = &co_nmt_boot_up_cfg_on_enter,
709  .on_cfg_con = &co_nmt_boot_up_cfg_on_cfg_con
710 )
711 // clang-format on
712 
715 
721  co_nmt_boot_t *boot, const struct can_msg *msg);
722 
725  co_nmt_boot_t *boot, const struct timespec *tp);
726 
728 // clang-format off
729 LELY_CO_DEFINE_STATE(co_nmt_boot_ec_state,
730  .on_enter = &co_nmt_boot_ec_on_enter,
731  .on_recv = &co_nmt_boot_ec_on_recv,
732  .on_time = &co_nmt_boot_ec_on_time
733 )
734 // clang-format on
735 
736 #undef LELY_CO_DEFINE_STATE
737 
750 static int co_nmt_boot_dn(co_nmt_boot_t *boot, co_unsigned16_t idx,
751  co_unsigned8_t subidx, co_unsigned16_t type, const void *val);
752 
763 static int co_nmt_boot_up(co_nmt_boot_t *boot, co_unsigned16_t idx,
764  co_unsigned8_t subidx);
765 
779 static int co_nmt_boot_chk(co_nmt_boot_t *boot, co_unsigned16_t idx,
780  co_unsigned8_t subidx, const void *ptr, size_t n);
781 
787 static int co_nmt_boot_send_rtr(co_nmt_boot_t *boot);
788 
789 void *
790 __co_nmt_boot_alloc(void)
791 {
792  void *ptr = malloc(sizeof(struct __co_nmt_boot));
793  if (!ptr)
794  set_errc(errno2c(errno));
795  return ptr;
796 }
797 
798 void
799 __co_nmt_boot_free(void *ptr)
800 {
801  free(ptr);
802 }
803 
804 struct __co_nmt_boot *
805 __co_nmt_boot_init(struct __co_nmt_boot *boot, can_net_t *net, co_dev_t *dev,
806  co_nmt_t *nmt)
807 {
808  assert(boot);
809  assert(net);
810  assert(dev);
811  assert(nmt);
812 
813  int errc = 0;
814 
815  boot->net = net;
816  boot->dev = dev;
817  boot->nmt = nmt;
818 
819  boot->state = NULL;
820 
821  boot->recv = can_recv_create();
822  if (!boot->recv) {
823  errc = get_errc();
824  goto error_create_recv;
825  }
826  can_recv_set_func(boot->recv, &co_nmt_boot_recv, boot);
827 
828  boot->timer = can_timer_create();
829  if (!boot->timer) {
830  errc = get_errc();
831  goto error_create_timer;
832  }
834 
835  boot->id = 0;
836 
837  boot->timeout = 0;
838  boot->sdo = NULL;
839 
840  boot->start = (struct timespec){ 0, 0 };
841  can_net_get_time(boot->net, &boot->start);
842 
843  boot->assignment = 0;
844  boot->ms = 0;
845 
846  boot->st = 0;
847  boot->es = 0;
848 
849  co_sdo_req_init(&boot->req);
850  boot->retry = 0;
851 
853  return boot;
854 
855  // can_timer_destroy(boot->timer);
856 error_create_timer:
857  can_recv_destroy(boot->recv);
858 error_create_recv:
859  set_errc(errc);
860  return NULL;
861 }
862 
863 void
864 __co_nmt_boot_fini(struct __co_nmt_boot *boot)
865 {
866  assert(boot);
867 
868  co_sdo_req_fini(&boot->req);
869 
870  co_csdo_destroy(boot->sdo);
871 
872  can_timer_destroy(boot->timer);
873  can_recv_destroy(boot->recv);
874 }
875 
878 {
879  int errc = 0;
880 
881  co_nmt_boot_t *boot = __co_nmt_boot_alloc();
882  if (!boot) {
883  errc = get_errc();
884  goto error_alloc_boot;
885  }
886 
887  if (!__co_nmt_boot_init(boot, net, dev, nmt)) {
888  errc = get_errc();
889  goto error_init_boot;
890  }
891 
892  return boot;
893 
894 error_init_boot:
895  __co_nmt_boot_free(boot);
896 error_alloc_boot:
897  set_errc(errc);
898  return NULL;
899 }
900 
901 void
903 {
904  if (boot) {
905  __co_nmt_boot_fini(boot);
906  __co_nmt_boot_free(boot);
907  }
908 }
909 
910 int
911 co_nmt_boot_boot_req(co_nmt_boot_t *boot, co_unsigned8_t id, int timeout,
912  co_csdo_ind_t *dn_ind, co_csdo_ind_t *up_ind, void *data)
913 {
914  assert(boot);
915 
916  if (!id || id > CO_NUM_NODES) {
918  return -1;
919  }
920 
921  if (boot->state != co_nmt_boot_wait_state) {
923  return -1;
924  }
925 
926  boot->id = id;
927 
928  boot->timeout = timeout;
929  co_csdo_destroy(boot->sdo);
930  boot->sdo = co_csdo_create(boot->net, NULL, boot->id);
931  if (!boot->sdo)
932  return -1;
933  co_csdo_set_timeout(boot->sdo, boot->timeout);
934  co_csdo_set_dn_ind(boot->sdo, dn_ind, data);
935  co_csdo_set_up_ind(boot->sdo, up_ind, data);
936 
937  co_nmt_boot_emit_time(boot, NULL);
938 
939  return 0;
940 }
941 
942 static int
943 co_nmt_boot_recv(const struct can_msg *msg, void *data)
944 {
945  assert(msg);
946  co_nmt_boot_t *boot = data;
947  assert(boot);
948 
949  co_nmt_boot_emit_recv(boot, msg);
950 
951  return 0;
952 }
953 
954 static int
955 co_nmt_boot_timer(const struct timespec *tp, void *data)
956 {
957  assert(tp);
958  co_nmt_boot_t *boot = data;
959  assert(boot);
960 
961  co_nmt_boot_emit_time(boot, tp);
962 
963  return 0;
964 }
965 
966 static void
967 co_nmt_boot_dn_con(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx,
968  co_unsigned32_t ac, void *data)
969 {
970  (void)sdo;
971  (void)idx;
972  (void)subidx;
973  co_nmt_boot_t *boot = data;
974  assert(boot);
975 
976  co_nmt_boot_emit_dn_con(boot, ac);
977 }
978 
979 static void
980 co_nmt_boot_up_con(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx,
981  co_unsigned32_t ac, const void *ptr, size_t n, void *data)
982 {
983  (void)sdo;
984  (void)idx;
985  (void)subidx;
986  co_nmt_boot_t *boot = data;
987  assert(boot);
988 
989  co_nmt_boot_emit_up_con(boot, ac, ptr, n);
990 }
991 
992 static void
993 co_nmt_boot_cfg_con(co_nmt_t *nmt, co_unsigned8_t id, co_unsigned32_t ac,
994  void *data)
995 {
996  (void)nmt;
997  (void)id;
998  co_nmt_boot_t *boot = data;
999  assert(boot);
1000 
1001  co_nmt_boot_emit_cfg_con(boot, ac);
1002 }
1003 
1004 static void
1006 {
1007  assert(boot);
1008 
1009  while (next) {
1010  co_nmt_boot_state_t *prev = boot->state;
1011  boot->state = next;
1012 
1013  if (prev && prev->on_leave)
1014  prev->on_leave(boot);
1015 
1016  next = next->on_enter ? next->on_enter(boot) : NULL;
1017  }
1018 }
1019 
1020 static inline void
1022 {
1023  assert(boot);
1024  assert(boot->state);
1025  assert(boot->state->on_recv);
1026 
1027  co_nmt_boot_enter(boot, boot->state->on_recv(boot, msg));
1028 }
1029 
1030 static inline void
1031 co_nmt_boot_emit_time(co_nmt_boot_t *boot, const struct timespec *tp)
1032 {
1033  assert(boot);
1034  assert(boot->state);
1035  assert(boot->state->on_time);
1036 
1037  co_nmt_boot_enter(boot, boot->state->on_time(boot, tp));
1038 }
1039 
1040 static inline void
1041 co_nmt_boot_emit_dn_con(co_nmt_boot_t *boot, co_unsigned32_t ac)
1042 {
1043  assert(boot);
1044  assert(boot->state);
1045  assert(boot->state->on_dn_con);
1046 
1047  co_nmt_boot_enter(boot, boot->state->on_dn_con(boot, ac));
1048 }
1049 
1050 static inline void
1051 co_nmt_boot_emit_up_con(co_nmt_boot_t *boot, co_unsigned32_t ac,
1052  const void *ptr, size_t n)
1053 {
1054  assert(boot);
1055  assert(boot->state);
1056  assert(boot->state->on_up_con);
1057 
1058  co_nmt_boot_enter(boot, boot->state->on_up_con(boot, ac, ptr, n));
1059 }
1060 
1061 static inline void
1062 co_nmt_boot_emit_cfg_con(co_nmt_boot_t *boot, co_unsigned32_t ac)
1063 {
1064  assert(boot);
1065  assert(boot->state);
1066  assert(boot->state->on_cfg_con);
1067 
1068  co_nmt_boot_enter(boot, boot->state->on_cfg_con(boot, ac));
1069 }
1070 
1071 static co_nmt_boot_state_t *
1072 co_nmt_boot_wait_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
1073 {
1074  (void)boot;
1075  (void)tp;
1076 
1077  boot->st = 0;
1078  boot->es = 0;
1079 
1080  // Retrieve the slave assignment for the node.
1081  boot->assignment = co_dev_get_val_u32(boot->dev, 0x1f81, boot->id);
1082 
1083  // Find the consumer heartbeat time for the node.
1084  boot->ms = 0;
1085  co_obj_t *obj_1016 = co_dev_find_obj(boot->dev, 0x1016);
1086  if (obj_1016) {
1087  co_unsigned8_t n = co_obj_get_val_u8(obj_1016, 0x00);
1088  for (size_t i = 1; i <= n; i++) {
1089  co_unsigned32_t val =
1090  co_obj_get_val_u32(obj_1016, i & 0xff);
1091  if (((val >> 16) & 0x7f) == boot->id)
1092  boot->ms = val & 0xffff;
1093  }
1094  }
1095 
1096  // Abort the 'boot slave' process if the slave is not in the network
1097  // list.
1098  if (!(boot->assignment & 0x01)) {
1099  boot->es = 'A';
1100  return co_nmt_boot_abort_state;
1101  }
1102 
1103  if (!(boot->assignment & 0x04))
1104  // Skip booting and start the error control service.
1105  return co_nmt_boot_ec_state;
1106 
1108 }
1109 
1110 static co_nmt_boot_state_t *
1112 {
1113  assert(boot);
1114 
1115  can_recv_stop(boot->recv);
1116  can_timer_stop(boot->timer);
1117 
1118  // If the node is already operational, end the 'boot slave' process with
1119  // error status L.
1120  if (!boot->es && (boot->st & ~CO_NMT_ST_TOGGLE) == CO_NMT_ST_START)
1121  boot->es = 'L';
1122 
1123  // Retry on error status B (see Fig. 4 in CiA 302-2 version 4.1.0).
1124  if (boot->es == 'B') {
1125  int wait = 1;
1126  if (boot->assignment & 0x08) {
1127  // Obtain the time (in milliseconds) the master will
1128  // wait for a mandatory slave to boot.
1129  co_unsigned32_t boot_time = co_dev_get_val_u32(
1130  boot->dev, 0x1f89, 0x00);
1131  // Check if this time has elapsed.
1132  if (boot_time) {
1133  struct timespec now = { 0, 0 };
1134  can_net_get_time(boot->net, &now);
1135  wait = timespec_diff_msec(&now, &boot->start)
1136  < boot_time;
1137  }
1138  }
1139  // If the slave is not mandatory, or the boot time has not yet
1140  // elapsed, wait asynchronously for a while and retry the 'boot
1141  // slave' process.
1142  if (wait) {
1143  can_timer_timeout(boot->timer, boot->net,
1145  return co_nmt_boot_wait_state;
1146  }
1147  }
1148 
1149  return co_nmt_boot_error_state;
1150 }
1151 
1152 static co_nmt_boot_state_t *
1154 {
1155  (void)boot;
1156 
1157  return co_nmt_boot_wait_state;
1158 }
1159 
1160 static void
1162 {
1163  assert(boot);
1164 
1165  co_nmt_boot_con(boot->nmt, boot->id, boot->st, boot->es);
1166 }
1167 
1168 static co_nmt_boot_state_t *
1170 {
1171  assert(boot);
1172 
1173  boot->es = 'B';
1174 
1175  // The device type check may follow an NMT 'reset communication'
1176  // command, in which case we may have to give the slave some time to
1177  // complete the state change. Start the first SDO request by simulating
1178  // a timeout.
1179  boot->retry = LELY_CO_NMT_BOOT_SDO_RETRY + 1;
1181  boot, CO_SDO_AC_TIMEOUT, NULL, 0);
1182 }
1183 
1184 static co_nmt_boot_state_t *
1186  const void *ptr, size_t n)
1187 {
1188  assert(boot);
1189 
1190  // Retry the SDO request on timeout (this includes the first attempt).
1191  if (ac == CO_SDO_AC_TIMEOUT && boot->retry--) {
1192  // Read the device type of the slave (object 1000).
1193  if (co_nmt_boot_up(boot, 0x1000, 0x00) == -1)
1194  return co_nmt_boot_abort_state;
1195  return NULL;
1196  } else if (ac) {
1197  diag(DIAG_ERROR, 0,
1198  "SDO abort code %08" PRIX32
1199  " received on upload request of object 1000 (Device type) to node %02X: %s",
1200  ac, boot->id, co_sdo_ac2str(ac));
1201  return co_nmt_boot_abort_state;
1202  }
1203 
1204  // If the expected device type (sub-object 1F84:ID) is 0, skip the check
1205  // and proceed with the vendor ID.
1206  co_unsigned32_t device_type =
1207  co_dev_get_val_u32(boot->dev, 0x1f84, boot->id);
1208  if (device_type && !co_nmt_boot_chk(boot, 0x1f84, boot->id, ptr, n)) {
1209  boot->es = 'C';
1210  return co_nmt_boot_abort_state;
1211  }
1212 
1213  can_recv_stop(boot->recv);
1215 }
1216 
1217 static co_nmt_boot_state_t *
1219 {
1220  assert(boot);
1221 
1222  // If the expected vendor ID (sub-object 1F85:ID) is 0, skip the check
1223  // and proceed with the product code.
1224  co_unsigned32_t vendor_id =
1225  co_dev_get_val_u32(boot->dev, 0x1f85, boot->id);
1226  if (!vendor_id)
1228 
1229  boot->es = 'D';
1230 
1231  // Read the vendor ID of the slave (sub-object 1018:01).
1232  if (co_nmt_boot_up(boot, 0x1018, 0x01) == -1)
1233  return co_nmt_boot_abort_state;
1234 
1235  return NULL;
1236 }
1237 
1238 static co_nmt_boot_state_t *
1240  const void *ptr, size_t n)
1241 {
1242  assert(boot);
1243 
1244  if (ac)
1245  diag(DIAG_ERROR, 0,
1246  "SDO abort code %08" PRIX32
1247  " received on upload request of sub-object 1018:01 (Vendor-ID) to node %02X: %s",
1248  ac, boot->id, co_sdo_ac2str(ac));
1249 
1250  if (ac || !co_nmt_boot_chk(boot, 0x1f85, boot->id, ptr, n))
1251  return co_nmt_boot_abort_state;
1252 
1254 }
1255 
1256 static co_nmt_boot_state_t *
1258 {
1259  assert(boot);
1260 
1261  // If the expected product code (sub-object 1F86:ID) is 0, skip the
1262  // check and proceed with the revision number.
1263  co_unsigned32_t product_code =
1264  co_dev_get_val_u32(boot->dev, 0x1f86, boot->id);
1265  if (!product_code)
1267 
1268  boot->es = 'M';
1269 
1270  // Read the product code of the slave (sub-object 1018:02).
1271  if (co_nmt_boot_up(boot, 0x1018, 0x02) == -1)
1272  return co_nmt_boot_abort_state;
1273 
1274  return NULL;
1275 }
1276 
1277 static co_nmt_boot_state_t *
1279  const void *ptr, size_t n)
1280 {
1281  assert(boot);
1282 
1283  if (ac)
1284  diag(DIAG_ERROR, 0,
1285  "SDO abort code %08" PRIX32
1286  " received on upload request of sub-object 1018:02 (Product code) to node %02X: %s",
1287  ac, boot->id, co_sdo_ac2str(ac));
1288 
1289  if (ac || !co_nmt_boot_chk(boot, 0x1f86, boot->id, ptr, n))
1290  return co_nmt_boot_abort_state;
1291 
1293 }
1294 
1295 static co_nmt_boot_state_t *
1297 {
1298  assert(boot);
1299 
1300  // If the expected revision number (sub-object 1F87:ID) is 0, skip the
1301  // check and proceed with the serial number.
1302  co_unsigned32_t revision =
1303  co_dev_get_val_u32(boot->dev, 0x1f87, boot->id);
1304  if (!revision)
1306 
1307  boot->es = 'N';
1308 
1309  // Read the revision number of the slave (sub-object 1018:03).
1310  if (co_nmt_boot_up(boot, 0x1018, 0x03) == -1)
1311  return co_nmt_boot_abort_state;
1312 
1313  return NULL;
1314 }
1315 
1316 static co_nmt_boot_state_t *
1318  const void *ptr, size_t n)
1319 {
1320  assert(boot);
1321 
1322  if (ac)
1323  diag(DIAG_ERROR, 0,
1324  "SDO abort code %08" PRIX32
1325  " received on upload request of sub-object 1018:03 (Revision number) to node %02X: %s",
1326  ac, boot->id, co_sdo_ac2str(ac));
1327 
1328  if (ac || !co_nmt_boot_chk(boot, 0x1f87, boot->id, ptr, n))
1329  return co_nmt_boot_abort_state;
1330 
1332 }
1333 
1334 static co_nmt_boot_state_t *
1336 {
1337  assert(boot);
1338 
1339  // If the expected serial number (sub-object 1F88:ID) is 0, skip the
1340  // check and proceed to 'check node state'.
1341  co_unsigned32_t serial_nr =
1342  co_dev_get_val_u32(boot->dev, 0x1f88, boot->id);
1343  if (!serial_nr)
1345 
1346  boot->es = 'O';
1347 
1348  // Read the serial number of the slave (sub-object 1018:04).
1349  if (co_nmt_boot_up(boot, 0x1018, 0x04) == -1)
1350  return co_nmt_boot_abort_state;
1351 
1352  return NULL;
1353 }
1354 
1355 static co_nmt_boot_state_t *
1357  const void *ptr, size_t n)
1358 {
1359  assert(boot);
1360 
1361  if (ac)
1362  diag(DIAG_ERROR, 0,
1363  "SDO abort code %08" PRIX32
1364  " received on upload request of sub-object 1018:04 (Serial number) to node %02X: %s",
1365  ac, boot->id, co_sdo_ac2str(ac));
1366 
1367  if (ac || !co_nmt_boot_chk(boot, 0x1f88, boot->id, ptr, n))
1368  return co_nmt_boot_abort_state;
1369 
1371 }
1372 
1373 static co_nmt_boot_state_t *
1375 {
1376  assert(boot);
1377 
1378  // If the keep-alive bit is set, check the node state.
1379  if (boot->assignment & 0x10) {
1380  int ms;
1381  if (boot->ms) {
1382  ms = boot->ms;
1383  boot->es = 'E';
1384  } else {
1386  boot->es = 'F';
1387  // If we're not a heartbeat consumer, start node
1388  // guarding by sending the first RTR.
1389  co_nmt_boot_send_rtr(boot);
1390  }
1391 
1392  // Start the CAN frame receiver for the heartbeat or node guard
1393  // message.
1394  can_recv_start(boot->recv, boot->net, CO_NMT_EC_CANID(boot->id),
1395  0);
1396  // Start the CAN timer in case we do not receive a heartbeat
1397  // indication or a node guard confirmation.
1398  can_timer_timeout(boot->timer, boot->net, ms);
1399 
1400  return NULL;
1401  }
1402 
1403  return co_nmt_boot_chk_sw_state;
1404 }
1405 
1406 static co_nmt_boot_state_t *
1408 {
1409  assert(boot);
1410  assert(msg);
1411  assert(msg->id == (uint_least32_t)CO_NMT_EC_CANID(boot->id));
1412 
1413  can_timer_stop(boot->timer);
1414 
1415  if (msg->len >= 1) {
1416  boot->st = msg->data[0];
1417  if ((boot->st & ~CO_NMT_ST_TOGGLE) == CO_NMT_ST_START)
1418  // If the node is already operational, skip the 'check
1419  // and update software version' and 'check
1420  // configuration' steps and proceed immediately to
1421  // 'start error control service'.
1422  return co_nmt_boot_ec_state;
1423  }
1424  boot->st = 0;
1425  // If the node is not operational, send the NMT 'reset
1426  // communication' command and proceed as if the keep-alive bit
1427  // was not set.
1428  co_nmt_cs_req(boot->nmt, CO_NMT_CS_RESET_COMM, boot->id);
1430 }
1431 
1432 static co_nmt_boot_state_t *
1433 co_nmt_boot_chk_node_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
1434 {
1435  (void)boot;
1436  (void)tp;
1437 
1438  return co_nmt_boot_abort_state;
1439 }
1440 
1441 static void
1443 {
1444  assert(boot);
1445 
1446  can_recv_stop(boot->recv);
1447 }
1448 
1449 static co_nmt_boot_state_t *
1451 {
1452  assert(boot);
1453 
1454  // Start the CAN frame receiver for the boot-up message.
1455  can_recv_start(boot->recv, boot->net, CO_NMT_EC_CANID(boot->id), 0);
1456  // Wait until we receive a boot-up message.
1458  boot->timer, boot->net, LELY_CO_NMT_BOOT_RESET_TIMEOUT);
1459 
1460  return NULL;
1461 }
1462 
1463 static co_nmt_boot_state_t *
1465 {
1466  (void)boot;
1467  (void)msg;
1468 
1469  can_recv_stop(boot->recv);
1470  return co_nmt_boot_chk_sw_state;
1471 }
1472 
1473 static co_nmt_boot_state_t *
1474 co_nmt_boot_reset_comm_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
1475 {
1476  (void)boot;
1477  (void)tp;
1478 
1479  return co_nmt_boot_abort_state;
1480 }
1481 
1482 static co_nmt_boot_state_t *
1484 {
1485  assert(boot);
1486 
1487  if (boot->assignment & 0x20) {
1488  boot->es = 'G';
1489 
1490  // Abort if the expected program software identification
1491  // (sub-object 1F55:ID) is 0.
1492  co_unsigned32_t sw_id =
1493  co_dev_get_val_u32(boot->dev, 0x1f55, boot->id);
1494  if (!sw_id)
1495  return co_nmt_boot_abort_state;
1496 
1497  // The software version check may follow an NMT 'reset
1498  // communication' command, in which case we may have to give the
1499  // slave some time to complete the state change. Start the first
1500  // SDO request by simulating a timeout.
1501  boot->retry = LELY_CO_NMT_BOOT_SDO_RETRY + 1;
1503  boot, CO_SDO_AC_TIMEOUT, NULL, 0);
1504  }
1505 
1506  // Continue with the 'check configuration' step if the software version
1507  // check is not necessary.
1509 }
1510 
1511 static co_nmt_boot_state_t *
1513  const void *ptr, size_t n)
1514 {
1515  assert(boot);
1516 
1517  // Retry the SDO request on timeout (this includes the first attempt).
1518  if (ac == CO_SDO_AC_TIMEOUT && boot->retry--) {
1519  // Read the program software identification of the slave
1520  // (sub-object 1F56:01).
1521  if (co_nmt_boot_up(boot, 0x1f56, 0x01) == -1)
1522  return co_nmt_boot_abort_state;
1523  return NULL;
1524  } else if (ac) {
1525  diag(DIAG_ERROR, 0,
1526  "SDO abort code %08" PRIX32
1527  " received on upload request of sub-object 1F56:01 (Program software identification) to node %02X: %s",
1528  ac, boot->id, co_sdo_ac2str(ac));
1529  return co_nmt_boot_abort_state;
1530  }
1531 
1532  // If the program software identification matches the expected value,
1533  // proceed to 'check configuration'.
1534  if (co_nmt_boot_chk(boot, 0x1f55, boot->id, ptr, n))
1536 
1537  // Do not update the software if software update (bit 6) is not allowed
1538  // or if the keep-alive bit (bit 4) is set.
1539  if ((boot->assignment & 0x50) != 0x40) {
1540  boot->es = 'H';
1541  return co_nmt_boot_abort_state;
1542  }
1543 
1544  boot->es = 'I';
1545 
1547 }
1548 
1549 static co_nmt_boot_state_t *
1551 {
1552  assert(boot);
1553 
1554  // Read the program control of the slave (sub-object 1F51:01).
1555  if (co_nmt_boot_up(boot, 0x1f51, 0x01) == -1)
1556  return co_nmt_boot_abort_state;
1557 
1558  return NULL;
1559 }
1560 
1561 static co_nmt_boot_state_t *
1563 {
1564  assert(boot);
1565 
1566  // The download SDO request may be unconfirmed on some devices since it
1567  // stops the program on the slave (and may cause a restart of the
1568  // bootloader). We therefore ignore timeouts.
1569  if (ac && ac != CO_SDO_AC_TIMEOUT) {
1570  diag(DIAG_ERROR, 0,
1571  "SDO abort code %08" PRIX32
1572  " received on download request of sub-object 1F51:01 (Program control) to node %02X: %s",
1573  ac, boot->id, co_sdo_ac2str(ac));
1574  return co_nmt_boot_abort_state;
1575  }
1576 
1578 }
1579 
1580 static co_nmt_boot_state_t *
1582  const void *ptr, size_t n)
1583 {
1584  assert(boot);
1585 
1586  // If the value is already 0 (Program stopped), do not write a 0 (Stop
1587  // program), but skip to the 'clear program' state.
1588  co_unsigned8_t val = 0;
1589  // clang-format off
1590  if (!ac && co_val_read(CO_DEFTYPE_UNSIGNED8, &val, ptr,
1591  (const uint_least8_t *)ptr + n) && !val)
1592  // clang-format on
1594 
1595  // Write a 0 (Stop program) to the program control of the slave
1596  // (sub-object 1F51:01).
1597  // clang-format off
1598  if (co_nmt_boot_dn(boot, 0x1f51, 0x01, CO_DEFTYPE_UNSIGNED8,
1599  &(co_unsigned8_t){ 0 }) == -1)
1600  // clang-format on
1601  return co_nmt_boot_abort_state;
1602 
1603  return NULL;
1604 }
1605 
1606 static co_nmt_boot_state_t *
1608 {
1609  assert(boot);
1610 
1611  // The 'clear program' command follows the 'stop program' command, which
1612  // may have triggered a reboot of the slave. In that case we may have to
1613  // give the slave some time to finish booting. Start the first SDO
1614  // request by simulating a timeout.
1615  boot->retry = LELY_CO_NMT_BOOT_SDO_RETRY + 1;
1617 }
1618 
1619 static co_nmt_boot_state_t *
1621 {
1622  assert(boot);
1623 
1624  // Retry the SDO request on timeout.
1625  if (ac == CO_SDO_AC_TIMEOUT && boot->retry--) {
1626  // Write a 3 (Clear program) to the program control of the slave
1627  // (sub-object 1F51:01).
1628  // clang-format off
1629  if (co_nmt_boot_dn(boot, 0x1f51, 0x01, CO_DEFTYPE_UNSIGNED8,
1630  &(co_unsigned8_t){ 3 }) == -1)
1631  // clang-format on
1632  return co_nmt_boot_abort_state;
1633  return NULL;
1634  } else if (ac) {
1635  diag(DIAG_ERROR, 0,
1636  "SDO abort code %08" PRIX32
1637  " received on download request of sub-object 1F51:01 (Program control) to node %02X: %s",
1638  ac, boot->id, co_sdo_ac2str(ac));
1639  return co_nmt_boot_abort_state;
1640  }
1641 
1643 }
1644 
1645 static co_nmt_boot_state_t *
1647 {
1648  assert(boot);
1649 
1650  co_sub_t *sub = co_dev_find_sub(boot->dev, 0x1f58, boot->id);
1651  if (!sub)
1652  return co_nmt_boot_abort_state;
1653 
1654  // Upload the program data.
1655  struct co_sdo_req *req = &boot->req;
1656  co_sdo_req_clear(req);
1657  co_unsigned32_t ac = co_sub_up_ind(sub, req);
1658  if (ac || !co_sdo_req_first(req) || !co_sdo_req_last(req)) {
1659  if (ac)
1660  diag(DIAG_ERROR, 0,
1661  "SDO abort code %08" PRIX32
1662  " on upload request of object 1F58:%02X (Program data): %s",
1663  ac, boot->id, co_sdo_ac2str(ac));
1664  return co_nmt_boot_abort_state;
1665  }
1666 
1667  // The 'clear program' step may take some time to complete, causing an
1668  // immediate 'download program' to generate a timeout. Start the first
1669  // attempt by simulating a timeout.
1670  boot->retry = LELY_CO_NMT_BOOT_SDO_RETRY + 1;
1672 }
1673 
1674 static co_nmt_boot_state_t *
1676 {
1677  (void)boot;
1678 
1679  // Retry the SDO request on timeout (this includes the first attempt).
1680  if (ac == CO_SDO_AC_TIMEOUT && boot->retry--) {
1681  struct co_sdo_req *req = &boot->req;
1682  // Write the program data (sub-object 1F58:ID) to the program
1683  // data of the slave (sub-object 1F50:01) using SDO block
1684  // transfer.
1685  // clang-format off
1686  if (co_csdo_blk_dn_req(boot->sdo, 0x1f50, 0x01, req->buf,
1687  req->size, &co_nmt_boot_dn_con, boot) == -1)
1688  // clang-format on
1689  return co_nmt_boot_abort_state;
1690  return NULL;
1691  } else if (ac) {
1692  // If SDO block transfer is not supported, fall back to SDO
1693  // segmented transfer.
1695  }
1696 
1698 }
1699 
1700 static co_nmt_boot_state_t *
1702 {
1703  assert(boot);
1704 
1705  // If SDO block transfer is not supported, we may still have to wait for
1706  // the 'clear program' step to complete before successfully doing a
1707  // segmented SDO transfer. Start the first attempt by simulating a
1708  // timeout.
1709  boot->retry = LELY_CO_NMT_BOOT_SDO_RETRY + 1;
1711 }
1712 
1713 static co_nmt_boot_state_t *
1715 {
1716  assert(boot);
1717 
1718  // Retry the SDO request on timeout (this includes the first attempt).
1719  if (ac == CO_SDO_AC_TIMEOUT && boot->retry--) {
1720  struct co_sdo_req *req = &boot->req;
1721  // Write the program data (sub-object 1F58:ID) to the program
1722  // data of the slave (sub-object 1F50:01) using SDO segmented
1723  // transfer.
1724  // clang-format off
1725  if (co_csdo_dn_req(boot->sdo, 0x1f50, 0x01, req->buf, req->size,
1726  &co_nmt_boot_dn_con, boot) == -1)
1727  // clang-format on
1728  return co_nmt_boot_abort_state;
1729  return NULL;
1730  } else if (ac) {
1731  diag(DIAG_ERROR, 0,
1732  "SDO abort code %08" PRIX32
1733  " received on download request of sub-object 1F50:01 (Program data) to node %02X: %s",
1734  ac, boot->id, co_sdo_ac2str(ac));
1735  return co_nmt_boot_abort_state;
1736  }
1737 
1739 }
1740 
1741 static co_nmt_boot_state_t *
1743 {
1744  assert(boot);
1745 
1746  // Wait for a while before checking the flash status indication.
1748  boot->timer, boot->net, LELY_CO_NMT_BOOT_CHECK_TIMEOUT);
1749 
1750  return NULL;
1751 }
1752 
1753 static co_nmt_boot_state_t *
1754 co_nmt_boot_wait_flash_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
1755 {
1756  assert(boot);
1757  (void)tp;
1758 
1759  // Read the flash status indication of the slave (sub-object 1F57:01).
1760  if (co_nmt_boot_up(boot, 0x1f57, 0x01) == -1)
1761  return co_nmt_boot_abort_state;
1762 
1763  return NULL;
1764 }
1765 
1766 static co_nmt_boot_state_t *
1768  const void *ptr, size_t n)
1769 {
1770  assert(boot);
1771 
1772  if (ac)
1773  diag(DIAG_ERROR, 0,
1774  "SDO abort code %08" PRIX32
1775  " received on upload request of sub-object 1F57:01 (Flash status indication) to node %02X: %s",
1776  ac, boot->id, co_sdo_ac2str(ac));
1777 
1778  // If the flash status indication is not valid, try again.
1779  co_unsigned32_t val = 0;
1780  // clang-format off
1781  if (!co_val_read(CO_DEFTYPE_UNSIGNED32, &val, ptr,
1782  (const uint_least8_t *)ptr + n) || (val & 0x01))
1783  // clang-format on
1785 
1786  co_unsigned8_t st = (val >> 1) & 0x7f;
1787  switch (st) {
1788  case 0: return co_nmt_boot_chk_prog_state;
1789  case 1:
1790  diag(DIAG_ERROR, 0,
1791  "flash status identification %d: No valid program available",
1792  st);
1793  break;
1794  case 2:
1795  diag(DIAG_ERROR, 0,
1796  "flash status identification %d: Data format unknown",
1797  st);
1798  break;
1799  case 3:
1800  diag(DIAG_ERROR, 0,
1801  "flash status identification %d: Data format error or data CRC error",
1802  st);
1803  break;
1804  case 4:
1805  diag(DIAG_ERROR, 0,
1806  "flash status identification %d: Flash not cleared before write",
1807  st);
1808  break;
1809  case 5:
1810  diag(DIAG_ERROR, 0,
1811  "flash status identification %d: Flash write error",
1812  st);
1813  break;
1814  case 6:
1815  diag(DIAG_ERROR, 0,
1816  "flash status identification %d: General address error",
1817  st);
1818  break;
1819  case 7:
1820  diag(DIAG_ERROR, 0,
1821  "flash status identification %d: Flash secured (= write access currently forbidden)",
1822  st);
1823  break;
1824  case 63:
1825  diag(DIAG_ERROR, 0,
1826  "flash status identification %d: Unspecified error",
1827  st);
1828  break;
1829  default:
1830  if (st > 63)
1831  diag(DIAG_ERROR, 0,
1832  "flash status identification %d: Manufacturer-specific error: 0x%08" PRIX32
1833  "",
1834  st, (val >> 16) & 0xffff);
1835  break;
1836  }
1837 
1838  return co_nmt_boot_abort_state;
1839 }
1840 
1841 static co_nmt_boot_state_t *
1843 {
1844  assert(boot);
1845 
1846  // Read the program software identification of the slave (sub-object
1847  // 1F56:01).
1848  if (co_nmt_boot_up(boot, 0x1f56, 0x01) == -1)
1849  return co_nmt_boot_abort_state;
1850 
1851  return NULL;
1852 }
1853 
1854 static co_nmt_boot_state_t *
1856  const void *ptr, size_t n)
1857 {
1858  assert(boot);
1859 
1860  if (ac)
1861  diag(DIAG_ERROR, 0,
1862  "SDO abort code %08" PRIX32
1863  " received on upload request of sub-object 1F56:01 (Program software identification) to node %02X: %s",
1864  ac, boot->id, co_sdo_ac2str(ac));
1865 
1866  if (ac || !co_nmt_boot_chk(boot, 0x1f55, boot->id, ptr, n))
1867  return co_nmt_boot_abort_state;
1868 
1870 }
1871 
1872 static co_nmt_boot_state_t *
1874 {
1875  assert(boot);
1876 
1877  // Write a 1 (Start program) to the program control of the slave
1878  // (sub-object 1F51:01).
1879  // clang-format off
1880  if (co_nmt_boot_dn(boot, 0x1f51, 0x01, CO_DEFTYPE_UNSIGNED8,
1881  &(co_unsigned8_t){ 1 }) == -1)
1882  // clang-format on
1883  return co_nmt_boot_abort_state;
1884 
1885  return NULL;
1886 }
1887 
1888 static co_nmt_boot_state_t *
1890 {
1891  assert(boot);
1892 
1893  if (ac) {
1894  diag(DIAG_ERROR, 0,
1895  "SDO abort code %08" PRIX32
1896  " received on download request of sub-object 1F51:01 (Program control) to node %02X: %s",
1897  ac, boot->id, co_sdo_ac2str(ac));
1898  return co_nmt_boot_abort_state;
1899  }
1900 
1902 }
1903 
1904 static co_nmt_boot_state_t *
1906 {
1907  assert(boot);
1908 
1909  // Wait for a while before checking the program control.
1911  boot->timer, boot->net, LELY_CO_NMT_BOOT_CHECK_TIMEOUT);
1912 
1913  return NULL;
1914 }
1915 
1916 static co_nmt_boot_state_t *
1917 co_nmt_boot_wait_prog_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
1918 {
1919  assert(boot);
1920  (void)tp;
1921 
1922  // The 'start program' step may take some time to complete, causing an
1923  // immediate SDO upload request to generate a timeout. Start the first
1924  // attempt by simulating a timeout.
1925  boot->retry = LELY_CO_NMT_BOOT_SDO_RETRY + 1;
1927  boot, CO_SDO_AC_TIMEOUT, NULL, 0);
1928 
1929  return NULL;
1930 }
1931 
1932 static co_nmt_boot_state_t *
1934  const void *ptr, size_t n)
1935 {
1936  assert(boot);
1937 
1938  // Retry the SDO request on timeout (this includes the first attempt).
1939  if (ac == CO_SDO_AC_TIMEOUT && boot->retry--) {
1940  // Read the program control of the slave (sub-object 1F51:01).
1941  if (co_nmt_boot_up(boot, 0x1f51, 0x01) == -1)
1942  return co_nmt_boot_abort_state;
1943  return NULL;
1944  } else if (ac) {
1945  diag(DIAG_ERROR, 0,
1946  "SDO abort code %08" PRIX32
1947  " received on upload request of sub-object 1F51:01 (Program control) to node %02X: %s",
1948  ac, boot->id, co_sdo_ac2str(ac));
1949  return co_nmt_boot_abort_state;
1950  }
1951 
1952  // If the program control differs from 'Program started', try again.
1953  co_unsigned8_t val = 0;
1954  // clang-format off
1955  if (!co_val_read(CO_DEFTYPE_UNSIGNED8, &val, ptr,
1956  (const uint_least8_t *)ptr + n) || val != 1)
1957  // clang-format on
1959 
1961 }
1962 
1963 static co_nmt_boot_state_t *
1965 {
1966  assert(boot);
1967 
1968  boot->es = 'J';
1969 
1970  // If the expected configuration date (sub-object 1F26:ID) or time
1971  // (sub-object 1F27:ID) are not configured, proceed to 'update
1972  // configuration'.
1973  co_unsigned32_t cfg_date =
1974  co_dev_get_val_u32(boot->dev, 0x1f26, boot->id);
1975  co_unsigned32_t cfg_time =
1976  co_dev_get_val_u32(boot->dev, 0x1f27, boot->id);
1977  if (!cfg_date || !cfg_time)
1978  return co_nmt_boot_up_cfg_state;
1979 
1980  // The configuration check may follow an NMT 'reset communication'
1981  // command (if the 'check software version' step was skipped), in which
1982  // case we may have to give the slave some time to complete the state
1983  // change. Start the first SDO request by simulating a timeout.
1984  boot->retry = LELY_CO_NMT_BOOT_SDO_RETRY + 1;
1986  boot, CO_SDO_AC_TIMEOUT, NULL, 0);
1987 }
1988 
1989 static co_nmt_boot_state_t *
1991  const void *ptr, size_t n)
1992 {
1993  assert(boot);
1994 
1995  // Retry the SDO request on timeout (this includes the first attempt).
1996  if (ac == CO_SDO_AC_TIMEOUT && boot->retry--) {
1997  // Read the configuration date of the slave (sub-object
1998  // 1020:01).
1999  if (co_nmt_boot_up(boot, 0x1020, 0x01) == -1)
2000  return co_nmt_boot_abort_state;
2001  return NULL;
2002  } else if (ac) {
2003  diag(DIAG_ERROR, 0,
2004  "SDO abort code %08" PRIX32
2005  " received on upload request of sub-object 1020:01 (Configuration date) to node %02X: %s",
2006  ac, boot->id, co_sdo_ac2str(ac));
2007  }
2008 
2009  // If the configuration date does not match the expected value, skip
2010  // checking the time and proceed to 'update configuration'.
2011  if (ac || !co_nmt_boot_chk(boot, 0x1f26, boot->id, ptr, n))
2012  return co_nmt_boot_up_cfg_state;
2013 
2014  // Read the configuration time of the slave (sub-object 1020:02).
2015  if (co_nmt_boot_up(boot, 0x1020, 0x02) == -1)
2016  return co_nmt_boot_abort_state;
2017 
2019 }
2020 
2021 static co_nmt_boot_state_t *
2023  const void *ptr, size_t n)
2024 {
2025  assert(boot);
2026 
2027  if (ac)
2028  diag(DIAG_ERROR, 0,
2029  "SDO abort code %08" PRIX32
2030  " received on upload request of sub-object 1020:02 (Configuration time) to node %02X: %s",
2031  ac, boot->id, co_sdo_ac2str(ac));
2032 
2033  // If the configuration time does not match the expected value, proceed
2034  // to 'update configuration'.
2035  if (ac || !co_nmt_boot_chk(boot, 0x1f27, boot->id, ptr, n))
2036  return co_nmt_boot_up_cfg_state;
2037 
2038  return co_nmt_boot_ec_state;
2039 }
2040 
2041 static co_nmt_boot_state_t *
2043 {
2044  assert(boot);
2045 
2046  boot->es = 'J';
2047 
2048  // clang-format off
2049  if (co_nmt_cfg_req(boot->nmt, boot->id, boot->timeout,
2050  &co_nmt_boot_cfg_con, boot) == -1)
2051  // clang-format on
2052  return co_nmt_boot_abort_state;
2053 
2054  return NULL;
2055 }
2056 
2057 static co_nmt_boot_state_t *
2059 {
2060  assert(boot);
2061 
2062  if (ac) {
2063  diag(DIAG_ERROR, 0,
2064  "SDO abort code %08" PRIX32
2065  " received while updating the configuration of node %02X: %s",
2066  ac, boot->id, co_sdo_ac2str(ac));
2067  return co_nmt_boot_abort_state;
2068  }
2069 
2070  return co_nmt_boot_ec_state;
2071 }
2072 
2073 static co_nmt_boot_state_t *
2075 {
2076  assert(boot);
2077 
2078  if (boot->ms) {
2079  boot->es = 'K';
2080  // Start the CAN frame receiver for heartbeat messages.
2081  can_recv_start(boot->recv, boot->net, CO_NMT_EC_CANID(boot->id),
2082  0);
2083  // Wait for the first heartbeat indication.
2084  can_timer_timeout(boot->timer, boot->net, boot->ms);
2085  return NULL;
2086  } else if (boot->assignment & 0x01) {
2087  // If the guard time is non-zero, start node guarding by sending
2088  // the first RTR, but do not wait for the response.
2089  co_unsigned16_t gt = (boot->assignment >> 16) & 0xffff;
2090  if (gt)
2091  co_nmt_boot_send_rtr(boot);
2092  }
2093 
2094  boot->es = 0;
2095  return co_nmt_boot_abort_state;
2096 }
2097 
2098 static co_nmt_boot_state_t *
2100 {
2101  assert(boot);
2102  assert(msg);
2103  assert(msg->id == (uint_least32_t)CO_NMT_EC_CANID(boot->id));
2104 
2105  if (msg->len >= 1) {
2106  co_unsigned8_t st = msg->data[0];
2107  // Do not consider a boot-up message to be a heartbeat message.
2108  if (st == CO_NMT_ST_BOOTUP)
2109  return NULL;
2110  boot->st = st;
2111  boot->es = 0;
2112  }
2113 
2114  return co_nmt_boot_abort_state;
2115 }
2116 
2117 static co_nmt_boot_state_t *
2118 co_nmt_boot_ec_on_time(co_nmt_boot_t *boot, const struct timespec *tp)
2119 {
2120  (void)boot;
2121  (void)tp;
2122 
2123  return co_nmt_boot_abort_state;
2124 }
2125 
2126 static int
2127 co_nmt_boot_dn(co_nmt_boot_t *boot, co_unsigned16_t idx, co_unsigned8_t subidx,
2128  co_unsigned16_t type, const void *val)
2129 {
2130  assert(boot);
2131 
2132  return co_csdo_dn_val_req(boot->sdo, idx, subidx, type, val,
2133  &co_nmt_boot_dn_con, boot);
2134 }
2135 
2136 static int
2137 co_nmt_boot_up(co_nmt_boot_t *boot, co_unsigned16_t idx, co_unsigned8_t subidx)
2138 {
2139  assert(boot);
2140 
2141  return co_csdo_up_req(
2142  boot->sdo, idx, subidx, &co_nmt_boot_up_con, boot);
2143 }
2144 
2145 static int
2146 co_nmt_boot_chk(co_nmt_boot_t *boot, co_unsigned16_t idx, co_unsigned8_t subidx,
2147  const void *ptr, size_t n)
2148 {
2149  assert(boot);
2150 
2151  co_sub_t *sub = co_dev_find_sub(boot->dev, idx, subidx);
2152  if (!sub)
2153  return 0;
2154  co_unsigned16_t type = co_sub_get_type(sub);
2155 
2156  union co_val val;
2157  if (!co_val_read(type, &val, ptr, (const uint_least8_t *)ptr + n))
2158  return 0;
2159 
2160  int eq = !co_val_cmp(type, &val, co_sub_get_val(sub));
2161  co_val_fini(type, &val);
2162  return eq;
2163 }
2164 
2165 static int
2167 {
2168  assert(boot);
2169 
2170  struct can_msg msg = CAN_MSG_INIT;
2171  msg.id = CO_NMT_EC_CANID(boot->id);
2172  msg.flags |= CAN_FLAG_RTR;
2173 
2174  return can_net_send(boot->net, &msg);
2175 }
2176 
2177 #endif // !LELY_NO_CO_MASTER
@ 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:299
#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:288
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:868
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:1079
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:1012
void co_csdo_set_timeout(co_csdo_t *sdo, int timeout)
Sets the timeout of a Client-SDO.
Definition: csdo.c:945
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:1098
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:987
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:967
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:1034
void co_csdo_destroy(co_csdo_t *sdo)
Destroys a CANopen Client-SDO service.
Definition: csdo.c:895
This header file is part of the utilities library; it contains the diagnostic declarations.
@ DIAG_ERROR
An error.
Definition: diag.h:49
void diag(enum diag_severity severity, int errc, const char *format,...)
Emits a diagnostic message.
Definition: diag.c:156
@ ERRNUM_INVAL
Invalid argument.
Definition: errnum.h:129
@ ERRNUM_INPROGRESS
Operation in progress.
Definition: errnum.h:125
int get_errc(void)
Returns the last (thread-specific) native error code set by a system call or library function.
Definition: errnum.c:947
void set_errc(int errc)
Sets the current (thread-specific) native error code to errc.
Definition: errnum.c:957
int errno2c(int errnum)
Transforms a standard C error number to a native error code.
Definition: errnum.c:43
void set_errnum(errnum_t errnum)
Sets the current (thread-specific) platform-independent error number to errnum.
Definition: errnum.h:375
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:679
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:1019
co_unsigned16_t co_sub_get_type(const co_sub_t *sub)
Returns the data type of a CANopen sub-object.
Definition: obj.c:570
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 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:346
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:468
int can_net_send(can_net_t *net, const struct can_msg *msg)
Sends a CAN frame from a network interface.
Definition: net.c:308
void can_net_get_time(const can_net_t *net, struct timespec *tp)
Retrieves the current time of a CAN network interface.
Definition: net.c:204
void can_timer_set_func(can_timer_t *timer, can_timer_func_t *func, void *data)
Sets the callback function invoked when a CAN timer is triggered.
Definition: net.c:428
can_recv_t * can_recv_create(void)
Creates a new CAN frame receiver.
Definition: net.c:537
void can_recv_stop(can_recv_t *recv)
Stops a CAN frame receiver from processing frames and unregisters it with the network interface.
Definition: net.c:613
void can_recv_set_func(can_recv_t *recv, can_recv_func_t *func, void *data)
Sets the callback function used to process CAN frames with a receiver.
Definition: net.c:582
void can_recv_destroy(can_recv_t *recv)
Destroys a CAN frame receiver.
Definition: net.c:562
can_timer_t * can_timer_create(void)
Creates a new CAN timer.
Definition: net.c:382
void can_timer_timeout(can_timer_t *timer, can_net_t *net, int timeout)
Starts a CAN timer and registers it with a network interface.
Definition: net.c:484
void can_recv_start(can_recv_t *recv, can_net_t *net, uint_least32_t id, uint_least8_t flags)
Registers a CAN frame receiver with a network interface and starts processing frames.
Definition: net.c:591
void can_timer_destroy(can_timer_t *timer)
Destroys a CAN timer.
Definition: net.c:407
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:1977
#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:1701
#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:1569
#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:1464
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:2058
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:410
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:504
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:690
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:1041
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:1153
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:1296
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:1905
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:1374
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:585
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:2118
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:1873
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:1855
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:542
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:2146
static co_nmt_boot_state_t *const co_nmt_boot_up_cfg_state
The 'update configuration' state (see Fig.
Definition: nmt_boot.c:710
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:2099
#define LELY_CO_NMT_BOOT_RTR_TIMEOUT
The timeout (in milliseconds) after sending a node guarding RTR.
Definition: nmt_boot.c:51
static void co_nmt_boot_error_on_leave(co_nmt_boot_t *boot)
The exit function of the 'error' state.
Definition: nmt_boot.c:1161
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:1062
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:1620
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:1646
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:1964
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:1169
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:2022
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:1742
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:1933
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:1005
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:980
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:1714
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:651
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:1072
void co_nmt_boot_destroy(co_nmt_boot_t *boot)
Destroys a CANopen NMT 'boot slave' service.
Definition: nmt_boot.c:902
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:1356
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:993
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:1607
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:1581
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:370
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:1842
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:1512
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:1990
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:2074
#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:67
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:436
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:1675
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:967
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:1257
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:955
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:1031
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:877
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:390
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:1407
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:733
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:1239
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:1562
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:911
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:350
#define LELY_CO_NMT_BOOT_WAIT_TIMEOUT
The timeout (in milliseconds) before trying to boot the slave again.
Definition: nmt_boot.c:41
#define LELY_CO_NMT_BOOT_RESET_TIMEOUT
The timeout (in milliseconds) after sending the NMT 'reset communication' command.
Definition: nmt_boot.c:59
static int co_nmt_boot_send_rtr(co_nmt_boot_t *boot)
Sends a node guarding RTR to the slave.
Definition: nmt_boot.c:2166
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:1889
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:2127
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:622
static co_nmt_boot_state_t *const co_nmt_boot_wait_state
The 'wait asynchronously' state.
Definition: nmt_boot.c:286
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:603
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:1754
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:560
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:1185
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:1701
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:460
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:1442
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:2137
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:943
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:1335
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:1051
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:1218
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:1550
static co_nmt_boot_state_t *const co_nmt_boot_error_state
The 'error' state.
Definition: nmt_boot.c:310
#define LELY_CO_NMT_BOOT_SDO_RETRY
The number of times an SDO request is retried after a timeout.
Definition: nmt_boot.c:46
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:1433
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:330
static co_nmt_boot_state_t *const co_nmt_boot_abort_state
The 'abort' state.
Definition: nmt_boot.c:296
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:673
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:1278
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:478
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:1317
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:1483
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:523
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:2042
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:1917
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:1111
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:1767
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:1021
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:1450
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:1474
This is the internal header file of the NMT 'boot slave' declarations.
This is the internal header file of the CANopen library.
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib....
A CAN network interface.
Definition: net.c:37
A CAN frame receiver.
Definition: net.c:99
A CAN timer.
Definition: net.c:63
A CANopen Client-SDO.
Definition: csdo.c:45
A CANopen device.
Definition: dev.c:41
A CANopen NMT 'boot slave' state.
Definition: nmt_boot.c:211
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:233
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:223
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:257
void(* on_leave)(co_nmt_boot_t *boot)
A pointer to the function invoked when the current state is left.
Definition: nmt_boot.c:271
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:213
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:268
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:244
A CANopen NMT 'boot slave' service.
Definition: nmt_boot.c:75
can_recv_t * recv
A pointer to the CAN frame receiver.
Definition: nmt_boot.c:85
struct co_sdo_req req
The CANopen SDO upload request used for reading sub-objects.
Definition: nmt_boot.c:101
can_timer_t * timer
A pointer to the CAN timer.
Definition: nmt_boot.c:87
co_nmt_t * nmt
A pointer to an NMT master service.
Definition: nmt_boot.c:81
can_net_t * net
A pointer to a CAN network interface.
Definition: nmt_boot.c:77
co_nmt_boot_state_t * state
A pointer to the current state.
Definition: nmt_boot.c:83
co_unsigned32_t assignment
The NMT slave assignment (object 1F81).
Definition: nmt_boot.c:97
co_unsigned16_t ms
The consumer heartbeat time (in milliseconds).
Definition: nmt_boot.c:99
int retry
The number of SDO retries remaining.
Definition: nmt_boot.c:103
co_csdo_t * sdo
A pointer to the Client-SDO used to access slave objects.
Definition: nmt_boot.c:93
co_unsigned8_t id
The node-ID.
Definition: nmt_boot.c:89
co_dev_t * dev
A pointer to a CANopen device.
Definition: nmt_boot.c:79
char es
The error status.
Definition: nmt_boot.c:107
struct timespec start
The time at which the 'boot slave' request was received.
Definition: nmt_boot.c:95
int timeout
The SDO timeout (in milliseconds).
Definition: nmt_boot.c:91
co_unsigned8_t st
The state of the node (including the toggle bit).
Definition: nmt_boot.c:105
A CANopen NMT master/slave service.
Definition: nmt.c:104
A CANopen object.
Definition: obj.h:32
A CANopen sub-object.
Definition: obj.h:54
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:178
size_t size
The total size (in bytes) of the value to be uploaded/downloaded.
Definition: sdo.h:184
const void * buf
A pointer to the next bytes to be uploaded/downloaded.
Definition: sdo.h:186
#define CO_DEFTYPE_UNSIGNED8
The data type (and object index) of an 8-bit unsigned integer.
Definition: type.h:44
#define CO_DEFTYPE_UNSIGNED32
The data type (and object index) of a 32-bit unsigned integer.
Definition: type.h:50
A union of the CANopen static data types.
Definition: val.h:163
This header file is part of the utilities library; it contains the time function declarations.
int_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:207
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:481
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:397
void co_val_fini(co_unsigned16_t type, void *val)
Finalizes a value of the specified data type.
Definition: val.c:275