Lely core libraries  1.9.2
csdo.c
Go to the documentation of this file.
1 
24 #include "co.h"
25 
26 #ifndef LELY_NO_CO_CSDO
27 
28 #include "sdo.h"
29 #include <lely/co/crc.h>
30 #include <lely/co/csdo.h>
31 #include <lely/co/dev.h>
32 #include <lely/co/obj.h>
33 #include <lely/co/val.h>
34 #include <lely/util/endian.h>
35 #include <lely/util/errnum.h>
36 
37 #include <assert.h>
38 #include <stdlib.h>
39 
40 struct __co_csdo_state;
42 typedef const struct __co_csdo_state co_csdo_state_t;
43 
45 struct __co_csdo {
51  co_unsigned8_t num;
53  struct co_sdo_par par;
57  int timeout;
63  co_unsigned32_t ac;
65  co_unsigned16_t idx;
67  co_unsigned8_t subidx;
69  uint32_t size;
71  uint8_t toggle;
73  uint8_t blksize;
75  uint8_t ackseq;
77  unsigned crc : 1;
79  struct membuf buf;
83  void *dn_con_data;
87  void *dn_ind_data;
91  void *up_con_data;
95  void *up_ind_data;
96 };
97 
104 static int co_csdo_update(co_csdo_t *sdo);
105 
112 static co_unsigned32_t co_1280_dn_ind(
113  co_sub_t *sub, struct co_sdo_req *req, void *data);
114 
120 static int co_csdo_recv(const struct can_msg *msg, void *data);
121 
127 static int co_csdo_timer(const struct timespec *tp, void *data);
128 
133 static inline void co_csdo_enter(co_csdo_t *sdo, co_csdo_state_t *next);
134 
142 static inline void co_csdo_emit_abort(co_csdo_t *sdo, co_unsigned32_t ac);
143 
151 static inline void co_csdo_emit_time(co_csdo_t *sdo, const struct timespec *tp);
152 
160 static inline void co_csdo_emit_recv(co_csdo_t *sdo, const struct can_msg *msg);
161 
165  co_csdo_state_t *(*on_enter)(co_csdo_t *sdo);
175  co_csdo_state_t *(*on_abort)(co_csdo_t *sdo, co_unsigned32_t ac);
184  co_csdo_state_t *(*on_time)(co_csdo_t *sdo, const struct timespec *tp);
194  co_csdo_state_t *(*on_recv)(co_csdo_t *sdo, const struct can_msg *msg);
196  void (*on_leave)(co_csdo_t *sdo);
197 };
198 
199 #define LELY_CO_DEFINE_STATE(name, ...) \
200  static co_csdo_state_t *const name = &(co_csdo_state_t){ __VA_ARGS__ };
201 
204  co_csdo_t *sdo, co_unsigned32_t ac);
205 
208  co_csdo_t *sdo, const struct can_msg *msg);
209 
211 // clang-format off
212 LELY_CO_DEFINE_STATE(co_csdo_wait_state,
213  .on_abort = &co_csdo_wait_on_abort,
214  .on_recv = &co_csdo_wait_on_recv
215 )
216 // clang-format on
217 
220 
222 static void co_csdo_abort_on_leave(co_csdo_t *sdo);
223 
225 // clang-format off
226 LELY_CO_DEFINE_STATE(co_csdo_abort_state,
227  .on_enter = &co_csdo_abort_on_enter,
228  .on_leave = &co_csdo_abort_on_leave
229 )
230 // clang-format on
231 
234  co_csdo_t *sdo, co_unsigned32_t ac);
235 
238  co_csdo_t *sdo, const struct timespec *tp);
239 
245  co_csdo_t *sdo, const struct can_msg *msg);
246 
248 // clang-format off
249 LELY_CO_DEFINE_STATE(co_csdo_dn_ini_state,
250  .on_abort = &co_csdo_dn_ini_on_abort,
251  .on_time = &co_csdo_dn_ini_on_time,
252  .on_recv = &co_csdo_dn_ini_on_recv
253 )
254 // clang-format on
255 
258 
261  co_csdo_t *sdo, co_unsigned32_t ac);
262 
265  co_csdo_t *sdo, const struct timespec *tp);
266 
271  co_csdo_t *sdo, const struct can_msg *msg);
272 
274 // clang-format off
275 LELY_CO_DEFINE_STATE(co_csdo_dn_seg_state,
276  .on_enter = &co_csdo_dn_seg_on_enter,
277  .on_abort = &co_csdo_dn_seg_on_abort,
278  .on_time = &co_csdo_dn_seg_on_time,
279  .on_recv = &co_csdo_dn_seg_on_recv
280 )
281 // clang-format on
282 
285  co_csdo_t *sdo, co_unsigned32_t ac);
286 
289  co_csdo_t *sdo, const struct timespec *tp);
290 
293  co_csdo_t *sdo, const struct can_msg *msg);
294 
296 // clang-format off
297 LELY_CO_DEFINE_STATE(co_csdo_up_ini_state,
298  .on_abort = &co_csdo_up_ini_on_abort,
299  .on_time = &co_csdo_up_ini_on_time,
300  .on_recv = &co_csdo_up_ini_on_recv
301 )
302 // clang-format on
303 
306  co_csdo_t *sdo, co_unsigned32_t ac);
307 
310  co_csdo_t *sdo, const struct timespec *tp);
311 
314  co_csdo_t *sdo, const struct can_msg *msg);
315 
317 // clang-format off
318 LELY_CO_DEFINE_STATE(co_csdo_up_seg_state,
319  .on_abort = &co_csdo_up_seg_on_abort,
320  .on_time = &co_csdo_up_seg_on_time,
321  .on_recv = &co_csdo_up_seg_on_recv
322 )
323 // clang-format on
324 
327  co_csdo_t *sdo, co_unsigned32_t ac);
328 
331  co_csdo_t *sdo, const struct timespec *tp);
332 
338  co_csdo_t *sdo, const struct can_msg *msg);
339 
341 // clang-format off
342 LELY_CO_DEFINE_STATE(co_csdo_blk_dn_ini_state,
343  .on_abort = &co_csdo_blk_dn_ini_on_abort,
344  .on_time = &co_csdo_blk_dn_ini_on_time,
345  .on_recv = &co_csdo_blk_dn_ini_on_recv
346 )
347 // clang-format on
348 
351 
354  co_csdo_t *sdo, co_unsigned32_t ac);
355 
358  co_csdo_t *sdo, const struct timespec *tp);
359 
365  co_csdo_t *sdo, const struct can_msg *msg);
366 
368 // clang-format off
369 LELY_CO_DEFINE_STATE(co_csdo_blk_dn_sub_state,
370  .on_enter = &co_csdo_blk_dn_sub_on_enter,
371  .on_abort = &co_csdo_blk_dn_sub_on_abort,
372  .on_time = &co_csdo_blk_dn_sub_on_time,
373  .on_recv = &co_csdo_blk_dn_sub_on_recv
374 )
375 // clang-format on
376 
379  co_csdo_t *sdo, co_unsigned32_t ac);
380 
383  co_csdo_t *sdo, const struct timespec *tp);
384 
390  co_csdo_t *sdo, const struct can_msg *msg);
391 
393 // clang-format off
394 LELY_CO_DEFINE_STATE(co_csdo_blk_dn_end_state,
395  .on_abort = &co_csdo_blk_dn_end_on_abort,
396  .on_time = &co_csdo_blk_dn_end_on_time,
397  .on_recv = &co_csdo_blk_dn_end_on_recv
398 )
399 // clang-format on
400 
403  co_csdo_t *sdo, co_unsigned32_t ac);
404 
407  co_csdo_t *sdo, const struct timespec *tp);
408 
414  co_csdo_t *sdo, const struct can_msg *msg);
415 
417 // clang-format off
418 LELY_CO_DEFINE_STATE(co_csdo_blk_up_ini_state,
419  .on_abort = &co_csdo_blk_up_ini_on_abort,
420  .on_time = &co_csdo_blk_up_ini_on_time,
421  .on_recv = &co_csdo_blk_up_ini_on_recv
422 )
423 // clang-format on
424 
427  co_csdo_t *sdo, co_unsigned32_t ac);
428 
431  co_csdo_t *sdo, const struct timespec *tp);
432 
438  co_csdo_t *sdo, const struct can_msg *msg);
439 
441 // clang-format off
442 LELY_CO_DEFINE_STATE(co_csdo_blk_up_sub_state,
443  .on_abort = &co_csdo_blk_up_sub_on_abort,
444  .on_time = &co_csdo_blk_up_sub_on_time,
445  .on_recv = &co_csdo_blk_up_sub_on_recv
446 )
447 // clang-format on
448 
451  co_csdo_t *sdo, co_unsigned32_t ac);
452 
455  co_csdo_t *sdo, const struct timespec *tp);
456 
461  co_csdo_t *sdo, const struct can_msg *msg);
462 
464 // clang-format off
465 LELY_CO_DEFINE_STATE(co_csdo_blk_up_end_state,
466  .on_abort = &co_csdo_blk_up_end_on_abort,
467  .on_time = &co_csdo_blk_up_end_on_time,
468  .on_recv = &co_csdo_blk_up_end_on_recv
469 )
470 // clang-format on
471 
472 #undef LELY_CO_DEFINE_STATE
473 
480 static co_csdo_state_t *co_csdo_abort_ind(co_csdo_t *sdo, co_unsigned32_t ac);
481 
493 static co_csdo_state_t *co_csdo_abort_res(co_csdo_t *sdo, co_unsigned32_t ac);
494 
503 static int co_csdo_dn_ind(co_csdo_t *sdo, co_unsigned16_t idx,
504  co_unsigned8_t subidx, const void *ptr, size_t n,
505  co_csdo_dn_con_t *con, void *data);
506 
515 static int co_csdo_up_ind(co_csdo_t *sdo, co_unsigned16_t idx,
516  co_unsigned8_t subidx, co_csdo_up_con_t *con, void *data);
517 
524 static void co_csdo_send_abort(co_csdo_t *sdo, co_unsigned32_t ac);
525 
527 static void co_csdo_send_dn_exp_req(co_csdo_t *sdo);
528 
530 static void co_csdo_send_dn_ini_req(co_csdo_t *sdo);
531 
540 static void co_csdo_send_dn_seg_req(co_csdo_t *sdo, uint32_t n, int last);
541 
543 static void co_csdo_send_up_ini_req(co_csdo_t *sdo);
544 
546 static void co_csdo_send_up_seg_req(co_csdo_t *sdo);
547 
549 static void co_csdo_send_blk_dn_ini_req(co_csdo_t *sdo);
550 
557 static void co_csdo_send_blk_dn_sub_req(co_csdo_t *sdo, uint8_t seqno);
558 
560 static void co_csdo_send_blk_dn_end_req(co_csdo_t *sdo);
561 
568 static void co_csdo_send_blk_up_ini_req(co_csdo_t *sdo, uint8_t pst);
569 
571 static void co_csdo_send_start_up_req(co_csdo_t *sdo);
572 
574 static void co_csdo_send_blk_up_sub_res(co_csdo_t *sdo);
575 
577 static void co_csdo_send_blk_up_end_res(co_csdo_t *sdo);
578 
586 static void co_csdo_init_ini_req(
587  co_csdo_t *sdo, struct can_msg *msg, uint8_t cs);
588 
596 static void co_csdo_init_seg_req(
597  co_csdo_t *sdo, struct can_msg *msg, uint8_t cs);
598 
599 int
600 co_dev_dn_req(co_dev_t *dev, co_unsigned16_t idx, co_unsigned8_t subidx,
601  const void *ptr, size_t n, co_csdo_dn_con_t *con, void *data)
602 {
603  assert(dev);
604 
605  int errc = get_errc();
606  struct co_sdo_req req = CO_SDO_REQ_INIT;
607 
608  co_unsigned32_t ac = 0;
609 
610  co_obj_t *obj = co_dev_find_obj(dev, idx);
611  if (__unlikely(!obj)) {
612  ac = CO_SDO_AC_NO_OBJ;
613  goto done;
614  }
615 
616  co_sub_t *sub = co_obj_find_sub(obj, subidx);
617  if (__unlikely(!sub)) {
618  ac = CO_SDO_AC_NO_SUB;
619  goto done;
620  }
621 
622  if (__unlikely(co_sdo_req_up(&req, ptr, n, &ac) == -1))
623  goto done;
624 
625  ac = co_sub_dn_ind(sub, &req);
626 
627 done:
628  if (con)
629  con(NULL, idx, subidx, ac, data);
630 
631  co_sdo_req_fini(&req);
632  set_errc(errc);
633  return 0;
634 }
635 
636 int
637 co_dev_dn_val_req(co_dev_t *dev, co_unsigned16_t idx, co_unsigned8_t subidx,
638  co_unsigned16_t type, const void *val, co_csdo_dn_con_t *con,
639  void *data)
640 {
641  assert(dev);
642 
643  int errc = get_errc();
644  struct co_sdo_req req = CO_SDO_REQ_INIT;
645 
646  co_unsigned32_t ac = 0;
647 
648  co_obj_t *obj = co_dev_find_obj(dev, idx);
649  if (__unlikely(!obj)) {
650  ac = CO_SDO_AC_NO_OBJ;
651  goto done;
652  }
653 
654  co_sub_t *sub = co_obj_find_sub(obj, subidx);
655  if (__unlikely(!sub)) {
656  ac = CO_SDO_AC_NO_SUB;
657  goto done;
658  }
659 
660  if (__unlikely(co_sdo_req_up_val(&req, type, val, &ac) == -1))
661  goto done;
662 
663  ac = co_sub_dn_ind(sub, &req);
664 
665 done:
666  if (con)
667  con(NULL, idx, subidx, ac, data);
668 
669  co_sdo_req_fini(&req);
670  set_errc(errc);
671  return 0;
672 }
673 
674 int
675 co_dev_up_req(const co_dev_t *dev, co_unsigned16_t idx, co_unsigned8_t subidx,
676  co_csdo_up_con_t *con, void *data)
677 {
678  assert(dev);
679 
680  int errc = get_errc();
681  struct membuf buf = MEMBUF_INIT;
682  co_unsigned32_t ac = 0;
683 
684  const co_obj_t *obj = co_dev_find_obj(dev, idx);
685  if (__unlikely(!obj)) {
686  ac = CO_SDO_AC_NO_OBJ;
687  goto done;
688  }
689 
690  const co_sub_t *sub = co_obj_find_sub(obj, subidx);
691  if (__unlikely(!sub)) {
692  ac = CO_SDO_AC_NO_SUB;
693  goto done;
694  }
695 
696  // If the object is an array, check whether the element exists.
697  if (co_obj_get_code(obj) == CO_OBJECT_ARRAY
698  && __unlikely(subidx > co_obj_get_val_u8(obj, 0))) {
699  ac = CO_SDO_AC_NO_DATA;
700  goto done;
701  }
702 
703  struct co_sdo_req req = CO_SDO_REQ_INIT;
704 
705  ac = co_sub_up_ind(sub, &req);
706  if (!ac && req.size && !membuf_reserve(&buf, req.size))
707  ac = CO_SDO_AC_NO_MEM;
708 
709  do {
710  membuf_write(&buf, req.buf, req.nbyte);
711  if (!co_sdo_req_last(&req))
712  ac = co_sub_up_ind(sub, &req);
713  } while (!ac && membuf_size(&buf) < req.size);
714 
715  co_sdo_req_fini(&req);
716 
717 done:
718  if (con)
719  con(NULL, idx, subidx, ac, ac ? NULL : buf.begin,
720  ac ? 0 : membuf_size(&buf), data);
721 
722  membuf_fini(&buf);
723  set_errc(errc);
724  return 0;
725 }
726 
727 void *
728 __co_csdo_alloc(void)
729 {
730  void *ptr = malloc(sizeof(struct __co_csdo));
731  if (__unlikely(!ptr))
732  set_errc(errno2c(errno));
733  return ptr;
734 }
735 
736 void
737 __co_csdo_free(void *ptr)
738 {
739  free(ptr);
740 }
741 
742 struct __co_csdo *
743 __co_csdo_init(struct __co_csdo *sdo, can_net_t *net, co_dev_t *dev,
744  co_unsigned8_t num)
745 {
746  assert(sdo);
747  assert(net);
748 
749  int errc = 0;
750 
751  if (__unlikely(!num || num > (dev ? 128 : CO_NUM_NODES))) {
752  errc = errnum2c(ERRNUM_INVAL);
753  goto error_param;
754  }
755 
756  // Find the SDO client parameter in the object dictionary.
757  co_obj_t *obj_1280 =
758  dev ? co_dev_find_obj(dev, 0x1280 + num - 1) : NULL;
759  if (__unlikely(dev && !obj_1280)) {
760  errc = errnum2c(ERRNUM_INVAL);
761  goto error_param;
762  }
763 
764  sdo->net = net;
765  sdo->dev = dev;
766  sdo->num = num;
767 
768  // Initialize the SDO parameter record with the default values.
769  sdo->par.n = 3;
770  sdo->par.id = num;
771  sdo->par.cobid_req = 0x600 + sdo->par.id;
772  sdo->par.cobid_res = 0x580 + sdo->par.id;
773 
774  if (obj_1280) {
775  // Copy the SDO parameter record.
776  size_t size = co_obj_sizeof_val(obj_1280);
777  memcpy(&sdo->par, co_obj_addressof_val(obj_1280),
778  MIN(size, sizeof(sdo->par)));
779  }
780 
781  sdo->recv = can_recv_create();
782  if (__unlikely(!sdo->recv)) {
783  errc = get_errc();
784  goto error_create_recv;
785  }
786  can_recv_set_func(sdo->recv, &co_csdo_recv, sdo);
787 
788  sdo->timeout = 0;
789 
790  sdo->timer = can_timer_create();
791  if (__unlikely(!sdo->timer)) {
792  errc = get_errc();
793  goto error_create_timer;
794  }
796 
797  sdo->state = co_csdo_wait_state;
798 
799  sdo->ac = 0;
800  sdo->idx = 0;
801  sdo->subidx = 0;
802  sdo->size = 0;
803 
804  sdo->toggle = 0;
805  sdo->blksize = 0;
806  sdo->ackseq = 0;
807  sdo->crc = 0;
808 
809  membuf_init(&sdo->buf);
810 
811  sdo->dn_con = NULL;
812  sdo->dn_con_data = NULL;
813 
814  sdo->dn_ind = NULL;
815  sdo->dn_ind_data = NULL;
816 
817  sdo->up_con = NULL;
818  sdo->up_con_data = NULL;
819 
820  sdo->up_ind = NULL;
821  sdo->up_ind_data = NULL;
822 
823  // Set the download indication function for the SDO parameter record.
824  if (obj_1280)
825  co_obj_set_dn_ind(obj_1280, &co_1280_dn_ind, sdo);
826 
827  if (__unlikely(co_csdo_update(sdo) == -1)) {
828  errc = get_errc();
829  goto error_update;
830  }
831 
832  return sdo;
833 
834 error_update:
835  if (obj_1280)
836  co_obj_set_dn_ind(obj_1280, NULL, NULL);
837  can_timer_destroy(sdo->timer);
838 error_create_timer:
839  can_recv_destroy(sdo->recv);
840 error_create_recv:
841 error_param:
842  set_errc(errc);
843  return NULL;
844 }
845 
846 void
847 __co_csdo_fini(struct __co_csdo *sdo)
848 {
849  assert(sdo);
850  assert(sdo->num >= 1 && sdo->num <= 128);
851 
852  // Remove the download indication functions for the SDO parameter
853  // record.
854  co_obj_t *obj_1280 = sdo->dev
855  ? co_dev_find_obj(sdo->dev, 0x1280 + sdo->num - 1)
856  : NULL;
857  if (obj_1280)
858  co_obj_set_dn_ind(obj_1280, NULL, NULL);
859 
860  membuf_fini(&sdo->buf);
861 
862  can_timer_destroy(sdo->timer);
863  can_recv_destroy(sdo->recv);
864 }
865 
866 co_csdo_t *
868 {
869  trace("creating Client-SDO %d", num);
870 
871  int errc = 0;
872 
873  co_csdo_t *sdo = __co_csdo_alloc();
874  if (__unlikely(!sdo)) {
875  errc = get_errc();
876  goto error_alloc_sdo;
877  }
878 
879  if (__unlikely(!__co_csdo_init(sdo, net, dev, num))) {
880  errc = get_errc();
881  goto error_init_sdo;
882  }
883 
884  return sdo;
885 
886 error_init_sdo:
887  __co_csdo_free(sdo);
888 error_alloc_sdo:
889  set_errc(errc);
890  return NULL;
891 }
892 
893 void
895 {
896  if (csdo) {
897  trace("destroying Client-SDO %d", csdo->num);
898  __co_csdo_fini(csdo);
899  __co_csdo_free(csdo);
900  }
901 }
902 
903 can_net_t *
905 {
906  assert(sdo);
907 
908  return sdo->net;
909 }
910 
911 co_dev_t *
913 {
914  assert(sdo);
915 
916  return sdo->dev;
917 }
918 
919 co_unsigned8_t
921 {
922  assert(sdo);
923 
924  return sdo->num;
925 }
926 
927 const struct co_sdo_par *
929 {
930  assert(sdo);
931 
932  return &sdo->par;
933 }
934 
935 int
937 {
938  assert(sdo);
939 
940  return sdo->timeout;
941 }
942 
943 void
944 co_csdo_set_timeout(co_csdo_t *sdo, int timeout)
945 {
946  assert(sdo);
947 
948  if (sdo->timeout && timeout <= 0)
949  can_timer_stop(sdo->timer);
950 
951  sdo->timeout = MAX(0, timeout);
952 }
953 
954 void
955 co_csdo_get_dn_ind(const co_csdo_t *sdo, co_csdo_ind_t **pind, void **pdata)
956 {
957  assert(sdo);
958 
959  if (pind)
960  *pind = sdo->dn_ind;
961  if (pdata)
962  *pdata = sdo->dn_ind_data;
963 }
964 
965 void
967 {
968  assert(sdo);
969 
970  sdo->dn_ind = ind;
971  sdo->dn_ind_data = data;
972 }
973 
974 void
975 co_csdo_get_up_ind(const co_csdo_t *sdo, co_csdo_ind_t **pind, void **pdata)
976 {
977  assert(sdo);
978 
979  if (pind)
980  *pind = sdo->up_ind;
981  if (pdata)
982  *pdata = sdo->up_ind_data;
983 }
984 
985 void
987 {
988  assert(sdo);
989 
990  sdo->up_ind = ind;
991  sdo->up_ind_data = data;
992 }
993 
994 int
996 {
997  assert(sdo);
998 
999  return sdo->state == co_csdo_wait_state;
1000 }
1001 
1002 void
1003 co_csdo_abort_req(co_csdo_t *sdo, co_unsigned32_t ac)
1004 {
1005  assert(sdo);
1006 
1007  co_csdo_emit_abort(sdo, ac);
1008 }
1009 
1010 int
1011 co_csdo_dn_req(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx,
1012  const void *ptr, size_t n, co_csdo_dn_con_t *con, void *data)
1013 {
1014  assert(sdo);
1015 
1016  // clang-format off
1017  if (__unlikely(co_csdo_dn_ind(sdo, idx, subidx, ptr, n, con, data)
1018  == -1))
1019  // clang-format on
1020  return -1;
1021 
1022  trace("CSDO: %04X:%02X: initiate download", idx, subidx);
1023 
1024  if (sdo->timeout)
1025  can_timer_timeout(sdo->timer, sdo->net, sdo->timeout);
1026  if (sdo->size && sdo->size <= 4)
1028  else
1031 
1032  return 0;
1033 }
1034 
1035 int
1036 co_csdo_dn_val_req(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx,
1037  co_unsigned16_t type, const void *val, co_csdo_dn_con_t *con,
1038  void *data)
1039 {
1040  assert(sdo);
1041 
1042  // Obtain the size of the serialized value (which may be 0 for arrays).
1043  size_t n = co_val_write(type, val, NULL, NULL);
1044  if (__unlikely(!n && co_val_sizeof(type, val)))
1045  return -1;
1046 
1047  if (co_type_is_array(type) || n > 8) {
1048  int res = 0;
1049  int errc = get_errc();
1050 
1051  uint8_t *buf = n ? malloc(n) : NULL;
1052  if (__unlikely(n && !buf)) {
1053  errc = errno2c(errno);
1054  goto error_malloc_buf;
1055  }
1056 
1057  if (__unlikely(co_val_write(type, val, buf, buf + n) != n)) {
1058  errc = get_errc();
1059  goto error_write_val;
1060  }
1061 
1062  res = co_csdo_dn_req(sdo, idx, subidx, buf, n, con, data);
1063 
1064  error_write_val:
1065  free(buf);
1066  error_malloc_buf:
1067  set_errc(errc);
1068  return res;
1069  } else {
1070  // Fast path for values small enough to be allocated on the
1071  // heap.
1072  uint8_t buf[8];
1073  if (__unlikely(co_val_write(type, val, buf, buf + n) != n))
1074  return -1;
1075  return co_csdo_dn_req(sdo, idx, subidx, buf, n, con, data);
1076  }
1077 }
1078 
1079 int
1080 co_csdo_up_req(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx,
1081  co_csdo_up_con_t *con, void *data)
1082 {
1083  assert(sdo);
1084 
1085  if (__unlikely(co_csdo_up_ind(sdo, idx, subidx, con, data) == -1))
1086  return -1;
1087 
1088  trace("CSDO: %04X:%02X: initiate upload", idx, subidx);
1089 
1090  if (sdo->timeout)
1091  can_timer_timeout(sdo->timer, sdo->net, sdo->timeout);
1094 
1095  return 0;
1096 }
1097 
1098 int
1099 co_csdo_blk_dn_req(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx,
1100  const void *ptr, size_t n, co_csdo_dn_con_t *con, void *data)
1101 {
1102  assert(sdo);
1103 
1104  // clang-format off
1105  if (__unlikely(co_csdo_dn_ind(sdo, idx, subidx, ptr, n, con, data)
1106  == -1))
1107  // clang-format on
1108  return -1;
1109 
1110  trace("CSDO: %04X:%02X: initiate block download", idx, subidx);
1111 
1112  if (sdo->timeout)
1113  can_timer_timeout(sdo->timer, sdo->net, sdo->timeout);
1116 
1117  return 0;
1118 }
1119 
1120 int
1121 co_csdo_blk_up_req(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx,
1122  uint8_t pst, co_csdo_up_con_t *con, void *data)
1123 {
1124  assert(sdo);
1125 
1126  if (__unlikely(co_csdo_up_ind(sdo, idx, subidx, con, data) == -1))
1127  return -1;
1128 
1129  trace("CSDO: %04X:%02X: initiate block upload", idx, subidx);
1130 
1131  // Use the maximum block size by default.
1132  sdo->blksize = CO_SDO_MAX_SEQNO;
1133 
1134  if (sdo->timeout)
1135  can_timer_timeout(sdo->timer, sdo->net, sdo->timeout);
1136  co_csdo_send_blk_up_ini_req(sdo, pst);
1138 
1139  return 0;
1140 }
1141 
1142 static int
1144 {
1145  assert(sdo);
1146 
1147  // Abort any ongoing transfer.
1149 
1150  int valid_req = !(sdo->par.cobid_req & CO_SDO_COBID_VALID);
1151  int valid_res = !(sdo->par.cobid_res & CO_SDO_COBID_VALID);
1152  if (valid_req && valid_res) {
1153  uint32_t id = sdo->par.cobid_res;
1154  uint8_t flags = 0;
1155  if (id & CO_SDO_COBID_FRAME) {
1156  id &= CAN_MASK_EID;
1157  flags |= CAN_FLAG_IDE;
1158  } else {
1159  id &= CAN_MASK_BID;
1160  }
1161  can_recv_start(sdo->recv, sdo->net, id, flags);
1162  } else {
1163  can_recv_stop(sdo->recv);
1164  }
1165 
1166  return 0;
1167 }
1168 
1169 static co_unsigned32_t
1170 co_1280_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
1171 {
1172  assert(sub);
1173  assert(req);
1174  co_csdo_t *sdo = data;
1175  assert(sdo);
1176  assert(co_obj_get_idx(co_sub_get_obj(sub)) == 0x1280 + sdo->num - 1);
1177 
1178  co_unsigned32_t ac = 0;
1179 
1180  co_unsigned16_t type = co_sub_get_type(sub);
1181  union co_val val;
1182  if (__unlikely(co_sdo_req_dn_val(req, type, &val, &ac) == -1))
1183  return ac;
1184 
1185  switch (co_sub_get_subidx(sub)) {
1186  case 0: ac = CO_SDO_AC_NO_WRITE; goto error;
1187  case 1: {
1188  assert(type == CO_DEFTYPE_UNSIGNED32);
1189  co_unsigned32_t cobid = val.u32;
1190  co_unsigned32_t cobid_old = co_sub_get_val_u32(sub);
1191  if (cobid == cobid_old)
1192  goto error;
1193 
1194  // The CAN-ID cannot be changed when the SDO is and remains
1195  // valid.
1196  int valid = !(cobid & CO_SDO_COBID_VALID);
1197  int valid_old = !(cobid_old & CO_SDO_COBID_VALID);
1198  uint32_t canid = cobid & CAN_MASK_EID;
1199  uint32_t canid_old = cobid_old & CAN_MASK_EID;
1200  if (__unlikely(valid && valid_old && canid != canid_old)) {
1201  ac = CO_SDO_AC_PARAM_VAL;
1202  goto error;
1203  }
1204 
1205  // A 29-bit CAN-ID is only valid if the frame bit is set.
1206  // clang-format off
1207  if (__unlikely(!(cobid & CO_SDO_COBID_FRAME)
1208  && (cobid & (CAN_MASK_EID ^ CAN_MASK_BID)))) {
1209  // clang-format on
1210  ac = CO_SDO_AC_PARAM_VAL;
1211  goto error;
1212  }
1213 
1214  sdo->par.cobid_req = cobid;
1215  break;
1216  }
1217  case 2: {
1218  assert(type == CO_DEFTYPE_UNSIGNED32);
1219  co_unsigned32_t cobid = val.u32;
1220  co_unsigned32_t cobid_old = co_sub_get_val_u32(sub);
1221  if (cobid == cobid_old)
1222  goto error;
1223 
1224  // The CAN-ID cannot be changed when the SDO is and remains
1225  // valid.
1226  int valid = !(cobid & CO_SDO_COBID_VALID);
1227  int valid_old = !(cobid_old & CO_SDO_COBID_VALID);
1228  uint32_t canid = cobid & CAN_MASK_EID;
1229  uint32_t canid_old = cobid_old & CAN_MASK_EID;
1230  if (__unlikely(valid && valid_old && canid != canid_old)) {
1231  ac = CO_SDO_AC_PARAM_VAL;
1232  goto error;
1233  }
1234 
1235  // A 29-bit CAN-ID is only valid if the frame bit is set.
1236  // clang-format off
1237  if (__unlikely(!(cobid & CO_SDO_COBID_FRAME)
1238  && (cobid & (CAN_MASK_EID ^ CAN_MASK_BID)))) {
1239  // clang-format on
1240  ac = CO_SDO_AC_PARAM_VAL;
1241  goto error;
1242  }
1243 
1244  sdo->par.cobid_res = cobid;
1245  break;
1246  }
1247  case 3: {
1248  assert(type == CO_DEFTYPE_UNSIGNED8);
1249  co_unsigned8_t id = val.u8;
1250  co_unsigned8_t id_old = co_sub_get_val_u8(sub);
1251  if (id == id_old)
1252  goto error;
1253 
1254  sdo->par.id = id;
1255  break;
1256  }
1257  default: ac = CO_SDO_AC_NO_SUB; goto error;
1258  }
1259 
1260  co_sub_dn(sub, &val);
1261  co_val_fini(type, &val);
1262 
1263  co_csdo_update(sdo);
1264  return 0;
1265 
1266 error:
1267  co_val_fini(type, &val);
1268  return ac;
1269 }
1270 
1271 static int
1272 co_csdo_recv(const struct can_msg *msg, void *data)
1273 {
1274  assert(msg);
1275  co_csdo_t *sdo = data;
1276  assert(sdo);
1277 
1278  // Ignore remote frames.
1279  if (__unlikely(msg->flags & CAN_FLAG_RTR))
1280  return 0;
1281 
1282 #ifndef LELY_NO_CANFD
1283  // Ignore CAN FD format frames.
1284  if (__unlikely(msg->flags & CAN_FLAG_EDL))
1285  return 0;
1286 #endif
1287 
1288  co_csdo_emit_recv(sdo, msg);
1289 
1290  return 0;
1291 }
1292 
1293 static int
1294 co_csdo_timer(const struct timespec *tp, void *data)
1295 {
1296  assert(tp);
1297  co_csdo_t *sdo = data;
1298  assert(sdo);
1299 
1300  co_csdo_emit_time(sdo, tp);
1301 
1302  return 0;
1303 }
1304 
1305 static inline void
1307 {
1308  assert(sdo);
1309  assert(sdo->state);
1310 
1311  while (next) {
1312  co_csdo_state_t *prev = sdo->state;
1313  sdo->state = next;
1314 
1315  if (prev->on_leave)
1316  prev->on_leave(sdo);
1317 
1318  next = next->on_enter ? next->on_enter(sdo) : NULL;
1319  }
1320 }
1321 
1322 static inline void
1323 co_csdo_emit_abort(co_csdo_t *sdo, co_unsigned32_t ac)
1324 {
1325  assert(sdo);
1326  assert(sdo->state);
1327  assert(sdo->state->on_abort);
1328 
1329  co_csdo_enter(sdo, sdo->state->on_abort(sdo, ac));
1330 }
1331 
1332 static inline void
1333 co_csdo_emit_time(co_csdo_t *sdo, const struct timespec *tp)
1334 {
1335  assert(sdo);
1336  assert(sdo->state);
1337  assert(sdo->state->on_time);
1338 
1339  co_csdo_enter(sdo, sdo->state->on_time(sdo, tp));
1340 }
1341 
1342 static inline void
1343 co_csdo_emit_recv(co_csdo_t *sdo, const struct can_msg *msg)
1344 {
1345  assert(sdo);
1346  assert(sdo->state);
1347  assert(sdo->state->on_recv);
1348 
1349  co_csdo_enter(sdo, sdo->state->on_recv(sdo, msg));
1350 }
1351 
1352 static co_csdo_state_t *
1353 co_csdo_wait_on_abort(co_csdo_t *sdo, co_unsigned32_t ac)
1354 {
1355  (void)sdo;
1356  (void)ac;
1357 
1358  return NULL;
1359 }
1360 
1361 static co_csdo_state_t *
1362 co_csdo_wait_on_recv(co_csdo_t *sdo, const struct can_msg *msg)
1363 {
1364  assert(sdo);
1365  assert(msg);
1366 
1367  if (__unlikely(msg->len < 1))
1368  return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1369  uint8_t cs = msg->data[0];
1370 
1371  co_unsigned32_t ac;
1372  switch (cs & CO_SDO_CS_MASK) {
1373  case CO_SDO_CS_ABORT:
1374  ac = msg->len < 8 ? 0 : ldle_u32(msg->data + 4);
1375  return co_csdo_abort_ind(sdo, ac ? ac : CO_SDO_AC_ERROR);
1376  default: return NULL;
1377  }
1378 }
1379 
1380 static co_csdo_state_t *
1382 {
1383  (void)sdo;
1384 
1385  can_timer_stop(sdo->timer);
1386 
1387  return co_csdo_wait_state;
1388 }
1389 
1390 static void
1392 {
1393  assert(sdo);
1394 
1395  co_csdo_dn_con_t *dn_con = sdo->dn_con;
1396  sdo->dn_con = NULL;
1397  void *dn_con_data = sdo->dn_con_data;
1398  sdo->dn_con_data = NULL;
1399 
1400  co_csdo_up_con_t *up_con = sdo->up_con;
1401  sdo->up_con = NULL;
1402  void *up_con_data = sdo->up_con_data;
1403  sdo->up_con_data = NULL;
1404 
1405  if (dn_con) {
1406  dn_con(sdo, sdo->idx, sdo->subidx, sdo->ac, dn_con_data);
1407  } else if (up_con) {
1408  up_con(sdo, sdo->idx, sdo->subidx, sdo->ac,
1409  sdo->ac ? NULL : sdo->buf.begin,
1410  sdo->ac ? 0 : membuf_size(&sdo->buf),
1411  up_con_data);
1412  }
1413 }
1414 
1415 static co_csdo_state_t *
1416 co_csdo_dn_ini_on_abort(co_csdo_t *sdo, co_unsigned32_t ac)
1417 {
1418  return co_csdo_abort_res(sdo, ac);
1419 }
1420 
1421 static co_csdo_state_t *
1422 co_csdo_dn_ini_on_time(co_csdo_t *sdo, const struct timespec *tp)
1423 {
1424  (void)tp;
1425 
1426  return co_csdo_abort_res(sdo, CO_SDO_AC_TIMEOUT);
1427 }
1428 
1429 static co_csdo_state_t *
1430 co_csdo_dn_ini_on_recv(co_csdo_t *sdo, const struct can_msg *msg)
1431 {
1432  assert(sdo);
1433  assert(msg);
1434 
1435  if (__unlikely(msg->len < 1))
1436  return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1437  uint8_t cs = msg->data[0];
1438 
1439  // Check the server command specifier.
1440  co_unsigned32_t ac;
1441  switch (cs & CO_SDO_CS_MASK) {
1442  case CO_SDO_SCS_DN_INI_RES: break;
1443  case CO_SDO_CS_ABORT:
1444  ac = msg->len < 8 ? 0 : ldle_u32(msg->data + 4);
1445  return co_csdo_abort_ind(sdo, ac ? ac : CO_SDO_AC_ERROR);
1446  default: return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1447  }
1448 
1449  // Check the object index and sub-index.
1450  if (__unlikely(msg->len < 4))
1451  return co_csdo_abort_res(sdo, CO_SDO_AC_ERROR);
1452  co_unsigned16_t idx = ldle_u16(msg->data + 1);
1453  co_unsigned8_t subidx = msg->data[3];
1454  if (__unlikely(idx != sdo->idx || subidx != sdo->subidx))
1455  return co_csdo_abort_res(sdo, CO_SDO_AC_ERROR);
1456 
1457  return co_csdo_dn_seg_state;
1458 }
1459 
1460 static co_csdo_state_t *
1462 {
1463  assert(sdo);
1464 
1465  size_t n = sdo->size - membuf_size(&sdo->buf);
1466  // 0-byte values cannot be sent using expedited transfer, so we need to
1467  // send one empty segment. We use the toggle bit to check if it was
1468  // sent.
1469  if (n || (!sdo->size && !sdo->toggle)) {
1470  if (sdo->timeout)
1471  can_timer_timeout(sdo->timer, sdo->net, sdo->timeout);
1472  co_csdo_send_dn_seg_req(sdo, MIN(n, 7), n <= 7);
1473  return NULL;
1474  } else {
1475  return co_csdo_abort_ind(sdo, 0);
1476  }
1477 }
1478 
1479 static co_csdo_state_t *
1480 co_csdo_dn_seg_on_abort(co_csdo_t *sdo, co_unsigned32_t ac)
1481 {
1482  return co_csdo_abort_res(sdo, ac);
1483 }
1484 
1485 static co_csdo_state_t *
1486 co_csdo_dn_seg_on_time(co_csdo_t *sdo, const struct timespec *tp)
1487 {
1488  (void)tp;
1489 
1490  return co_csdo_abort_res(sdo, CO_SDO_AC_TIMEOUT);
1491 }
1492 
1493 static co_csdo_state_t *
1494 co_csdo_dn_seg_on_recv(co_csdo_t *sdo, const struct can_msg *msg)
1495 {
1496  assert(sdo);
1497  assert(msg);
1498 
1499  if (__unlikely(msg->len < 1))
1500  return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1501  uint8_t cs = msg->data[0];
1502 
1503  // Check the server command specifier.
1504  co_unsigned32_t ac;
1505  switch (cs & CO_SDO_CS_MASK) {
1506  case CO_SDO_SCS_DN_SEG_RES: break;
1507  case CO_SDO_CS_ABORT:
1508  ac = msg->len < 8 ? 0 : ldle_u32(msg->data + 4);
1509  return co_csdo_abort_ind(sdo, ac ? ac : CO_SDO_AC_ERROR);
1510  default: return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1511  }
1512 
1513  // Check the value of the toggle bit.
1514  if (__unlikely((cs & CO_SDO_SEG_TOGGLE) == sdo->toggle))
1515  return co_csdo_abort_res(sdo, CO_SDO_AC_TOGGLE);
1516 
1517  return co_csdo_dn_seg_state;
1518 }
1519 
1520 static co_csdo_state_t *
1521 co_csdo_up_ini_on_abort(co_csdo_t *sdo, co_unsigned32_t ac)
1522 {
1523  return co_csdo_abort_res(sdo, ac);
1524 }
1525 
1526 static co_csdo_state_t *
1527 co_csdo_up_ini_on_time(co_csdo_t *sdo, const struct timespec *tp)
1528 {
1529  (void)tp;
1530 
1531  return co_csdo_abort_res(sdo, CO_SDO_AC_TIMEOUT);
1532 }
1533 
1534 static co_csdo_state_t *
1535 co_csdo_up_ini_on_recv(co_csdo_t *sdo, const struct can_msg *msg)
1536 {
1537  assert(sdo);
1538  assert(msg);
1539 
1540  if (__unlikely(msg->len < 1))
1541  return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1542  uint8_t cs = msg->data[0];
1543 
1544  // Check the server command specifier.
1545  co_unsigned32_t ac;
1546  switch (cs & CO_SDO_CS_MASK) {
1547  case CO_SDO_SCS_UP_INI_RES: break;
1548  case CO_SDO_CS_ABORT:
1549  ac = msg->len < 8 ? 0 : ldle_u32(msg->data + 4);
1550  return co_csdo_abort_ind(sdo, ac ? ac : CO_SDO_AC_ERROR);
1551  default: return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1552  }
1553 
1554  // Check the object index and sub-index.
1555  if (__unlikely(msg->len < 4))
1556  return co_csdo_abort_res(sdo, CO_SDO_AC_ERROR);
1557  co_unsigned16_t idx = ldle_u16(msg->data + 1);
1558  co_unsigned8_t subidx = msg->data[3];
1559  if (__unlikely(idx != sdo->idx || subidx != sdo->subidx))
1560  return co_csdo_abort_res(sdo, CO_SDO_AC_ERROR);
1561 
1562  // Obtain the size from the command specifier.
1563  int exp = !!(cs & CO_SDO_INI_SIZE_EXP);
1564  sdo->size = 0;
1565  if (exp) {
1566  if (cs & CO_SDO_INI_SIZE_IND)
1567  sdo->size = CO_SDO_INI_SIZE_EXP_GET(cs);
1568  else
1569  sdo->size = msg->len - 4;
1570  } else if (cs & CO_SDO_INI_SIZE_IND) {
1571  if (__unlikely(msg->len < 8))
1572  return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1573  sdo->size = ldle_u32(msg->data + 4);
1574  }
1575 
1576  // Allocate the buffer.
1577  if (__unlikely(sdo->size && !membuf_reserve(&sdo->buf, sdo->size)))
1578  return co_csdo_abort_res(sdo, CO_SDO_AC_NO_MEM);
1579 
1580  if (exp) {
1581  // Perform an expedited transfer.
1582  memcpy(sdo->buf.cur, msg->data + 4, sdo->size);
1583  sdo->buf.cur += sdo->size;
1584 
1585  return co_csdo_abort_ind(sdo, 0);
1586  } else {
1587  if (sdo->size && sdo->up_ind)
1588  sdo->up_ind(sdo, sdo->idx, sdo->subidx, sdo->size, 0,
1589  sdo->up_ind_data);
1590  if (sdo->timeout)
1591  can_timer_timeout(sdo->timer, sdo->net, sdo->timeout);
1593  return co_csdo_up_seg_state;
1594  }
1595 }
1596 
1597 static co_csdo_state_t *
1598 co_csdo_up_seg_on_abort(co_csdo_t *sdo, co_unsigned32_t ac)
1599 {
1600  return co_csdo_abort_res(sdo, ac);
1601 }
1602 
1603 static co_csdo_state_t *
1604 co_csdo_up_seg_on_time(co_csdo_t *sdo, const struct timespec *tp)
1605 {
1606  (void)tp;
1607 
1608  return co_csdo_abort_res(sdo, CO_SDO_AC_TIMEOUT);
1609 }
1610 
1611 static co_csdo_state_t *
1612 co_csdo_up_seg_on_recv(co_csdo_t *sdo, const struct can_msg *msg)
1613 {
1614  assert(sdo);
1615  assert(msg);
1616 
1617  if (__unlikely(msg->len < 1))
1618  return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1619  uint8_t cs = msg->data[0];
1620 
1621  // Check the server command specifier.
1622  co_unsigned32_t ac;
1623  switch (cs & CO_SDO_CS_MASK) {
1624  case CO_SDO_SCS_UP_SEG_RES: break;
1625  case CO_SDO_CS_ABORT:
1626  ac = msg->len < 8 ? 0 : ldle_u32(msg->data + 4);
1627  return co_csdo_abort_ind(sdo, ac ? ac : CO_SDO_AC_ERROR);
1628  default: return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1629  }
1630 
1631  // Check the value of the toggle bit.
1632  if (__unlikely((cs & CO_SDO_SEG_TOGGLE) == sdo->toggle))
1633  return co_csdo_up_seg_state;
1634 
1635  // Obtain the size of the segment.
1636  size_t n = CO_SDO_SEG_SIZE_GET(cs);
1637  if (__unlikely(msg->len < 1 + n))
1638  return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1639  int last = !!(cs & CO_SDO_SEG_LAST);
1640 
1641  // Reserve room in the buffer, if necessary.
1642  if (__unlikely(n && !membuf_reserve(&sdo->buf, n)))
1643  return co_csdo_abort_res(sdo, CO_SDO_AC_NO_MEM);
1644 
1645  // Copy the data to the buffer.
1646  memcpy(sdo->buf.cur, msg->data + 1, n);
1647  sdo->buf.cur += n;
1648 
1649  if ((last || !(membuf_size(&sdo->buf) % (CO_SDO_MAX_SEQNO * 7)))
1650  && sdo->size && sdo->up_ind)
1651  sdo->up_ind(sdo, sdo->idx, sdo->subidx, sdo->size,
1652  membuf_size(&sdo->buf), sdo->up_ind_data);
1653  if (last) {
1654  // clang-format off
1655  if (__unlikely(sdo->size
1656  && membuf_size(&sdo->buf) != sdo->size))
1657  // clang-format on
1659  return co_csdo_abort_ind(sdo, 0);
1660  } else {
1661  if (sdo->timeout)
1662  can_timer_timeout(sdo->timer, sdo->net, sdo->timeout);
1664  return co_csdo_up_seg_state;
1665  }
1666 }
1667 
1668 static co_csdo_state_t *
1669 co_csdo_blk_dn_ini_on_abort(co_csdo_t *sdo, co_unsigned32_t ac)
1670 {
1671  return co_csdo_abort_res(sdo, ac);
1672 }
1673 
1674 static co_csdo_state_t *
1675 co_csdo_blk_dn_ini_on_time(co_csdo_t *sdo, const struct timespec *tp)
1676 {
1677  (void)tp;
1678 
1679  return co_csdo_abort_res(sdo, CO_SDO_AC_TIMEOUT);
1680 }
1681 
1682 static co_csdo_state_t *
1684 {
1685  assert(sdo);
1686  assert(msg);
1687 
1688  if (__unlikely(msg->len < 1))
1689  return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1690  uint8_t cs = msg->data[0];
1691 
1692  // Check the server command specifier.
1693  co_unsigned32_t ac;
1694  switch (cs & CO_SDO_CS_MASK) {
1695  case CO_SDO_SCS_BLK_DN_RES: break;
1696  case CO_SDO_CS_ABORT:
1697  ac = msg->len < 8 ? 0 : ldle_u32(msg->data + 4);
1698  return co_csdo_abort_ind(sdo, ac ? ac : CO_SDO_AC_ERROR);
1699  default: return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1700  }
1701 
1702  // Check the server subcommand.
1704  return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1705 
1706  // Check if the server supports generating a CRC.
1707  sdo->crc = !!(cs & CO_SDO_BLK_CRC);
1708 
1709  // Check the object index and sub-index.
1710  if (__unlikely(msg->len < 4))
1711  return co_csdo_abort_res(sdo, CO_SDO_AC_ERROR);
1712  co_unsigned16_t idx = ldle_u16(msg->data + 1);
1713  co_unsigned8_t subidx = msg->data[3];
1714  if (__unlikely(idx != sdo->idx || subidx != sdo->subidx))
1715  return co_csdo_abort_res(sdo, CO_SDO_AC_ERROR);
1716 
1717  // Load the number of segments per block.
1718  if (__unlikely(msg->len < 5))
1720  sdo->blksize = msg->data[4];
1721  if (__unlikely(!sdo->blksize || sdo->blksize > CO_SDO_MAX_SEQNO))
1723 
1724  return co_csdo_blk_dn_sub_state;
1725 }
1726 
1727 static co_csdo_state_t *
1729 {
1730  assert(sdo);
1731 
1732  size_t n = sdo->size - membuf_size(&sdo->buf);
1733  sdo->blksize = (uint8_t)MIN((n + 6) / 7, sdo->blksize);
1734 
1735  if (sdo->size && sdo->dn_ind)
1736  sdo->dn_ind(sdo, sdo->idx, sdo->subidx, sdo->size,
1737  membuf_size(&sdo->buf), sdo->dn_ind_data);
1738  if (sdo->timeout)
1739  can_timer_timeout(sdo->timer, sdo->net, sdo->timeout);
1740  if (n) {
1741  // Send all segments in the current block.
1742  for (uint8_t seqno = 1; seqno <= sdo->blksize; seqno++)
1743  co_csdo_send_blk_dn_sub_req(sdo, seqno);
1744  return NULL;
1745  } else {
1747  return co_csdo_blk_dn_end_state;
1748  }
1749 }
1750 
1751 static co_csdo_state_t *
1752 co_csdo_blk_dn_sub_on_abort(co_csdo_t *sdo, co_unsigned32_t ac)
1753 {
1754  return co_csdo_abort_res(sdo, ac);
1755 }
1756 
1757 static co_csdo_state_t *
1758 co_csdo_blk_dn_sub_on_time(co_csdo_t *sdo, const struct timespec *tp)
1759 {
1760  (void)tp;
1761 
1762  return co_csdo_abort_res(sdo, CO_SDO_AC_TIMEOUT);
1763 }
1764 
1765 static co_csdo_state_t *
1767 {
1768  assert(sdo);
1769  assert(msg);
1770 
1771  if (__unlikely(msg->len < 1))
1772  return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1773  uint8_t cs = msg->data[0];
1774 
1775  // Check the server command specifier.
1776  co_unsigned32_t ac;
1777  switch (cs & CO_SDO_CS_MASK) {
1778  case CO_SDO_SCS_BLK_DN_RES: break;
1779  case CO_SDO_CS_ABORT:
1780  ac = msg->len < 8 ? 0 : ldle_u32(msg->data + 4);
1781  return co_csdo_abort_ind(sdo, ac ? ac : CO_SDO_AC_ERROR);
1782  default: return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1783  }
1784 
1785  // Check the server subcommand.
1787  return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1788 
1789  if (__unlikely(msg->len < 3))
1790  return co_csdo_abort_res(sdo, CO_SDO_AC_BLK_SEQ);
1791  uint8_t ackseq = msg->data[1];
1792  if (ackseq < sdo->blksize) {
1793  // If the sequence number of the last segment that was
1794  // successfully received is smaller than the number of segments
1795  // in the block, resend the missing segments.
1796  size_t n = (membuf_size(&sdo->buf) + 6) / 7;
1797  assert(n >= sdo->blksize);
1798  n -= sdo->blksize - ackseq;
1799  sdo->buf.cur = sdo->buf.begin + n * 7;
1800  }
1801 
1802  // Read the number of segments in the next block.
1803  if (__unlikely(msg->len < 3))
1805  sdo->blksize = msg->data[2];
1806  if (__unlikely(!sdo->blksize || sdo->blksize > CO_SDO_MAX_SEQNO))
1808 
1809  return co_csdo_blk_dn_sub_state;
1810 }
1811 
1812 static co_csdo_state_t *
1813 co_csdo_blk_dn_end_on_abort(co_csdo_t *sdo, co_unsigned32_t ac)
1814 {
1815  return co_csdo_abort_res(sdo, ac);
1816 }
1817 
1818 static co_csdo_state_t *
1819 co_csdo_blk_dn_end_on_time(co_csdo_t *sdo, const struct timespec *tp)
1820 {
1821  (void)tp;
1822 
1823  return co_csdo_abort_res(sdo, CO_SDO_AC_TIMEOUT);
1824 }
1825 
1826 static co_csdo_state_t *
1828 {
1829  assert(sdo);
1830  assert(msg);
1831 
1832  if (__unlikely(msg->len < 1))
1833  return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1834  uint8_t cs = msg->data[0];
1835 
1836  // Check the server command specifier.
1837  co_unsigned32_t ac;
1838  switch (cs & CO_SDO_CS_MASK) {
1839  case CO_SDO_SCS_BLK_DN_RES: break;
1840  case CO_SDO_CS_ABORT:
1841  ac = msg->len < 8 ? 0 : ldle_u32(msg->data + 4);
1842  return co_csdo_abort_ind(sdo, ac ? ac : CO_SDO_AC_ERROR);
1843  default: return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1844  }
1845 
1846  // Check the server subcommand.
1848  return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1849 
1850  return co_csdo_abort_ind(sdo, 0);
1851 }
1852 
1853 static co_csdo_state_t *
1854 co_csdo_blk_up_ini_on_abort(co_csdo_t *sdo, co_unsigned32_t ac)
1855 {
1856  return co_csdo_abort_res(sdo, ac);
1857 }
1858 
1859 static co_csdo_state_t *
1860 co_csdo_blk_up_ini_on_time(co_csdo_t *sdo, const struct timespec *tp)
1861 {
1862  (void)tp;
1863 
1864  return co_csdo_abort_res(sdo, CO_SDO_AC_TIMEOUT);
1865 }
1866 
1867 static co_csdo_state_t *
1869 {
1870  assert(sdo);
1871  assert(msg);
1872 
1873  if (__unlikely(msg->len < 1))
1874  return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1875  uint8_t cs = msg->data[0];
1876 
1877  // Check the server command specifier.
1878  co_unsigned32_t ac;
1879  switch (cs & CO_SDO_CS_MASK) {
1880  case CO_SDO_SCS_UP_INI_RES:
1881  // In case of a server-induced protocol switch, fall back to the
1882  // SDO upload protocol.
1883  return co_csdo_up_ini_on_recv(sdo, msg);
1884  case CO_SDO_SCS_BLK_UP_RES: break;
1885  case CO_SDO_CS_ABORT:
1886  ac = msg->len < 8 ? 0 : ldle_u32(msg->data + 4);
1887  return co_csdo_abort_ind(sdo, ac ? ac : CO_SDO_AC_ERROR);
1888  default: return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1889  }
1890 
1891  // Check the server subcommand.
1892  if (__unlikely((cs & 0x01) != CO_SDO_SC_INI_BLK))
1893  return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1894 
1895  // Check if the server supports generating a CRC.
1896  sdo->crc = !!(cs & CO_SDO_BLK_CRC);
1897 
1898  // Check the object index and sub-index.
1899  if (__unlikely(msg->len < 4))
1900  return co_csdo_abort_res(sdo, CO_SDO_AC_ERROR);
1901  co_unsigned16_t idx = ldle_u16(msg->data + 1);
1902  co_unsigned8_t subidx = msg->data[3];
1903  if (__unlikely(idx != sdo->idx || subidx != sdo->subidx))
1904  return co_csdo_abort_res(sdo, CO_SDO_AC_ERROR);
1905 
1906  // Obtain the data set size.
1907  sdo->size = 0;
1908  if (cs & CO_SDO_BLK_SIZE_IND) {
1909  if (__unlikely(msg->len < 8))
1910  return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1911  sdo->size = ldle_u32(msg->data + 4);
1912  }
1913 
1914  // Allocate the buffer.
1915  if (__unlikely(sdo->size && !membuf_reserve(&sdo->buf, sdo->size)))
1916  return co_csdo_abort_res(sdo, CO_SDO_AC_NO_MEM);
1917 
1918  sdo->ackseq = 0;
1919 
1920  if (sdo->timeout)
1921  can_timer_timeout(sdo->timer, sdo->net, sdo->timeout);
1923  return co_csdo_blk_up_sub_state;
1924 }
1925 
1926 static co_csdo_state_t *
1927 co_csdo_blk_up_sub_on_abort(co_csdo_t *sdo, co_unsigned32_t ac)
1928 {
1929  return co_csdo_abort_res(sdo, ac);
1930 }
1931 
1932 static co_csdo_state_t *
1933 co_csdo_blk_up_sub_on_time(co_csdo_t *sdo, const struct timespec *tp)
1934 {
1935  (void)tp;
1936 
1937  return co_csdo_abort_res(sdo, CO_SDO_AC_TIMEOUT);
1938 }
1939 
1940 static co_csdo_state_t *
1942 {
1943  assert(sdo);
1944  assert(msg);
1945 
1946  if (__unlikely(msg->len < 1))
1947  return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1948  uint8_t cs = msg->data[0];
1949 
1950  if (__unlikely(cs == CO_SDO_CS_ABORT)) {
1951  co_unsigned32_t ac = msg->len < 8 ? 0 : ldle_u32(msg->data + 4);
1952  return co_csdo_abort_ind(sdo, ac ? ac : CO_SDO_AC_ERROR);
1953  }
1954 
1955  uint8_t seqno = cs & ~CO_SDO_SEQ_LAST;
1956  int last = !!(cs & CO_SDO_SEQ_LAST);
1957 
1958  // Only accept sequential segments. Dropped segments will be resent
1959  // after the confirmation message.
1960  if (seqno == sdo->ackseq + 1) {
1961  sdo->ackseq++;
1962 
1963  // Reserve room in the buffer, if necessary.
1964  if (__unlikely(!membuf_reserve(&sdo->buf, 7)))
1965  return co_csdo_abort_res(sdo, CO_SDO_AC_NO_MEM);
1966 
1967  // Copy the data to the buffer.
1968  memcpy(sdo->buf.cur, msg->data + 1, 7);
1969  sdo->buf.cur += 7;
1970  }
1971 
1972  // If this is the last segment in the block, send a confirmation.
1973  if (seqno == sdo->blksize || last) {
1975  sdo->ackseq = 0;
1976  }
1977 
1978  if (sdo->timeout)
1979  can_timer_timeout(sdo->timer, sdo->net, sdo->timeout);
1981 }
1982 
1983 static co_csdo_state_t *
1984 co_csdo_blk_up_end_on_abort(co_csdo_t *sdo, co_unsigned32_t ac)
1985 {
1986  return co_csdo_abort_res(sdo, ac);
1987 }
1988 
1989 static co_csdo_state_t *
1990 co_csdo_blk_up_end_on_time(co_csdo_t *sdo, const struct timespec *tp)
1991 {
1992  (void)tp;
1993 
1994  return co_csdo_abort_res(sdo, CO_SDO_AC_TIMEOUT);
1995 }
1996 
1997 static co_csdo_state_t *
1999 {
2000  assert(sdo);
2001  assert(msg);
2002 
2003  if (__unlikely(msg->len < 1))
2004  return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
2005  uint8_t cs = msg->data[0];
2006 
2007  // Check the server command specifier.
2008  co_unsigned32_t ac;
2009  switch (cs & CO_SDO_CS_MASK) {
2010  case CO_SDO_SCS_BLK_UP_RES: break;
2011  case CO_SDO_CS_ABORT:
2012  ac = msg->len < 8 ? 0 : ldle_u32(msg->data + 4);
2013  return co_csdo_abort_ind(sdo, ac ? ac : CO_SDO_AC_ERROR);
2014  default: return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
2015  }
2016 
2017  // Check the server subcommand.
2019  return co_csdo_abort_res(sdo, CO_SDO_AC_NO_CS);
2020 
2021  // Discard the bytes in the last segment that did not contain data.
2022  sdo->buf.cur -= 7 - CO_SDO_BLK_SIZE_GET(cs);
2023 
2024  // Check the total length.
2025  if (__unlikely(sdo->size && membuf_size(&sdo->buf) != sdo->size))
2027 
2028  // Check the CRC.
2029  if (sdo->crc) {
2030  uint16_t crc = ldle_u16(msg->data + 1);
2031  if (__unlikely(crc != co_crc(0, sdo->buf.begin, sdo->size)))
2032  return co_csdo_abort_res(sdo, CO_SDO_AC_BLK_CRC);
2033  }
2034 
2036  return co_csdo_abort_ind(sdo, 0);
2037 }
2038 
2039 static co_csdo_state_t *
2040 co_csdo_abort_ind(co_csdo_t *sdo, co_unsigned32_t ac)
2041 {
2042  assert(sdo);
2043 
2044  sdo->ac = ac;
2045  return co_csdo_abort_state;
2046 }
2047 
2048 static co_csdo_state_t *
2049 co_csdo_abort_res(co_csdo_t *sdo, co_unsigned32_t ac)
2050 {
2051  co_csdo_send_abort(sdo, ac);
2052  return co_csdo_abort_ind(sdo, ac);
2053 }
2054 
2055 static int
2056 co_csdo_dn_ind(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx,
2057  const void *ptr, size_t n, co_csdo_dn_con_t *con, void *data)
2058 {
2059  assert(sdo);
2060 
2061  // Check whether the SDO exists and is valid.
2062  int valid_req = !(sdo->par.cobid_req & CO_SDO_COBID_VALID);
2063  int valid_res = !(sdo->par.cobid_res & CO_SDO_COBID_VALID);
2064  if (__unlikely(!valid_req || !valid_res)) {
2066  return -1;
2067  }
2068 
2069  // Check whether we are in the waiting state.
2070  if (__unlikely(!co_csdo_is_idle(sdo))) {
2072  return -1;
2073  }
2074 
2075  sdo->ac = 0;
2076  sdo->idx = idx;
2077  sdo->subidx = subidx;
2078  sdo->size = ptr ? n : 0;
2079 
2080  sdo->toggle = 0;
2081  sdo->blksize = 0;
2082  sdo->ackseq = 0;
2083  sdo->crc = 0;
2084 
2085  // Allocate the buffer.
2086  membuf_clear(&sdo->buf);
2087  if (__unlikely(sdo->size && !membuf_reserve(&sdo->buf, sdo->size)))
2088  return -1;
2089 
2090  // Copy the bytes to the buffer.
2091  if (ptr)
2092  memcpy(sdo->buf.cur, ptr, sdo->size);
2093 
2094  sdo->dn_con = con;
2095  sdo->dn_con_data = data;
2096 
2097  sdo->up_con = NULL;
2098  sdo->up_con_data = NULL;
2099 
2100  return 0;
2101 }
2102 
2103 static int
2104 co_csdo_up_ind(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx,
2105  co_csdo_up_con_t *con, void *data)
2106 {
2107  assert(sdo);
2108 
2109  // Check whether the SDO exists and is valid.
2110  int valid_req = !(sdo->par.cobid_req & CO_SDO_COBID_VALID);
2111  int valid_res = !(sdo->par.cobid_res & CO_SDO_COBID_VALID);
2112  if (__unlikely(!valid_req || !valid_res)) {
2114  return -1;
2115  }
2116 
2117  // Check whether we are in the waiting state.
2118  if (__unlikely(!co_csdo_is_idle(sdo))) {
2120  return -1;
2121  }
2122 
2123  sdo->ac = 0;
2124  sdo->idx = idx;
2125  sdo->subidx = subidx;
2126  sdo->size = 0;
2127 
2128  sdo->toggle = 0;
2129  sdo->blksize = 0;
2130  sdo->ackseq = 0;
2131  sdo->crc = 0;
2132 
2133  membuf_clear(&sdo->buf);
2134 
2135  sdo->dn_con = NULL;
2136  sdo->dn_con_data = NULL;
2137 
2138  sdo->up_con = con;
2139  sdo->up_con_data = data;
2140 
2141  return 0;
2142 }
2143 
2144 static void
2145 co_csdo_send_abort(co_csdo_t *sdo, co_unsigned32_t ac)
2146 {
2147  assert(sdo);
2148 
2149  struct can_msg msg;
2151  stle_u32(msg.data + 4, ac);
2152  can_net_send(sdo->net, &msg);
2153 }
2154 
2155 static void
2157 {
2158  assert(sdo);
2159  assert(sdo->size && sdo->size <= 4);
2160 
2161  uint8_t cs = CO_SDO_CCS_DN_INI_REQ | CO_SDO_INI_SIZE_EXP_SET(sdo->size);
2162 
2163  struct can_msg msg;
2164  co_csdo_init_ini_req(sdo, &msg, cs);
2165  memcpy(msg.data + 4, sdo->buf.cur, sdo->size);
2166  sdo->buf.cur += sdo->size;
2167  can_net_send(sdo->net, &msg);
2168 }
2169 
2170 static void
2172 {
2173  assert(sdo);
2174  assert(!sdo->size || sdo->size > 4);
2175 
2177 
2178  struct can_msg msg;
2179  co_csdo_init_ini_req(sdo, &msg, cs);
2180  stle_u32(msg.data + 4, sdo->size);
2181  can_net_send(sdo->net, &msg);
2182 
2183  if (sdo->size && sdo->dn_ind)
2184  sdo->dn_ind(sdo, sdo->idx, sdo->subidx, sdo->size, 0,
2185  sdo->dn_ind_data);
2186 }
2187 
2188 static void
2189 co_csdo_send_dn_seg_req(co_csdo_t *sdo, uint32_t n, int last)
2190 {
2191  assert(sdo);
2192  assert(n <= 7);
2193 
2194  uint8_t cs = CO_SDO_CCS_DN_SEG_REQ | sdo->toggle
2195  | CO_SDO_SEG_SIZE_SET(n);
2196  sdo->toggle ^= CO_SDO_SEG_TOGGLE;
2197  if (last)
2198  cs |= CO_SDO_SEG_LAST;
2199 
2200  struct can_msg msg;
2201  co_csdo_init_seg_req(sdo, &msg, cs);
2202  memcpy(msg.data + 1, sdo->buf.cur, n);
2203  sdo->buf.cur += n;
2204  can_net_send(sdo->net, &msg);
2205 
2206  if ((last || !(membuf_size(&sdo->buf) % (CO_SDO_MAX_SEQNO * 7)))
2207  && sdo->size && sdo->dn_ind)
2208  sdo->dn_ind(sdo, sdo->idx, sdo->subidx, sdo->size,
2209  membuf_size(&sdo->buf), sdo->dn_ind_data);
2210 }
2211 
2212 static void
2214 {
2215  assert(sdo);
2216 
2217  uint8_t cs = CO_SDO_CCS_UP_INI_REQ;
2218 
2219  struct can_msg msg;
2220  co_csdo_init_ini_req(sdo, &msg, cs);
2221  can_net_send(sdo->net, &msg);
2222 }
2223 
2224 static void
2226 {
2227  assert(sdo);
2228 
2229  uint8_t cs = CO_SDO_CCS_UP_SEG_REQ | sdo->toggle;
2230  sdo->toggle ^= CO_SDO_SEG_TOGGLE;
2231 
2232  struct can_msg msg;
2233  co_csdo_init_seg_req(sdo, &msg, cs);
2234  can_net_send(sdo->net, &msg);
2235 }
2236 
2237 static void
2239 {
2240  assert(sdo);
2241 
2242  uint8_t cs = CO_SDO_CCS_BLK_DN_REQ | CO_SDO_BLK_CRC
2244 
2245  struct can_msg msg;
2246  co_csdo_init_ini_req(sdo, &msg, cs);
2247  stle_u32(msg.data + 4, sdo->size);
2248  can_net_send(sdo->net, &msg);
2249 }
2250 
2251 static void
2253 {
2254  assert(sdo);
2255  assert(seqno && seqno <= CO_SDO_MAX_SEQNO);
2256 
2257  size_t n = sdo->size - membuf_size(&sdo->buf);
2258  int last = n <= 7;
2259  n = MIN(n, 7);
2260 
2261  uint8_t cs = seqno;
2262  if (last)
2263  cs |= CO_SDO_SEQ_LAST;
2264 
2265  struct can_msg msg;
2266  co_csdo_init_seg_req(sdo, &msg, cs);
2267  memcpy(msg.data + 1, sdo->buf.cur, n);
2268  sdo->buf.cur += n;
2269  can_net_send(sdo->net, &msg);
2270 }
2271 
2272 static void
2274 {
2275  assert(sdo);
2276 
2277  // Compute the number of bytes in the last segment containing data.
2278  uint8_t n = sdo->size ? (sdo->size - 1) % 7 + 1 : 0;
2279 
2281  | CO_SDO_BLK_SIZE_SET(n);
2282 
2283  uint16_t crc = sdo->crc ? co_crc(0, sdo->buf.begin, sdo->size) : 0;
2284 
2285  struct can_msg msg;
2286  co_csdo_init_seg_req(sdo, &msg, cs);
2287  stle_u16(msg.data + 1, crc);
2288  can_net_send(sdo->net, &msg);
2289 }
2290 
2291 static void
2293 {
2294  assert(sdo);
2295 
2297 
2298  struct can_msg msg;
2299  co_csdo_init_ini_req(sdo, &msg, cs);
2300  msg.data[4] = sdo->blksize;
2301  msg.data[5] = pst;
2302  can_net_send(sdo->net, &msg);
2303 }
2304 
2305 static void
2307 {
2308  assert(sdo);
2309 
2311 
2312  struct can_msg msg;
2313  co_csdo_init_seg_req(sdo, &msg, cs);
2314  can_net_send(sdo->net, &msg);
2315 
2316  if (sdo->size && sdo->up_ind)
2317  sdo->up_ind(sdo, sdo->idx, sdo->subidx, sdo->size, 0,
2318  sdo->up_ind_data);
2319 }
2320 
2321 static void
2323 {
2324  assert(sdo);
2325 
2327 
2328  struct can_msg msg;
2329  co_csdo_init_seg_req(sdo, &msg, cs);
2330  msg.data[1] = sdo->ackseq;
2331  msg.data[2] = sdo->blksize;
2332  can_net_send(sdo->net, &msg);
2333 
2334  if (sdo->size && sdo->up_ind)
2335  sdo->up_ind(sdo, sdo->idx, sdo->subidx, sdo->size,
2336  membuf_size(&sdo->buf), sdo->up_ind_data);
2337 }
2338 
2339 static void
2341 {
2342  assert(sdo);
2343 
2345 
2346  struct can_msg msg;
2347  co_csdo_init_seg_req(sdo, &msg, cs);
2348  can_net_send(sdo->net, &msg);
2349 }
2350 
2351 static void
2352 co_csdo_init_ini_req(co_csdo_t *sdo, struct can_msg *msg, uint8_t cs)
2353 {
2354  assert(sdo);
2355  assert(msg);
2356 
2357  *msg = (struct can_msg)CAN_MSG_INIT;
2358  msg->id = sdo->par.cobid_req;
2359  if (sdo->par.cobid_req & CO_SDO_COBID_FRAME) {
2360  msg->id &= CAN_MASK_EID;
2361  msg->flags |= CAN_FLAG_IDE;
2362  } else {
2363  msg->id &= CAN_MASK_BID;
2364  }
2365  msg->len = CAN_MAX_LEN;
2366  msg->data[0] = cs;
2367  stle_u16(msg->data + 1, sdo->idx);
2368  msg->data[3] = sdo->subidx;
2369 }
2370 
2371 static void
2372 co_csdo_init_seg_req(co_csdo_t *sdo, struct can_msg *msg, uint8_t cs)
2373 {
2374  assert(sdo);
2375  assert(msg);
2376 
2377  *msg = (struct can_msg)CAN_MSG_INIT;
2378  msg->id = sdo->par.cobid_req;
2379  if (sdo->par.cobid_req & CO_SDO_COBID_FRAME) {
2380  msg->id &= CAN_MASK_EID;
2381  msg->flags |= CAN_FLAG_IDE;
2382  } else {
2383  msg->id &= CAN_MASK_BID;
2384  }
2385  msg->len = CAN_MAX_LEN;
2386  msg->data[0] = cs;
2387 }
2388 
2389 #endif // !LELY_NO_CO_CSDO
A CANopen SDO upload/download request.
Definition: sdo.h:178
#define CO_SDO_SCS_UP_INI_RES
The SDO server command specifier &#39;upload initiate&#39; response.
Definition: sdo.h:43
static co_csdo_state_t * co_csdo_blk_up_sub_on_recv(co_csdo_t *sdo, const struct can_msg *msg)
The &#39;CAN frame received&#39; transition function of the &#39;block upload sub-block&#39; state.
Definition: csdo.c:1941
static co_csdo_state_t * co_csdo_blk_dn_ini_on_recv(co_csdo_t *sdo, const struct can_msg *msg)
The &#39;CAN frame received&#39; transition function of the &#39;block download initiate&#39; state.
Definition: csdo.c:1683
A CAN or CAN FD format frame.
Definition: msg.h:88
void stle_u32(void *ptr, uint_least32_t x)
Stores a 32-bit unsigned integer in little-endian byte order.
Definition: endian.h:534
size_t membuf_write(struct membuf *buf, const void *ptr, size_t size)
Writes data to a memory buffer.
Definition: membuf.h:192
int co_dev_dn_val_req(co_dev_t *dev, 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 local device.
Definition: csdo.c:637
static int co_csdo_recv(const struct can_msg *msg, void *data)
The CAN receive callback function for a Client-SDO service.
Definition: csdo.c:1272
static co_csdo_state_t * co_csdo_up_seg_on_abort(co_csdo_t *sdo, co_unsigned32_t ac)
The &#39;abort&#39; transition function of the &#39;upload segment&#39; state.
Definition: csdo.c:1598
void co_obj_set_dn_ind(co_obj_t *obj, co_sub_dn_ind_t *ind, void *data)
Sets the download indication function for a CANopen object.
Definition: obj.c:336
static void co_csdo_send_blk_up_sub_res(co_csdo_t *sdo)
Sends a Client-SDO &#39;block upload sub-block&#39; response.
Definition: csdo.c:2322
#define CO_SDO_AC_BLK_SIZE
SDO abort code: Invalid block size (block mode only).
Definition: sdo.h:72
uint_least32_t id
The identifier (11 or 29 bits, depending on the CAN_FLAG_IDE flag).
Definition: msg.h:90
can_net_t * net
A pointer to a CAN network interface.
Definition: csdo.c:47
size_t co_obj_sizeof_val(const co_obj_t *obj)
Returns size (in bytes) of the value of a CANopen object.
Definition: obj.c:281
co_unsigned8_t n
Highest sub-index supported.
Definition: sdo.h:47
co_unsigned32_t cobid_res
COB-ID server -> client.
Definition: sdo.h:51
static co_csdo_state_t * co_csdo_blk_dn_end_on_recv(co_csdo_t *sdo, const struct can_msg *msg)
The &#39;CAN frame received&#39; transition function of the &#39;block download end&#39; state.
Definition: csdo.c:1827
co_unsigned8_t num
The SDO number.
Definition: csdo.c:51
const struct co_sdo_par * co_csdo_get_par(const co_csdo_t *sdo)
Returns a pointer to the SDO parameter record of a Client-SDO.
Definition: csdo.c:928
static co_csdo_state_t *const co_csdo_blk_dn_sub_state
The &#39;block download sub-block&#39; state.
Definition: csdo.c:374
A CAN network interface.
Definition: net.c:37
#define CO_SDO_SEG_SIZE_SET(n)
Sets the SDO size indicator, indicating n bytes contain segment data (in the range [0...
Definition: sdo.h:139
#define CAN_MASK_EID
The mask used to extract the 29-bit Extended Identifier from a CAN frame.
Definition: msg.h:34
uint_least8_t len
The number of bytes in data (or the requested number of bytes in case of a remote frame)...
Definition: msg.h:101
void co_csdo_set_timeout(co_csdo_t *sdo, int timeout)
Sets the timeout of a Client-SDO.
Definition: csdo.c:944
co_csdo_up_con_t * up_con
A pointer to the upload confirmation function.
Definition: csdo.c:89
size_t size
The total size (in bytes) of the value to be uploaded/downloaded.
Definition: sdo.h:184
A CANopen sub-object.
Definition: obj.h:54
int co_dev_dn_req(co_dev_t *dev, 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 local device.
Definition: csdo.c:600
int co_sdo_req_up(struct co_sdo_req *req, const void *ptr, size_t n, co_unsigned32_t *pac)
Writes the specified bytes to a buffer and constructs a CANopen SDO upload request.
Definition: sdo.c:251
static co_csdo_state_t * co_csdo_blk_dn_sub_on_recv(co_csdo_t *sdo, const struct can_msg *msg)
The &#39;CAN frame received&#39; transition function of the &#39;block download sub-block&#39; state.
Definition: csdo.c:1766
#define CO_SDO_AC_NO_DATA
SDO abort code: No data available.
Definition: sdo.h:175
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
int errnum2c(errnum_t errnum)
Transforms a platform-independent error number to a native error code.
Definition: errnum.c:825
static co_csdo_state_t *const co_csdo_up_ini_state
The &#39;upload initiate&#39; state.
Definition: csdo.c:301
static void co_csdo_send_up_ini_req(co_csdo_t *sdo)
Sends a Client-SDO &#39;upload initiate&#39; request.
Definition: csdo.c:2213
static co_csdo_state_t * co_csdo_blk_up_ini_on_abort(co_csdo_t *sdo, co_unsigned32_t ac)
The &#39;abort&#39; transition function of the &#39;block upload initiate&#39; state.
Definition: csdo.c:1854
static void co_csdo_enter(co_csdo_t *sdo, co_csdo_state_t *next)
Enters the specified state of a Client-SDO service and invokes the exit and entry functions...
Definition: csdo.c:1306
#define CAN_MSG_INIT
The static initializer for can_msg.
Definition: msg.h:114
void * up_con_data
A pointer to user-specified data for up_con.
Definition: csdo.c:91
static co_csdo_state_t * co_csdo_blk_dn_sub_on_abort(co_csdo_t *sdo, co_unsigned32_t ac)
The &#39;abort&#39; transition function of the &#39;block download sub-block&#39; state.
Definition: csdo.c:1752
uint32_t size
The data set size (in bytes).
Definition: csdo.c:69
void set_errnum(errnum_t errnum)
Sets the current (thread-specific) platform-independent error number to errnum.
Definition: errnum.h:375
#define CO_SDO_INI_SIZE_EXP_SET(n)
Sets the SDO size indicator, indicating the expedited transfer of n bytes (in the range [1...
Definition: sdo.h:113
#define CO_SDO_SCS_DN_INI_RES
The SDO server command specifier &#39;download initiate&#39; response.
Definition: sdo.h:37
void membuf_clear(struct membuf *buf)
Clears a memory buffer.
Definition: membuf.h:150
#define MEMBUF_INIT
The static initializer for struct membuf.
Definition: membuf.h:45
#define CO_SDO_BLK_CRC
The SDO CRC support flag.
Definition: sdo.h:148
void membuf_init(struct membuf *buf)
Initializes a memory buffer.
Definition: membuf.h:138
#define CO_SDO_AC_PARAM_VAL
SDO abort code: Invalid value for parameter (download only).
Definition: sdo.h:135
#define CO_SDO_COBID_VALID
The bit in the SDO COB-ID specifying whether the SDO exists and is valid.
Definition: sdo.h:33
void co_csdo_get_up_ind(const co_csdo_t *sdo, co_csdo_ind_t **pind, void **pdata)
Retrieves the indication function used to notify the user of the progress of the current SDO upload r...
Definition: csdo.c:975
A union of the CANopen static data types.
Definition: val.h:163
int co_sdo_req_up_val(struct co_sdo_req *req, co_unsigned16_t type, const void *val, co_unsigned32_t *pac)
Writes the specified value to a buffer and constructs a CANopen SDO upload request.
Definition: sdo.c:278
static co_csdo_state_t * co_csdo_blk_dn_end_on_abort(co_csdo_t *sdo, co_unsigned32_t ac)
The &#39;abort&#39; transition function of the &#39;block download end&#39; state.
Definition: csdo.c:1813
static co_csdo_state_t * co_csdo_blk_up_end_on_recv(co_csdo_t *sdo, const struct can_msg *msg)
The &#39;CAN frame received&#39; transition function of the &#39;block upload end&#39; state.
Definition: csdo.c:1998
#define CO_SDO_SCS_UP_SEG_RES
The SDO server command specifier &#39;upload segment&#39; response.
Definition: sdo.h:46
static co_csdo_state_t * co_csdo_dn_seg_on_abort(co_csdo_t *sdo, co_unsigned32_t ac)
The &#39;abort&#39; transition function of the &#39;download segment&#39; state.
Definition: csdo.c:1480
uint_least16_t ldle_u16(const void *ptr)
Loads a 16-bit unsigned integer in little-endian byte order.
Definition: endian.h:487
static co_csdo_state_t * co_csdo_blk_up_sub_on_time(co_csdo_t *sdo, const struct timespec *tp)
The &#39;timeout&#39; transition function of the &#39;block upload sub-block&#39; state.
Definition: csdo.c:1933
#define CO_SDO_AC_ERROR
SDO abort code: General error.
Definition: sdo.h:150
static co_csdo_state_t * co_csdo_blk_dn_ini_on_abort(co_csdo_t *sdo, co_unsigned32_t ac)
The &#39;abort&#39; transition function of the &#39;block download initiate&#39; state.
Definition: csdo.c:1669
#define CO_SDO_CS_ABORT
The SDO client/server command specifier &#39;abort transfer&#39; request.
Definition: sdo.h:34
#define MIN(a, b)
Returns the minimum of a and b.
Definition: util.h:57
#define CO_SDO_BLK_SIZE_IND
The SDO size indicator flag indicating that the data set size is indicated.
Definition: sdo.h:145
#define CAN_MASK_BID
The mask used to extract the 11-bit Base Identifier from a CAN frame.
Definition: msg.h:31
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
co_unsigned32_t cobid_req
COB-ID client -> server.
Definition: sdo.h:49
#define CO_SDO_INI_SIZE_EXP_GET(cs)
Retrieves the number of bytes containing expedited data from the SDO size indicator.
Definition: sdo.h:105
co_unsigned8_t co_obj_get_code(const co_obj_t *obj)
Returns the object code of a CANopen object.
Definition: obj.c:250
This header file is part of the CANopen library; it contains the Cyclic Redundancy Check (CRC) declar...
static int co_csdo_up_ind(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx, co_csdo_up_con_t *con, void *data)
Processes an upload request from a Client-SDO by checking and updating the state. ...
Definition: csdo.c:2104
co_csdo_ind_t * up_ind
A pointer to the upload progress indication function.
Definition: csdo.c:93
This header file is part of the CANopen library; it contains the Client-SDO declarations.
static co_csdo_state_t * co_csdo_up_ini_on_recv(co_csdo_t *sdo, const struct can_msg *msg)
The &#39;CAN frame received&#39; transition function of the &#39;upload initiate&#39; state.
Definition: csdo.c:1535
void co_csdo_dn_con_t(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx, co_unsigned32_t ac, void *data)
The type of a CANopen Client-SDO download confirmation callback function, invoked when a download req...
Definition: csdo.h:43
#define CO_SDO_AC_BLK_CRC
SDO abort code: CRC error (block mode only).
Definition: sdo.h:78
static co_csdo_state_t * co_csdo_dn_ini_on_recv(co_csdo_t *sdo, const struct can_msg *msg)
The &#39;CAN frame received&#39; transition function of the &#39;download initiate&#39; state.
Definition: csdo.c:1430
#define CO_SDO_AC_NO_SDO
SDO abort code: Resource not available: SDO connection.
Definition: sdo.h:147
static void co_csdo_emit_recv(co_csdo_t *sdo, const struct can_msg *msg)
Invokes the &#39;CAN frame received&#39; transition function of the current state of a Client-SDO service...
Definition: csdo.c:1343
This header file is part of the CANopen library; it contains the CANopen value declarations.
void set_errc(int errc)
Sets the current (thread-specific) native error code to errc.
Definition: errnum.c:957
#define CO_SDO_CCS_DN_INI_REQ
The SDO client command specifier &#39;download initiate&#39; request.
Definition: sdo.h:55
#define CO_SDO_CCS_BLK_UP_REQ
The SDO client command specifier &#39;block upload&#39; request.
Definition: sdo.h:70
int co_csdo_up_req(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx, co_csdo_up_con_t *con, void *data)
Submits an upload request to a remote Server-SDO.
Definition: csdo.c:1080
A CANopen Client-SDO state.
Definition: csdo.c:163
co_unsigned8_t subidx
The current object sub-index.
Definition: csdo.c:67
void co_csdo_destroy(co_csdo_t *csdo)
Destroys a CANopen Client-SDO service.
Definition: csdo.c:894
static void co_csdo_send_blk_dn_sub_req(co_csdo_t *sdo, uint8_t seqno)
Sends a Client-SDO &#39;block download sub-block&#39; request.
Definition: csdo.c:2252
#define CO_SDO_INI_SIZE_EXP
The SDO size indicator flag indicating expedited transfer.
Definition: sdo.h:97
void * dn_con_data
A pointer to user-specified data for dn_con.
Definition: csdo.c:83
struct membuf buf
The buffer.
Definition: csdo.c:79
#define CO_SDO_SC_INI_BLK
The SDO server/client subcommand &#39;initiate download/upload&#39;.
Definition: sdo.h:76
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
static co_csdo_state_t * co_csdo_dn_ini_on_time(co_csdo_t *sdo, const struct timespec *tp)
The &#39;timeout&#39; transition function of the &#39;download initiate&#39; state.
Definition: csdo.c:1422
#define CO_DEFTYPE_UNSIGNED32
The data type (and object index) of a 32-bit unsigned integer.
Definition: type.h:50
#define CO_SDO_AC_TOGGLE
SDO abort code: Toggle bit not altered.
Definition: sdo.h:63
static void co_csdo_send_blk_up_ini_req(co_csdo_t *sdo, uint8_t pst)
Sends a Client-SDO &#39;block upload initiate&#39; request.
Definition: csdo.c:2292
uint_least8_t data[CAN_MSG_MAX_LEN]
The frame payload (in case of a data frame).
Definition: msg.h:103
int co_csdo_get_timeout(const co_csdo_t *sdo)
Returns the timeout (in milliseconds) of a Client-SDO.
Definition: csdo.c:936
static co_csdo_state_t * co_csdo_blk_up_sub_on_abort(co_csdo_t *sdo, co_unsigned32_t ac)
The &#39;abort&#39; transition function of the &#39;block upload sub-block&#39; state.
Definition: csdo.c:1927
static void co_csdo_send_blk_up_end_res(co_csdo_t *sdo)
Sends a Client-SDO &#39;block upload end&#39; response.
Definition: csdo.c:2340
This header file is part of the utilities library; it contains the native and platform-independent er...
char * begin
A pointer to the first byte in the buffer.
Definition: membuf.h:39
size_t membuf_reserve(struct membuf *buf, size_t size)
Resizes a memory buffer, if necessary, to make room for at least an additional size bytes...
Definition: membuf.c:46
static co_csdo_state_t * co_csdo_dn_seg_on_time(co_csdo_t *sdo, const struct timespec *tp)
The &#39;timeout&#39; transition function of the &#39;download segment&#39; state.
Definition: csdo.c:1486
void co_csdo_up_con_t(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 type of a CANopen Client-SDO upload confirmation callback function, invoked when an upload reques...
Definition: csdo.h:59
static co_csdo_state_t *const co_csdo_abort_state
The &#39;abort transfer&#39; state.
Definition: csdo.c:229
void can_timer_stop(can_timer_t *timer)
Stops a CAN timer and unregisters it with a network interface.
Definition: net.c:468
static co_csdo_state_t *const co_csdo_dn_ini_state
The &#39;download initiate&#39; state.
Definition: csdo.c:253
#define CAN_FLAG_IDE
The Identifier Extension (IDE) flag.
Definition: msg.h:41
static void co_csdo_send_start_up_req(co_csdo_t *sdo)
Sends a Client-SDO &#39;start upload&#39; request.
Definition: csdo.c:2306
static co_csdo_state_t * co_csdo_blk_dn_sub_on_time(co_csdo_t *sdo, const struct timespec *tp)
The &#39;timeout&#39; transition function of the &#39;block download sub-block&#39; state.
Definition: csdo.c:1758
static void co_csdo_abort_on_leave(co_csdo_t *sdo)
The exit function of the &#39;abort transfer&#39; state.
Definition: csdo.c:1391
static co_csdo_state_t * co_csdo_dn_seg_on_enter(co_csdo_t *sdo)
The entry function of the &#39;download segment&#39; state.
Definition: csdo.c:1461
#define CAN_FLAG_EDL
The Extended Data Length (EDL) flag.
Definition: msg.h:55
static co_csdo_state_t * co_csdo_blk_up_ini_on_recv(co_csdo_t *sdo, const struct can_msg *msg)
The &#39;CAN frame received&#39; transition function of the &#39;block upload initiate&#39; state.
Definition: csdo.c:1868
void can_recv_destroy(can_recv_t *recv)
Destroys a CAN frame receiver.
Definition: net.c:562
This header file is part of the utilities library; it contains the byte order (endianness) function d...
#define CO_SDO_SCS_BLK_DN_RES
The SDO server command specifier &#39;block download&#39; response.
Definition: sdo.h:49
#define CO_SDO_AC_TIMEOUT
SDO abort code: SDO protocol timed out.
Definition: sdo.h:66
static co_csdo_state_t *const co_csdo_blk_dn_end_state
The &#39;block download end&#39; state.
Definition: csdo.c:398
#define CO_SDO_CCS_UP_INI_REQ
The SDO client command specifier &#39;upload initiate&#39; request.
Definition: sdo.h:61
This is the internal header file of the CANopen library.
char * cur
A pointer to one past the last byte written to the buffer.
Definition: membuf.h:37
int co_csdo_dn_req(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx, const void *ptr, size_t n, co_csdo_dn_con_t *con, void *data)
Submits a download request to a remote Server-SDO.
Definition: csdo.c:1011
co_csdo_state_t *(* on_time)(co_csdo_t *sdo, const struct timespec *tp)
A pointer to the transition function invoked when a timeout occurs.
Definition: csdo.c:184
void co_csdo_abort_req(co_csdo_t *sdo, co_unsigned32_t ac)
Submits an abort transfer request to a remote Server-SDO.
Definition: csdo.c:1003
static co_csdo_state_t * co_csdo_blk_dn_end_on_time(co_csdo_t *sdo, const struct timespec *tp)
The &#39;timeout&#39; transition function of the &#39;block download end&#39; state.
Definition: csdo.c:1819
co_unsigned32_t co_sub_up_ind(const co_sub_t *sub, struct co_sdo_req *req)
Invokes the upload indication function of a CANopen sub-object, registered with co_sub_set_up_ind().
Definition: obj.c:890
#define CO_SDO_INI_SIZE_IND
The SDO size indicator flag indicating that the data set size is indicated.
Definition: sdo.h:94
can_timer_t * timer
A pointer to the CAN timer.
Definition: csdo.c:59
static co_csdo_state_t *const co_csdo_up_seg_state
The &#39;upload segment&#39; state.
Definition: csdo.c:322
co_csdo_state_t * state
A pointer to the current state.
Definition: csdo.c:61
size_t co_val_sizeof(co_unsigned16_t type, const void *val)
Returns the size (in bytes) of a value of the specified data type.
Definition: val.c:293
int get_errc(void)
Returns the last (thread-specific) native error code set by a system call or library function...
Definition: errnum.c:947
#define CO_SDO_SC_END_BLK
The SDO server/client subcommand &#39;end block download/upload&#39;.
Definition: sdo.h:79
A CAN timer.
Definition: net.c:63
co_csdo_state_t *(* on_recv)(co_csdo_t *sdo, const struct can_msg *msg)
A pointer to the transition function invoked when a CAN frame has been received.
Definition: csdo.c:194
#define MAX(a, b)
Returns the maximum of a and b.
Definition: util.h:65
A CANopen Client-SDO.
Definition: csdo.c:45
static co_csdo_state_t *const co_csdo_dn_seg_state
The &#39;download segment&#39; state.
Definition: csdo.c:280
#define CO_SDO_CCS_DN_SEG_REQ
The SDO client command specifier &#39;download segment&#39; request.
Definition: sdo.h:58
void can_timer_destroy(can_timer_t *timer)
Destroys a CAN timer.
Definition: net.c:407
int co_sub_dn(co_sub_t *sub, void *val)
Downloads (moves) a value into a CANopen sub-object if the refuse-write-on-download flag (CO_OBJ_FLAG...
Definition: obj.c:832
#define CO_DEFTYPE_UNSIGNED8
The data type (and object index) of an 8-bit unsigned integer.
Definition: type.h:44
static void co_csdo_send_blk_dn_end_req(co_csdo_t *sdo)
Sends a Client-SDO &#39;block download end&#39; request.
Definition: csdo.c:2273
#define CO_NUM_NODES
The maximum number of nodes in a CANopen network.
Definition: dev.h:56
static co_csdo_state_t *const co_csdo_blk_up_end_state
The &#39;block upload end&#39; state.
Definition: csdo.c:469
void(* on_leave)(co_csdo_t *sdo)
A pointer to the function invoked when the current state is left.
Definition: csdo.c:196
co_dev_t * co_csdo_get_dev(const co_csdo_t *sdo)
Returns a pointer to the CANopen device of a Client-SDO.
Definition: csdo.c:912
int co_csdo_blk_up_req(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx, uint8_t pst, co_csdo_up_con_t *con, void *data)
Submits a block upload request to a remote Server-SDO.
Definition: csdo.c:1121
static co_csdo_state_t * co_csdo_abort_ind(co_csdo_t *sdo, co_unsigned32_t ac)
Processes an abort transfer indication by aborting any ongoing transfer of a Client-SDO and returning...
Definition: csdo.c:2040
Invalid argument.
Definition: errnum.h:129
static co_csdo_state_t *const co_csdo_wait_state
The &#39;waiting&#39; state.
Definition: csdo.c:215
static co_csdo_state_t * co_csdo_abort_on_enter(co_csdo_t *sdo)
The entry function of the &#39;abort transfer&#39; state.
Definition: csdo.c:1381
void stle_u16(void *ptr, uint_least16_t x)
Stores a 16-bit unsigned integer in little-endian byte order.
Definition: endian.h:480
uint8_t toggle
The current value of the toggle bit.
Definition: csdo.c:71
co_unsigned16_t idx
The current object index.
Definition: csdo.c:65
#define CO_SDO_BLK_SIZE_GET(cs)
Retrieves the number of bytes containing segment data from the SDO size indicator.
Definition: sdo.h:165
A memory buffer.
Definition: membuf.h:35
An SDO parameter record.
Definition: sdo.h:45
static co_csdo_state_t * co_csdo_dn_ini_on_abort(co_csdo_t *sdo, co_unsigned32_t ac)
The &#39;abort&#39; transition function of the &#39;download initiate&#39; state.
Definition: csdo.c:1416
int co_csdo_dn_val_req(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx, co_unsigned16_t type, const void *val, co_csdo_dn_con_t *con, void *data)
Submits a download request to a remote Server-SDO.
Definition: csdo.c:1036
static void co_csdo_emit_abort(co_csdo_t *sdo, co_unsigned32_t ac)
Invokes the &#39;abort&#39; transition function of the current state of a Client-SDO service.
Definition: csdo.c:1323
uint16_t co_crc(uint16_t crc, const void *ptr, size_t n)
Computes a CRC-16 checksum.
Definition: crc.c:28
static co_csdo_state_t * co_csdo_up_ini_on_time(co_csdo_t *sdo, const struct timespec *tp)
The &#39;timeout&#39; transition function of the &#39;upload initiate&#39; state.
Definition: csdo.c:1527
static co_csdo_state_t * co_csdo_wait_on_recv(co_csdo_t *sdo, const struct can_msg *msg)
The &#39;CAN frame received&#39; transition function of the &#39;waiting&#39; state.
Definition: csdo.c:1362
int errno2c(int errnum)
Transforms a standard C error number to a native error code.
Definition: errnum.c:43
#define CO_SDO_AC_NO_WRITE
SDO abort code: Attempt to write a read only object.
Definition: sdo.h:90
#define __unlikely(x)
Indicates to the compiler that the expression is most-likely false.
Definition: features.h:286
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 * up_ind_data
A pointer to user-specified data for up_ind.
Definition: csdo.c:95
static co_csdo_state_t *const co_csdo_blk_up_ini_state
The &#39;block upload initiate&#39; state.
Definition: csdo.c:422
static co_csdo_state_t * co_csdo_up_ini_on_abort(co_csdo_t *sdo, co_unsigned32_t ac)
The &#39;abort&#39; transition function of the &#39;upload initiate&#39; state.
Definition: csdo.c:1521
static void co_csdo_send_abort(co_csdo_t *sdo, co_unsigned32_t ac)
Sends an abort transfer request.
Definition: csdo.c:2145
static co_csdo_state_t * co_csdo_abort_res(co_csdo_t *sdo, co_unsigned32_t ac)
Sends an abort transfer request and aborts any ongoing transfer by invoking co_csdo_abort_ind().
Definition: csdo.c:2049
static void co_csdo_send_dn_ini_req(co_csdo_t *sdo)
Sends a Client-SDO &#39;download initiate&#39; request.
Definition: csdo.c:2171
static void co_csdo_emit_time(co_csdo_t *sdo, const struct timespec *tp)
Invokes the &#39;timeout&#39; transition function of the current state of a Client-SDO service.
Definition: csdo.c:1333
A CANopen device.
Definition: dev.c:38
static co_csdo_state_t *const co_csdo_blk_up_sub_state
The &#39;block upload sub-block&#39; state.
Definition: csdo.c:446
#define CO_SDO_COBID_FRAME
The bit in the SDO COB-ID specifying whether to use an 11-bit (0) or 29-bit (1) CAN-ID.
Definition: sdo.h:39
void co_csdo_set_dn_ind(co_csdo_t *sdo, co_csdo_ind_t *ind, void *data)
Sets the indication function used to notify the user of the progress of the current SDO download requ...
Definition: csdo.c:966
#define CO_SDO_SCS_DN_SEG_RES
The SDO server command specifier &#39;download segment&#39; response.
Definition: sdo.h:40
unsigned crc
A flag indicating whether a CRC should be generated.
Definition: csdo.c:77
co_dev_t * dev
A pointer to a CANopen device.
Definition: csdo.c:49
void can_recv_set_func(can_recv_t *recv, can_recv_func_t *func, void *data)
Sets the callback function used to process CAN frames with a receiver.
Definition: net.c:582
uint_least8_t flags
The flags (any combination of CAN_FLAG_IDE, CAN_FLAG_RTR, CAN_FLAG_EDL, CAN_FLAG_BRS and CAN_FLAG_ESI...
Definition: msg.h:95
#define CO_SDO_AC_NO_CS
SDO abort code: Client/server command specifier not valid or unknown.
Definition: sdo.h:69
co_obj_t * co_sub_get_obj(const co_sub_t *sub)
Returns the a pointer to the CANopen object containing the specified sub-object.
Definition: obj.c:458
co_csdo_state_t *(* on_abort)(co_csdo_t *sdo, co_unsigned32_t ac)
A pointer to the transition function invoked when an abort code has been received.
Definition: csdo.c:175
can_timer_t * can_timer_create(void)
Creates a new CAN timer.
Definition: net.c:382
static co_csdo_state_t * co_csdo_blk_dn_sub_on_enter(co_csdo_t *sdo)
The entry function of the &#39;block download sub-block&#39; state.
Definition: csdo.c:1728
#define CO_SDO_SCS_BLK_UP_RES
The SDO server command specifier &#39;block upload&#39; response.
Definition: sdo.h:52
static co_csdo_state_t * co_csdo_blk_up_end_on_abort(co_csdo_t *sdo, co_unsigned32_t ac)
The &#39;abort&#39; transition function of the &#39;block upload end&#39; state.
Definition: csdo.c:1984
co_csdo_t * co_csdo_create(can_net_t *net, co_dev_t *dev, co_unsigned8_t num)
Creates a new CANopen Client-SDO service.
Definition: csdo.c:867
int timeout
The SDO timeout (in milliseconds).
Definition: csdo.c:57
void membuf_fini(struct membuf *buf)
Finalizes a memory buffer.
Definition: membuf.c:38
void can_recv_stop(can_recv_t *recv)
Stops a CAN frame receiver from processing frames and unregisters it with the network interface...
Definition: net.c:613
This header file is part of the CANopen library; it contains the device description declarations...
#define CO_SDO_SC_START_UP
The SDO client subcommand &#39;start upload&#39;.
Definition: sdo.h:85
co_csdo_ind_t * dn_ind
A pointer to the download progress indication function.
Definition: csdo.c:85
static void co_csdo_init_seg_req(co_csdo_t *sdo, struct can_msg *msg, uint8_t cs)
Initializes a Client-SDO download/upload segment request CAN frame.
Definition: csdo.c:2372
void co_val_fini(co_unsigned16_t type, void *val)
Finalizes a value of the specified data type.
Definition: val.c:273
struct co_sdo_par par
The SDO parameter record.
Definition: csdo.c:53
co_unsigned8_t id
Node-ID of SDO&#39;s client resp. server.
Definition: sdo.h:53
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
#define CO_SDO_SEG_SIZE_GET(cs)
Retrieves the number of bytes containing segment data from the SDO size indicator.
Definition: sdo.h:131
#define CO_SDO_CS_MASK
The mask to extract the command specifier (CS) from an SDO command byte.
Definition: sdo.h:31
int co_csdo_is_idle(const co_csdo_t *sdo)
Returns 1 if the specified Client-SDO service is idle, and 0 if a transfer is ongoing.
Definition: csdo.c:995
#define CO_SDO_AC_NO_SUB
SDO abort code: Sub-index does not exist.
Definition: sdo.h:132
can_net_t * co_csdo_get_net(const co_csdo_t *sdo)
Returns a pointer to the CAN network of a Client-SDO.
Definition: csdo.c:904
static void co_csdo_send_dn_seg_req(co_csdo_t *sdo, uint32_t n, int last)
Sends a Client-SDO &#39;download segment&#39; request.
Definition: csdo.c:2189
#define CO_SDO_AC_NO_MEM
SDO abort code: Out of memory.
Definition: sdo.h:81
#define CO_SDO_SC_BLK_RES
The SDO client/client subcommand &#39;block download/upload&#39; response.
Definition: sdo.h:82
int co_sdo_req_dn_val(struct co_sdo_req *req, co_unsigned16_t type, void *val, co_unsigned32_t *pac)
Copies the next segment of the specified CANopen SDO download request to the internal buffer and...
Definition: sdo.c:165
static co_unsigned32_t co_1280_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
The download indication function for (all sub-objects of) CANopen objects 1280..12FF (SDO client para...
Definition: csdo.c:1170
void co_csdo_get_dn_ind(const co_csdo_t *sdo, co_csdo_ind_t **pind, void **pdata)
Retrieves the indication function used to notify the user of the progress of the current SDO download...
Definition: csdo.c:955
size_t nbyte
The number of bytes available at buf.
Definition: sdo.h:188
co_csdo_dn_con_t * dn_con
A pointer to the download confirmation function.
Definition: csdo.c:81
const void * buf
A pointer to the next bytes to be uploaded/downloaded.
Definition: sdo.h:186
#define CO_SDO_SEG_LAST
The mask to get/set the last segment bit from an SDO command byte.
Definition: sdo.h:142
static co_csdo_state_t * co_csdo_up_seg_on_time(co_csdo_t *sdo, const struct timespec *tp)
The &#39;timeout&#39; transition function of the &#39;upload segment&#39; state.
Definition: csdo.c:1604
uint8_t ackseq
The sequence number of the last successfully received segment.
Definition: csdo.c:75
int co_csdo_blk_dn_req(co_csdo_t *sdo, co_unsigned16_t idx, co_unsigned8_t subidx, const void *ptr, size_t n, co_csdo_dn_con_t *con, void *data)
Submits a block download request to a remote Server-SDO.
Definition: csdo.c:1099
static co_csdo_state_t *const co_csdo_blk_dn_ini_state
The &#39;block download initiate&#39; state.
Definition: csdo.c:346
co_csdo_state_t *(* on_enter)(co_csdo_t *sdo)
A pointer to the function invoked when a new state is entered.
Definition: csdo.c:165
can_recv_t * recv
A pointer to the CAN frame receiver.
Definition: csdo.c:55
#define CAN_FLAG_RTR
The Remote Transmission Request (RTR) flag (unavailable in CAN FD format frames). ...
Definition: msg.h:47
#define CO_SDO_BLK_SIZE_SET(n)
Sets the SDO size indicator, indicating n bytes contain segment data (in the range [0...
Definition: sdo.h:173
co_unsigned8_t co_sub_get_subidx(const co_sub_t *sub)
Returns the sub-index of a CANopen sub-object.
Definition: obj.c:466
#define CO_SDO_CCS_UP_SEG_REQ
The SDO client command specifier &#39;upload segment&#39; request.
Definition: sdo.h:64
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib.h> and defines any missing functionality.
co_obj_t * co_dev_find_obj(const co_dev_t *dev, co_unsigned16_t idx)
Finds an object in the object dictionary of a CANopen device.
Definition: dev.c:279
A CAN frame receiver.
Definition: net.c:99
void co_sdo_req_fini(struct co_sdo_req *req)
Finalizes a CANopen SDO upload/download request.
Definition: sdo.c:121
#define CO_SDO_SC_MASK
The mask to extract the subcommand (SC) from an SDO command byte.
Definition: sdo.h:73
static void co_csdo_send_dn_exp_req(co_csdo_t *sdo)
Sends a Client-SDO &#39;download initiate&#39; (expedited) request.
Definition: csdo.c:2156
size_t membuf_size(const struct membuf *buf)
Returns the total number of bytes written to a memory buffer.
Definition: membuf.h:156
co_unsigned16_t co_sub_get_type(const co_sub_t *sub)
Returns the data type of a CANopen sub-object.
Definition: obj.c:508
co_sub_t * co_obj_find_sub(const co_obj_t *obj, co_unsigned8_t subidx)
Finds a sub-object in a CANopen object.
Definition: obj.c:207
int co_dev_up_req(const co_dev_t *dev, co_unsigned16_t idx, co_unsigned8_t subidx, co_csdo_up_con_t *con, void *data)
Submits an upload request to a local device.
Definition: csdo.c:675
static co_csdo_state_t * co_csdo_blk_dn_ini_on_time(co_csdo_t *sdo, const struct timespec *tp)
The &#39;timeout&#39; transition function of the &#39;block download initiate&#39; state.
Definition: csdo.c:1675
#define CAN_MAX_LEN
The maximum number of bytes in the payload of a CAN format frame.
Definition: msg.h:73
co_unsigned32_t co_sub_dn_ind(co_sub_t *sub, struct co_sdo_req *req)
Invokes the download indication function of a CANopen sub-object, registered with co_sub_set_dn_ind()...
Definition: obj.c:794
co_unsigned16_t co_obj_get_idx(const co_obj_t *obj)
Returns the index of a CANopen object.
Definition: obj.c:136
#define CO_SDO_CCS_BLK_DN_REQ
The SDO client command specifier &#39;block download&#39; request.
Definition: sdo.h:67
static int co_csdo_timer(const struct timespec *tp, void *data)
The CAN timer callback function for a Client-SDO service.
Definition: csdo.c:1294
static co_csdo_state_t * co_csdo_up_seg_on_recv(co_csdo_t *sdo, const struct can_msg *msg)
The &#39;CAN frame received&#39; transition function of the &#39;upload segment&#39; state.
Definition: csdo.c:1612
#define CO_SDO_AC_BLK_SEQ
SDO abort code: Invalid sequence number (block mode only).
Definition: sdo.h:75
size_t co_val_write(co_unsigned16_t type, const void *val, uint8_t *begin, uint8_t *end)
Writes a value of the specified data type to a memory buffer.
Definition: val.c:718
static void co_csdo_send_blk_dn_ini_req(co_csdo_t *sdo)
Sends a Client-SDO &#39;block download initiate&#39; request.
Definition: csdo.c:2238
void * dn_ind_data
A pointer to user-specified data for dn_ind.
Definition: csdo.c:87
static co_csdo_state_t * co_csdo_wait_on_abort(co_csdo_t *sdo, co_unsigned32_t ac)
The &#39;abort&#39; transition function of the &#39;waiting&#39; state.
Definition: csdo.c:1353
A CANopen object.
Definition: obj.h:32
static void co_csdo_send_up_seg_req(co_csdo_t *sdo)
Sends a Client-SDO &#39;upload segment&#39; request.
Definition: csdo.c:2225
This header file is part of the CANopen library; it contains the object dictionary declarations...
co_unsigned32_t ac
The current abort code.
Definition: csdo.c:63
#define CO_SDO_SEG_TOGGLE
The mask to get/set the toggle bit from an SDO command byte.
Definition: sdo.h:117
#define CO_SDO_AC_TYPE_LEN
SDO abort code: Data type does not match, length of service parameter does not match.
Definition: sdo.h:117
static int co_csdo_dn_ind(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)
Processes a download request from a Client-SDO by checking and updating the state and copying the val...
Definition: csdo.c:2056
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
#define CO_SDO_MAX_SEQNO
The maximum sequence number (or segments per block).
Definition: sdo.h:176
uint_least32_t ldle_u32(const void *ptr)
Loads a 32-bit unsigned integer in little-endian byte order.
Definition: endian.h:541
static void co_csdo_init_ini_req(co_csdo_t *sdo, struct can_msg *msg, uint8_t cs)
Initializes a Client-SDO download/upload initiate request CAN frame.
Definition: csdo.c:2352
void co_csdo_set_up_ind(co_csdo_t *sdo, co_csdo_ind_t *ind, void *data)
Sets the indication function used to notify the user of the progress of the current SDO upload reques...
Definition: csdo.c:986
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
#define CO_SDO_REQ_INIT
The static initializer for struct co_sdo_req.
Definition: sdo.h:203
void * co_obj_addressof_val(const co_obj_t *obj)
Returns the address of the value of a CANopen object.
Definition: obj.c:275
#define CO_OBJECT_ARRAY
A multiple data field object where each data field is a simple variable of the same basic data type...
Definition: obj.h:48
uint8_t blksize
The number of segments per block.
Definition: csdo.c:73
This is the internal header file of the Service Data Object (SDO) declarations.
co_unsigned8_t co_csdo_get_num(const co_csdo_t *sdo)
Returns the SDO number of a Client-SDO.
Definition: csdo.c:920
static int co_csdo_update(co_csdo_t *sdo)
Updates and (de)activates a Client-SDO service.
Definition: csdo.c:1143
#define CO_SDO_AC_NO_OBJ
SDO abort code: Object does not exist in the object dictionary.
Definition: sdo.h:93
static co_csdo_state_t * co_csdo_dn_seg_on_recv(co_csdo_t *sdo, const struct can_msg *msg)
The &#39;CAN frame received&#39; transition function of the &#39;download segment&#39; state.
Definition: csdo.c:1494
#define CO_SDO_SEQ_LAST
The mask to get/set the last segment bit from an SDO command byte.
Definition: sdo.h:151
static co_csdo_state_t * co_csdo_blk_up_ini_on_time(co_csdo_t *sdo, const struct timespec *tp)
The &#39;timeout&#39; transition function of the &#39;block upload initiate&#39; state.
Definition: csdo.c:1860
static co_csdo_state_t * co_csdo_blk_up_end_on_time(co_csdo_t *sdo, const struct timespec *tp)
The &#39;timeout&#39; transition function of the &#39;block upload end&#39; state.
Definition: csdo.c:1990