Lely core libraries  1.9.2
ssdo.c
Go to the documentation of this file.
1 
24 #include "co.h"
25 #include "sdo.h"
26 #include <lely/co/crc.h>
27 #include <lely/co/dev.h>
28 #include <lely/co/obj.h>
29 #include <lely/co/ssdo.h>
30 #include <lely/co/val.h>
31 #include <lely/util/endian.h>
32 #include <lely/util/errnum.h>
33 
34 #include <assert.h>
35 #include <inttypes.h>
36 #include <stdlib.h>
37 
38 struct __co_ssdo_state;
40 typedef const struct __co_ssdo_state co_ssdo_state_t;
41 
43 struct __co_ssdo {
49  co_unsigned8_t num;
51  struct co_sdo_par par;
55  int timeout;
61  co_unsigned16_t idx;
63  co_unsigned8_t subidx;
65  uint8_t toggle;
67  uint8_t blksize;
69  uint8_t ackseq;
71  unsigned gencrc : 1;
73  uint16_t crc;
75  struct co_sdo_req req;
77  struct membuf buf;
79  size_t nbyte;
80 };
81 
88 static int co_ssdo_update(co_ssdo_t *sdo);
89 
96 static co_unsigned32_t co_1200_dn_ind(
97  co_sub_t *sub, struct co_sdo_req *req, void *data);
98 
104 static int co_ssdo_recv(const struct can_msg *msg, void *data);
105 
111 static int co_ssdo_timer(const struct timespec *tp, void *data);
112 
114 static inline void co_ssdo_enter(co_ssdo_t *sdo, co_ssdo_state_t *next);
115 
123 static inline void co_ssdo_emit_abort(co_ssdo_t *sdo, co_unsigned32_t ac);
124 
132 static inline void co_ssdo_emit_time(co_ssdo_t *sdo, const struct timespec *tp);
133 
141 static inline void co_ssdo_emit_recv(co_ssdo_t *sdo, const struct can_msg *msg);
142 
154  co_ssdo_state_t *(*on_abort)(co_ssdo_t *sdo, co_unsigned32_t ac);
163  co_ssdo_state_t *(*on_time)(co_ssdo_t *sdo, const struct timespec *tp);
173  co_ssdo_state_t *(*on_recv)(co_ssdo_t *sdo, const struct can_msg *msg);
174 };
175 
176 #define LELY_CO_DEFINE_STATE(name, ...) \
177  static co_ssdo_state_t *const name = &(co_ssdo_state_t){ __VA_ARGS__ };
178 
181  co_ssdo_t *sdo, co_unsigned32_t ac);
182 
185  co_ssdo_t *sdo, const struct can_msg *msg);
186 
188 // clang-format off
189 LELY_CO_DEFINE_STATE(co_ssdo_wait_state,
190  .on_abort = &co_ssdo_wait_on_abort,
191  .on_recv = &co_ssdo_wait_on_recv
192 )
193 // clang-format on
194 
195 
200  co_ssdo_t *sdo, const struct can_msg *msg);
201 
203 // LELY_CO_DEFINE_STATE(co_ssdo_dn_ini_state,
204 // .on_recv = &co_ssdo_dn_ini_on_recv
205 //)
206 
209  co_ssdo_t *sdo, co_unsigned32_t ac);
210 
213  co_ssdo_t *sdo, const struct timespec *tp);
214 
219  co_ssdo_t *sdo, const struct can_msg *msg);
220 
222 // clang-format off
223 LELY_CO_DEFINE_STATE(co_ssdo_dn_seg_state,
224  .on_abort = &co_ssdo_dn_seg_on_abort,
225  .on_time = &co_ssdo_dn_seg_on_time,
226  .on_recv = &co_ssdo_dn_seg_on_recv
227 )
228 // clang-format on
229 
232  co_ssdo_t *sdo, const struct can_msg *msg);
233 
235 // LELY_CO_DEFINE_STATE(co_ssdo_up_ini_state,
236 // .on_recv = &co_ssdo_up_ini_on_recv
237 //)
238 
241  co_ssdo_t *sdo, co_unsigned32_t ac);
242 
245  co_ssdo_t *sdo, const struct timespec *tp);
246 
249  co_ssdo_t *sdo, const struct can_msg *msg);
250 
252 // clang-format off
253 LELY_CO_DEFINE_STATE(co_ssdo_up_seg_state,
254  .on_abort = &co_ssdo_up_seg_on_abort,
255  .on_time = &co_ssdo_up_seg_on_time,
256  .on_recv = &co_ssdo_up_seg_on_recv
257 )
258 // clang-format on
259 
265  co_ssdo_t *sdo, const struct can_msg *msg);
266 
268 // LELY_CO_DEFINE_STATE(co_ssdo_blk_dn_ini_state,
269 // .on_recv = &co_ssdo_blk_dn_ini_on_recv
270 //)
271 
274  co_ssdo_t *sdo, co_unsigned32_t ac);
275 
278  co_ssdo_t *sdo, const struct timespec *tp);
279 
285  co_ssdo_t *sdo, const struct can_msg *msg);
286 
288 // clang-format off
289 LELY_CO_DEFINE_STATE(co_ssdo_blk_dn_sub_state,
290  .on_abort = &co_ssdo_blk_dn_sub_on_abort,
291  .on_time = &co_ssdo_blk_dn_sub_on_time,
292  .on_recv = &co_ssdo_blk_dn_sub_on_recv
293 )
294 // clang-format on
295 
298  co_ssdo_t *sdo, co_unsigned32_t ac);
299 
302  co_ssdo_t *sdo, const struct timespec *tp);
303 
309  co_ssdo_t *sdo, const struct can_msg *msg);
310 
312 // clang-format off
313 LELY_CO_DEFINE_STATE(co_ssdo_blk_dn_end_state,
314  .on_abort = &co_ssdo_blk_dn_end_on_abort,
315  .on_time = &co_ssdo_blk_dn_end_on_time,
316  .on_recv = &co_ssdo_blk_dn_end_on_recv
317 )
318 // clang-format on
319 
325  co_ssdo_t *sdo, const struct can_msg *msg);
326 
328 // LELY_CO_DEFINE_STATE(co_ssdo_blk_up_ini_state,
329 // .on_recv = &co_ssdo_blk_up_ini_on_recv
330 //)
331 
334  co_ssdo_t *sdo, co_unsigned32_t ac);
335 
338  co_ssdo_t *sdo, const struct timespec *tp);
339 
345  co_ssdo_t *sdo, const struct can_msg *msg);
346 
348 // clang-format off
349 LELY_CO_DEFINE_STATE(co_ssdo_blk_up_sub_state,
350  .on_abort = &co_ssdo_blk_up_sub_on_abort,
351  .on_time = &co_ssdo_blk_up_sub_on_time,
352  .on_recv = &co_ssdo_blk_up_sub_on_recv
353 )
354 // clang-format on
355 
358  co_ssdo_t *sdo, co_unsigned32_t ac);
359 
362  co_ssdo_t *sdo, const struct timespec *tp);
363 
368  co_ssdo_t *sdo, const struct can_msg *msg);
369 
371 // clang-format off
372 LELY_CO_DEFINE_STATE(co_ssdo_blk_up_end_state,
373  .on_abort = &co_ssdo_blk_up_end_on_abort,
374  .on_time = &co_ssdo_blk_up_end_on_time,
375  .on_recv = &co_ssdo_blk_up_end_on_recv
376 )
377 // clang-format on
378 
379 #undef LELY_CO_DEFINE_STATE
380 
388 
400 static co_ssdo_state_t *co_ssdo_abort_res(co_ssdo_t *sdo, co_unsigned32_t ac);
401 
408 static co_unsigned32_t co_ssdo_dn_ind(co_ssdo_t *sdo);
409 
416 static co_unsigned32_t co_ssdo_up_ind(co_ssdo_t *sdo);
417 
424 static co_unsigned32_t co_ssdo_up_buf(co_ssdo_t *sdo, size_t nbyte);
425 
432 static void co_ssdo_send_abort(co_ssdo_t *sdo, co_unsigned32_t ac);
433 
435 static void co_ssdo_send_dn_ini_res(co_ssdo_t *sdo);
436 
438 static void co_ssdo_send_dn_seg_res(co_ssdo_t *sdo);
439 
441 static void co_ssdo_send_up_exp_res(co_ssdo_t *sdo);
442 
444 static void co_ssdo_send_up_ini_res(co_ssdo_t *sdo);
445 
452 static void co_ssdo_send_up_seg_res(co_ssdo_t *sdo, int last);
453 
455 static void co_ssdo_send_blk_dn_ini_res(co_ssdo_t *sdo);
456 
458 static void co_ssdo_send_blk_dn_sub_res(co_ssdo_t *sdo);
459 
461 static void co_ssdo_send_blk_dn_end_res(co_ssdo_t *sdo);
462 
464 static void co_ssdo_send_blk_up_ini_res(co_ssdo_t *sdo);
465 
472 static void co_ssdo_send_blk_up_sub_res(co_ssdo_t *sdo, int last);
473 
475 static void co_ssdo_send_blk_up_end_res(co_ssdo_t *sdo);
476 
484 static void co_ssdo_init_ini_res(
485  co_ssdo_t *sdo, struct can_msg *msg, uint8_t cs);
486 
494 static void co_ssdo_init_seg_res(
495  co_ssdo_t *sdo, struct can_msg *msg, uint8_t cs);
496 
497 void *
498 __co_ssdo_alloc(void)
499 {
500  void *ptr = malloc(sizeof(struct __co_ssdo));
501  if (__unlikely(!ptr))
502  set_errc(errno2c(errno));
503  return ptr;
504 }
505 
506 void
507 __co_ssdo_free(void *ptr)
508 {
509  free(ptr);
510 }
511 
512 struct __co_ssdo *
513 __co_ssdo_init(struct __co_ssdo *sdo, can_net_t *net, co_dev_t *dev,
514  co_unsigned8_t num)
515 {
516  assert(sdo);
517  assert(net);
518  assert(dev);
519 
520  int errc = 0;
521 
522  if (__unlikely(!num || num > 128)) {
523  errc = errnum2c(ERRNUM_INVAL);
524  goto error_param;
525  }
526 
527  // Find the SDO server parameter in the object dictionary. The default
528  // SDO (1200) is optional.
529  co_obj_t *obj_1200 = co_dev_find_obj(dev, 0x1200 + num - 1);
530  if (__unlikely(num != 1 && !obj_1200)) {
531  errc = errnum2c(ERRNUM_INVAL);
532  goto error_param;
533  }
534 
535  sdo->net = net;
536  sdo->dev = dev;
537  sdo->num = num;
538 
539  // Initialize the SDO parameter record with the default values.
540  sdo->par.n = 3;
541  sdo->par.id = co_dev_get_id(dev);
542  sdo->par.cobid_req = 0x600 + sdo->par.id;
543  sdo->par.cobid_res = 0x580 + sdo->par.id;
544 
545  if (obj_1200) {
546  // Copy the SDO parameter record.
547  size_t size = co_obj_sizeof_val(obj_1200);
548  memcpy(&sdo->par, co_obj_addressof_val(obj_1200),
549  MIN(size, sizeof(sdo->par)));
550  }
551 
552  sdo->recv = can_recv_create();
553  if (__unlikely(!sdo->recv)) {
554  errc = get_errc();
555  goto error_create_recv;
556  }
557  can_recv_set_func(sdo->recv, &co_ssdo_recv, sdo);
558 
559  sdo->timeout = 0;
560 
561  sdo->timer = can_timer_create();
562  if (__unlikely(!sdo->timer)) {
563  errc = get_errc();
564  goto error_create_timer;
565  }
567 
568  sdo->state = co_ssdo_wait_state;
569 
570  sdo->idx = 0;
571  sdo->subidx = 0;
572 
573  sdo->toggle = 0;
574  sdo->blksize = 0;
575  sdo->ackseq = 0;
576  sdo->gencrc = 0;
577  sdo->crc = 0;
578 
579  co_sdo_req_init(&sdo->req);
580  membuf_init(&sdo->buf);
581  sdo->nbyte = 0;
582 
583  // Set the download indication function for the SDO parameter record.
584  if (obj_1200)
585  co_obj_set_dn_ind(obj_1200, &co_1200_dn_ind, sdo);
586 
587  if (__unlikely(co_ssdo_update(sdo) == -1)) {
588  errc = get_errc();
589  goto error_update;
590  }
591 
592  return sdo;
593 
594 error_update:
595  if (obj_1200)
596  co_obj_set_dn_ind(obj_1200, NULL, NULL);
597  can_timer_destroy(sdo->timer);
598 error_create_timer:
599  can_recv_destroy(sdo->recv);
600 error_create_recv:
601 error_param:
602  set_errc(errc);
603  return NULL;
604 }
605 
606 void
607 __co_ssdo_fini(struct __co_ssdo *sdo)
608 {
609  assert(sdo);
610  assert(sdo->num >= 1 && sdo->num <= 128);
611 
612  // Remove the download indication functions for the SDO parameter
613  // record.
614  co_obj_t *obj_1200 = co_dev_find_obj(sdo->dev, 0x1200 + sdo->num - 1);
615  if (obj_1200)
616  co_obj_set_dn_ind(obj_1200, NULL, NULL);
617 
618  // Abort any ongoing transfer.
620 
621  membuf_fini(&sdo->buf);
622  co_sdo_req_fini(&sdo->req);
623 
624  can_timer_destroy(sdo->timer);
625 
626  can_recv_destroy(sdo->recv);
627 }
628 
629 co_ssdo_t *
631 {
632  trace("creating Server-SDO %d", num);
633 
634  int errc = 0;
635 
636  co_ssdo_t *sdo = __co_ssdo_alloc();
637  if (__unlikely(!sdo)) {
638  errc = get_errc();
639  goto error_alloc_sdo;
640  }
641 
642  if (__unlikely(!__co_ssdo_init(sdo, net, dev, num))) {
643  errc = get_errc();
644  goto error_init_sdo;
645  }
646 
647  return sdo;
648 
649 error_init_sdo:
650  __co_ssdo_free(sdo);
651 error_alloc_sdo:
652  set_errc(errc);
653  return NULL;
654 }
655 
656 void
658 {
659  if (ssdo) {
660  trace("destroying Server-SDO %d", ssdo->num);
661  __co_ssdo_fini(ssdo);
662  __co_ssdo_free(ssdo);
663  }
664 }
665 
666 can_net_t *
668 {
669  assert(sdo);
670 
671  return sdo->net;
672 }
673 
674 co_dev_t *
676 {
677  assert(sdo);
678 
679  return sdo->dev;
680 }
681 
682 co_unsigned8_t
684 {
685  assert(sdo);
686 
687  return sdo->num;
688 }
689 
690 const struct co_sdo_par *
692 {
693  assert(sdo);
694 
695  return &sdo->par;
696 }
697 
698 int
700 {
701  assert(sdo);
702 
703  return sdo->timeout;
704 }
705 
706 void
707 co_ssdo_set_timeout(co_ssdo_t *sdo, int timeout)
708 {
709  assert(sdo);
710 
711  if (sdo->timeout && timeout <= 0)
712  can_timer_stop(sdo->timer);
713 
714  sdo->timeout = MAX(0, timeout);
715 }
716 
717 static int
719 {
720  assert(sdo);
721 
722  // Abort any ongoing transfer.
724 
725  int valid_req = !(sdo->par.cobid_req & CO_SDO_COBID_VALID);
726  int valid_res = !(sdo->par.cobid_res & CO_SDO_COBID_VALID);
727  if (valid_req && valid_res) {
728  uint32_t id = sdo->par.cobid_req;
729  uint8_t flags = 0;
730  if (id & CO_SDO_COBID_FRAME) {
731  id &= CAN_MASK_EID;
732  flags |= CAN_FLAG_IDE;
733  } else {
734  id &= CAN_MASK_BID;
735  }
736  can_recv_start(sdo->recv, sdo->net, id, flags);
737  } else {
738  can_recv_stop(sdo->recv);
739  }
740 
741  return 0;
742 }
743 
744 static co_unsigned32_t
745 co_1200_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
746 {
747  assert(sub);
748  assert(req);
749  co_ssdo_t *sdo = data;
750  assert(sdo);
751  assert(co_obj_get_idx(co_sub_get_obj(sub)) == 0x1200 + sdo->num - 1);
752 
753  co_unsigned32_t ac = 0;
754 
755  co_unsigned16_t type = co_sub_get_type(sub);
756  union co_val val;
757  if (__unlikely(co_sdo_req_dn_val(req, type, &val, &ac) == -1))
758  return ac;
759 
760  switch (co_sub_get_subidx(sub)) {
761  case 0: ac = CO_SDO_AC_NO_WRITE; goto error;
762  case 1: {
763  assert(type == CO_DEFTYPE_UNSIGNED32);
764  co_unsigned32_t cobid = val.u32;
765  co_unsigned32_t cobid_old = co_sub_get_val_u32(sub);
766  if (cobid == cobid_old)
767  goto error;
768 
769  // The CAN-ID cannot be changed when the SDO is and remains
770  // valid.
771  int valid = !(cobid & CO_SDO_COBID_VALID);
772  int valid_old = !(cobid_old & CO_SDO_COBID_VALID);
773  uint32_t canid = cobid & CAN_MASK_EID;
774  uint32_t canid_old = cobid_old & CAN_MASK_EID;
775  if (__unlikely(valid && valid_old && canid != canid_old)) {
776  ac = CO_SDO_AC_PARAM_VAL;
777  goto error;
778  }
779 
780  // A 29-bit CAN-ID is only valid if the frame bit is set.
781  // clang-format off
782  if (__unlikely(!(cobid & CO_SDO_COBID_FRAME)
783  && (cobid & (CAN_MASK_EID ^ CAN_MASK_BID)))) {
784  // clang-format on
785  ac = CO_SDO_AC_PARAM_VAL;
786  goto error;
787  }
788 
789  sdo->par.cobid_req = cobid;
790  break;
791  }
792  case 2: {
793  assert(type == CO_DEFTYPE_UNSIGNED32);
794  co_unsigned32_t cobid = val.u32;
795  co_unsigned32_t cobid_old = co_sub_get_val_u32(sub);
796  if (cobid == cobid_old)
797  goto error;
798 
799  // The CAN-ID cannot be changed when the SDO is and remains
800  // valid.
801  int valid = !(cobid & CO_SDO_COBID_VALID);
802  int valid_old = !(cobid_old & CO_SDO_COBID_VALID);
803  uint32_t canid = cobid & CAN_MASK_EID;
804  uint32_t canid_old = cobid_old & CAN_MASK_EID;
805  if (__unlikely(valid && valid_old && canid != canid_old)) {
806  ac = CO_SDO_AC_PARAM_VAL;
807  goto error;
808  }
809 
810  // A 29-bit CAN-ID is only valid if the frame bit is set.
811  // clang-format off
812  if (__unlikely(!(cobid & CO_SDO_COBID_FRAME)
813  && (cobid & (CAN_MASK_EID ^ CAN_MASK_BID)))) {
814  // clang-format on
815  ac = CO_SDO_AC_PARAM_VAL;
816  goto error;
817  }
818 
819  sdo->par.cobid_res = cobid;
820  break;
821  }
822  case 3: {
823  assert(type == CO_DEFTYPE_UNSIGNED8);
824  co_unsigned8_t id = val.u8;
825  co_unsigned8_t id_old = co_sub_get_val_u8(sub);
826  if (id == id_old)
827  goto error;
828 
829  sdo->par.id = id;
830  break;
831  }
832  default: ac = CO_SDO_AC_NO_SUB; goto error;
833  }
834 
835  co_sub_dn(sub, &val);
836  co_val_fini(type, &val);
837 
838  co_ssdo_update(sdo);
839  return 0;
840 
841 error:
842  co_val_fini(type, &val);
843  return ac;
844 }
845 
846 static int
847 co_ssdo_recv(const struct can_msg *msg, void *data)
848 {
849  assert(msg);
850  co_ssdo_t *sdo = data;
851  assert(sdo);
852 
853  // Ignore remote frames.
854  if (__unlikely(msg->flags & CAN_FLAG_RTR))
855  return 0;
856 
857 #ifndef LELY_NO_CANFD
858  // Ignore CAN FD format frames.
859  if (__unlikely(msg->flags & CAN_FLAG_EDL))
860  return 0;
861 #endif
862 
863  co_ssdo_emit_recv(sdo, msg);
864 
865  return 0;
866 }
867 
868 static int
869 co_ssdo_timer(const struct timespec *tp, void *data)
870 {
871  assert(tp);
872  co_ssdo_t *sdo = data;
873  assert(sdo);
874 
875  co_ssdo_emit_time(sdo, tp);
876 
877  return 0;
878 }
879 
880 static inline void
882 {
883  assert(sdo);
884 
885  if (next)
886  sdo->state = next;
887 }
888 
889 static inline void
890 co_ssdo_emit_abort(co_ssdo_t *sdo, co_unsigned32_t ac)
891 {
892  assert(sdo);
893  assert(sdo->state);
894  assert(sdo->state->on_recv);
895 
896  co_ssdo_enter(sdo, sdo->state->on_abort(sdo, ac));
897 }
898 
899 static inline void
900 co_ssdo_emit_time(co_ssdo_t *sdo, const struct timespec *tp)
901 {
902  assert(sdo);
903  assert(sdo->state);
904  assert(sdo->state->on_time);
905 
906  co_ssdo_enter(sdo, sdo->state->on_time(sdo, tp));
907 }
908 
909 static inline void
910 co_ssdo_emit_recv(co_ssdo_t *sdo, const struct can_msg *msg)
911 {
912  assert(sdo);
913  assert(sdo->state);
914  assert(sdo->state->on_recv);
915 
916  co_ssdo_enter(sdo, sdo->state->on_recv(sdo, msg));
917 }
918 
919 static co_ssdo_state_t *
920 co_ssdo_wait_on_abort(co_ssdo_t *sdo, co_unsigned32_t ac)
921 {
922  (void)sdo;
923  (void)ac;
924 
925  return NULL;
926 }
927 
928 static co_ssdo_state_t *
929 co_ssdo_wait_on_recv(co_ssdo_t *sdo, const struct can_msg *msg)
930 {
931  assert(sdo);
932  assert(msg);
933 
934  if (__unlikely(msg->len < 1))
935  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_CS);
936  uint8_t cs = msg->data[0];
937 
938  switch (cs & CO_SDO_CS_MASK) {
939  case CO_SDO_CCS_DN_INI_REQ: return co_ssdo_dn_ini_on_recv(sdo, msg);
940  case CO_SDO_CCS_UP_INI_REQ: return co_ssdo_up_ini_on_recv(sdo, msg);
941  case CO_SDO_CCS_BLK_DN_REQ: return co_ssdo_blk_dn_ini_on_recv(sdo, msg);
942  case CO_SDO_CCS_BLK_UP_REQ: return co_ssdo_blk_up_ini_on_recv(sdo, msg);
943  case CO_SDO_CS_ABORT: return NULL;
944  default: return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_CS);
945  }
946 }
947 
948 static co_ssdo_state_t *
949 co_ssdo_dn_ini_on_recv(co_ssdo_t *sdo, const struct can_msg *msg)
950 {
951  assert(sdo);
952  assert(msg);
953 
954  assert(msg->len > 0);
955  uint8_t cs = msg->data[0];
956 
957  // Load the object index and sub-index from the CAN frame.
958  if (__unlikely(msg->len < 3))
959  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_OBJ);
960  sdo->idx = ldle_u16(msg->data + 1);
961  if (__unlikely(msg->len < 4))
962  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_SUB);
963  sdo->subidx = msg->data[3];
964 
965  trace("SSDO: %04X:%02X: received download request", sdo->idx,
966  sdo->subidx);
967 
968  // Obtain the size from the command specifier.
969  co_sdo_req_clear(&sdo->req);
970  int exp = !!(cs & CO_SDO_INI_SIZE_EXP);
971  if (exp) {
972  if (cs & CO_SDO_INI_SIZE_IND)
973  sdo->req.size = CO_SDO_INI_SIZE_EXP_GET(cs);
974  else
975  sdo->req.size = msg->len - 4;
976  } else if (cs & CO_SDO_INI_SIZE_IND) {
977  if (__unlikely(msg->len < 8))
978  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_CS);
979  sdo->req.size = ldle_u32(msg->data + 4);
980  }
981 
982  if (exp) {
983  // Perform an expedited transfer.
984  sdo->req.buf = msg->data + 4;
985  sdo->req.nbyte = sdo->req.size;
986  co_unsigned32_t ac = co_ssdo_dn_ind(sdo);
987  if (__unlikely(ac))
988  return co_ssdo_abort_res(sdo, ac);
989  // Finalize the transfer.
991  return co_ssdo_abort_ind(sdo);
992  } else {
994  if (sdo->timeout)
995  can_timer_timeout(sdo->timer, sdo->net, sdo->timeout);
996  return co_ssdo_dn_seg_state;
997  }
998 }
999 
1000 static co_ssdo_state_t *
1001 co_ssdo_dn_seg_on_abort(co_ssdo_t *sdo, co_unsigned32_t ac)
1002 {
1003  return co_ssdo_abort_res(sdo, ac);
1004 }
1005 
1006 static co_ssdo_state_t *
1007 co_ssdo_dn_seg_on_time(co_ssdo_t *sdo, const struct timespec *tp)
1008 {
1009  (void)tp;
1010 
1011  return co_ssdo_abort_res(sdo, CO_SDO_AC_TIMEOUT);
1012 }
1013 
1014 static co_ssdo_state_t *
1015 co_ssdo_dn_seg_on_recv(co_ssdo_t *sdo, const struct can_msg *msg)
1016 {
1017  assert(sdo);
1018  assert(msg);
1019 
1020  if (__unlikely(msg->len < 1))
1021  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1022  uint8_t cs = msg->data[0];
1023 
1024  // Check the client command specifier.
1025  switch (cs & CO_SDO_CS_MASK) {
1026  case CO_SDO_CCS_DN_SEG_REQ: break;
1027  case CO_SDO_CS_ABORT: return co_ssdo_abort_ind(sdo);
1028  default: return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1029  }
1030 
1031  // Check the value of the toggle bit.
1032  if (__unlikely((cs & CO_SDO_SEG_TOGGLE) != sdo->toggle))
1033  return co_ssdo_dn_seg_state;
1034 
1035  // Obtain the size of the segment.
1036  size_t n = CO_SDO_SEG_SIZE_GET(cs);
1037  if (__unlikely(msg->len < 1 + n))
1038  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1039  int last = !!(cs & CO_SDO_SEG_LAST);
1040 
1041  sdo->req.buf = msg->data + 1;
1042  sdo->req.offset += sdo->req.nbyte;
1043  sdo->req.nbyte = n;
1044 
1045  if (__unlikely(last && !co_sdo_req_last(&sdo->req)))
1047 
1048  co_unsigned32_t ac = co_ssdo_dn_ind(sdo);
1049  if (__unlikely(ac))
1050  return co_ssdo_abort_res(sdo, ac);
1051 
1053 
1054  if (last) {
1055  return co_ssdo_abort_ind(sdo);
1056  } else {
1057  if (sdo->timeout)
1058  can_timer_timeout(sdo->timer, sdo->net, sdo->timeout);
1059  return co_ssdo_dn_seg_state;
1060  }
1061 }
1062 
1063 static co_ssdo_state_t *
1064 co_ssdo_up_ini_on_recv(co_ssdo_t *sdo, const struct can_msg *msg)
1065 {
1066  assert(sdo);
1067  assert(msg);
1068 
1069  // Load the object index and sub-index from the CAN frame.
1070  if (__unlikely(msg->len < 3))
1071  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_OBJ);
1072  sdo->idx = ldle_u16(msg->data + 1);
1073  if (__unlikely(msg->len < 4))
1074  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_SUB);
1075  sdo->subidx = msg->data[3];
1076 
1077  trace("SSDO: %04X:%02X: received upload request", sdo->idx,
1078  sdo->subidx);
1079 
1080  // Perform access checks and start serializing the value.
1081  co_sdo_req_clear(&sdo->req);
1082  co_unsigned32_t ac = co_ssdo_up_ind(sdo);
1083  if (__unlikely(ac))
1084  return co_ssdo_abort_res(sdo, ac);
1085 
1086  if (sdo->req.size && sdo->req.size <= 4) {
1087  // Perform an expedited transfer.
1088  if (__unlikely((ac = co_ssdo_up_buf(sdo, sdo->req.size)) != 0))
1089  return co_ssdo_abort_res(sdo, ac);
1091  return co_ssdo_abort_ind(sdo);
1092  } else {
1094  if (sdo->timeout)
1095  can_timer_timeout(sdo->timer, sdo->net, sdo->timeout);
1096  return co_ssdo_up_seg_state;
1097  }
1098 }
1099 
1100 static co_ssdo_state_t *
1101 co_ssdo_up_seg_on_abort(co_ssdo_t *sdo, co_unsigned32_t ac)
1102 {
1103  return co_ssdo_abort_res(sdo, ac);
1104 }
1105 
1106 static co_ssdo_state_t *
1107 co_ssdo_up_seg_on_time(co_ssdo_t *sdo, const struct timespec *tp)
1108 {
1109  (void)tp;
1110 
1111  return co_ssdo_abort_res(sdo, CO_SDO_AC_TIMEOUT);
1112 }
1113 
1114 static co_ssdo_state_t *
1115 co_ssdo_up_seg_on_recv(co_ssdo_t *sdo, const struct can_msg *msg)
1116 {
1117  assert(sdo);
1118  assert(msg);
1119 
1120  if (__unlikely(msg->len < 1))
1121  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1122  uint8_t cs = msg->data[0];
1123 
1124  // Check the client command specifier.
1125  switch (cs & CO_SDO_CS_MASK) {
1126  case CO_SDO_CCS_UP_SEG_REQ: break;
1127  case CO_SDO_CS_ABORT: return co_ssdo_abort_ind(sdo);
1128  default: return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1129  }
1130 
1131  // Check the value of the toggle bit.
1132  if (__unlikely((cs & CO_SDO_SEG_TOGGLE) != sdo->toggle))
1133  return co_ssdo_abort_res(sdo, CO_SDO_AC_TOGGLE);
1134 
1135  membuf_clear(&sdo->buf);
1136  co_unsigned32_t ac = co_ssdo_up_buf(sdo, 7);
1137  if (__unlikely(ac))
1138  return co_ssdo_abort_res(sdo, ac);
1139 
1140  int last = co_sdo_req_last(&sdo->req) && sdo->nbyte == sdo->req.nbyte;
1141  co_ssdo_send_up_seg_res(sdo, last);
1142 
1143  if (last) {
1144  // Finalize the transfer.
1145  return co_ssdo_abort_ind(sdo);
1146  } else {
1147  if (sdo->timeout)
1148  can_timer_timeout(sdo->timer, sdo->net, sdo->timeout);
1149  return co_ssdo_up_seg_state;
1150  }
1151 }
1152 
1153 static co_ssdo_state_t *
1155 {
1156  assert(sdo);
1157  assert(msg);
1158 
1159  assert(msg->len > 0);
1160  uint8_t cs = msg->data[0];
1161 
1162  // Check the client subcommand.
1163  if (__unlikely((cs & 0x01) != CO_SDO_SC_INI_BLK))
1164  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1165 
1166  // Check if the client supports generating a CRC.
1167  sdo->gencrc = !!(cs & CO_SDO_BLK_CRC);
1168 
1169  // Load the object index and sub-index from the CAN frame.
1170  if (__unlikely(msg->len < 3))
1171  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_OBJ);
1172  sdo->idx = ldle_u16(msg->data + 1);
1173  if (__unlikely(msg->len < 4))
1174  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_SUB);
1175  sdo->subidx = msg->data[3];
1176 
1177  trace("SSDO: %04X:%02X: received block download request", sdo->idx,
1178  sdo->subidx);
1179 
1180  // Obtain the data set size.
1181  co_sdo_req_clear(&sdo->req);
1182  if (cs & CO_SDO_BLK_SIZE_IND) {
1183  if (__unlikely(msg->len < 8))
1184  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1185  sdo->req.size = ldle_u32(msg->data + 4);
1186  }
1187 
1188  // Use the maximum block size by default.
1189  sdo->blksize = CO_SDO_MAX_SEQNO;
1190  sdo->ackseq = 0;
1191 
1193 
1194  if (sdo->timeout)
1195  can_timer_timeout(sdo->timer, sdo->net, sdo->timeout);
1196  return co_ssdo_blk_dn_sub_state;
1197 }
1198 
1199 static co_ssdo_state_t *
1200 co_ssdo_blk_dn_sub_on_abort(co_ssdo_t *sdo, co_unsigned32_t ac)
1201 {
1202  return co_ssdo_abort_res(sdo, ac);
1203 }
1204 
1205 static co_ssdo_state_t *
1206 co_ssdo_blk_dn_sub_on_time(co_ssdo_t *sdo, const struct timespec *tp)
1207 {
1208  (void)tp;
1209 
1210  return co_ssdo_abort_res(sdo, CO_SDO_AC_TIMEOUT);
1211 }
1212 
1213 static co_ssdo_state_t *
1215 {
1216  assert(sdo);
1217  assert(msg);
1218 
1219  if (__unlikely(msg->len < 1))
1220  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1221  uint8_t cs = msg->data[0];
1222 
1223  if (__unlikely(cs == CO_SDO_CS_ABORT))
1224  return co_ssdo_abort_ind(sdo);
1225 
1226  uint8_t seqno = cs & ~CO_SDO_SEQ_LAST;
1227  int last = !!(cs & CO_SDO_SEQ_LAST);
1228 
1229  if (__unlikely(!seqno || seqno > sdo->blksize))
1230  return co_ssdo_abort_res(sdo, CO_SDO_AC_BLK_SEQ);
1231 
1232  // Only accept sequential segments. Dropped segments will be resent
1233  // after the confirmation message.
1234  if (seqno == sdo->ackseq + 1) {
1235  sdo->ackseq++;
1236  // Update the CRC.
1237  if (sdo->gencrc)
1238  sdo->crc = co_crc(
1239  sdo->crc, sdo->req.buf, sdo->req.nbyte);
1240  // Pass the previous frame to the download indication function.
1241  co_unsigned32_t ac = co_ssdo_dn_ind(sdo);
1242  if (__unlikely(ac))
1243  return co_ssdo_abort_res(sdo, ac);
1244  // Copy the new frame to the SDO request.
1245  membuf_clear(&sdo->buf);
1246  if (__unlikely(!membuf_reserve(&sdo->buf, 7)))
1247  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_MEM);
1248  membuf_write(&sdo->buf, msg->data + 1, 7);
1249  sdo->req.buf = membuf_begin(&sdo->buf);
1250  sdo->req.offset += sdo->req.nbyte;
1251  sdo->req.nbyte = membuf_size(&sdo->buf);
1252  }
1253 
1254  // If this is the last segment in the block, send a confirmation.
1255  if (seqno == sdo->blksize || last) {
1257  sdo->ackseq = 0;
1258  }
1259 
1260  if (sdo->timeout)
1261  can_timer_timeout(sdo->timer, sdo->net, sdo->timeout);
1263 }
1264 
1265 static co_ssdo_state_t *
1266 co_ssdo_blk_dn_end_on_abort(co_ssdo_t *sdo, co_unsigned32_t ac)
1267 {
1268  return co_ssdo_abort_res(sdo, ac);
1269 }
1270 
1271 static co_ssdo_state_t *
1272 co_ssdo_blk_dn_end_on_time(co_ssdo_t *sdo, const struct timespec *tp)
1273 {
1274  (void)tp;
1275 
1276  return co_ssdo_abort_res(sdo, CO_SDO_AC_TIMEOUT);
1277 }
1278 
1279 static co_ssdo_state_t *
1281 {
1282  assert(sdo);
1283  assert(msg);
1284 
1285  if (__unlikely(msg->len < 1))
1286  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1287  uint8_t cs = msg->data[0];
1288 
1289  // Check the client command specifier.
1290  switch (cs & CO_SDO_CS_MASK) {
1291  case CO_SDO_CCS_BLK_DN_REQ: break;
1292  case CO_SDO_CS_ABORT: return co_ssdo_abort_ind(sdo);
1293  default: return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1294  }
1295 
1296  // Check the client subcommand.
1298  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1299 
1300  // Discard the bytes in the last segment that did not contain data.
1301  sdo->req.nbyte -= 7 - CO_SDO_BLK_SIZE_GET(cs);
1302 
1303  // Check the CRC.
1304  if (sdo->gencrc) {
1305  sdo->crc = co_crc(sdo->crc, sdo->req.buf, sdo->req.nbyte);
1306  uint16_t crc = ldle_u16(msg->data + 1);
1307  if (__unlikely(sdo->crc != crc))
1308  return co_ssdo_abort_res(sdo, CO_SDO_AC_BLK_CRC);
1309  }
1310 
1311  co_unsigned32_t ac = co_ssdo_dn_ind(sdo);
1312  if (__unlikely(ac))
1313  return co_ssdo_abort_res(sdo, ac);
1314 
1315  // Finalize the transfer.
1317  return co_ssdo_abort_ind(sdo);
1318 }
1319 
1320 static co_ssdo_state_t *
1322 {
1323  assert(sdo);
1324  assert(msg);
1325 
1326  assert(msg->len > 0);
1327  uint8_t cs = msg->data[0];
1328 
1329  // Check the client subcommand.
1331  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1332 
1333  // Check if the client supports generating a CRC.
1334  sdo->gencrc = !!(cs & CO_SDO_BLK_CRC);
1335 
1336  // Load the object index and sub-index from the CAN frame.
1337  if (__unlikely(msg->len < 3))
1338  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_OBJ);
1339  sdo->idx = ldle_u16(msg->data + 1);
1340  if (__unlikely(msg->len < 4))
1341  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_SUB);
1342  sdo->subidx = msg->data[3];
1343 
1344  trace("SSDO: %04X:%02X: received block upload request", sdo->idx,
1345  sdo->subidx);
1346 
1347  // Load the number of segments per block.
1348  if (__unlikely(msg->len < 5))
1350  sdo->blksize = msg->data[4];
1351  if (__unlikely(!sdo->blksize || sdo->blksize > CO_SDO_MAX_SEQNO))
1353 
1354  // Load the protocol switch threshold (PST).
1355  uint8_t pst = msg->len > 5 ? msg->data[5] : 0;
1356 
1357  // Perform access checks and start serializing the value.
1358  co_sdo_req_clear(&sdo->req);
1359  co_unsigned32_t ac = co_ssdo_up_ind(sdo);
1360  if (__unlikely(ac))
1361  return co_ssdo_abort_res(sdo, ac);
1362 
1363  if (pst && sdo->req.size <= pst) {
1364  // If the PST is non-zero, and the number of bytes is smaller
1365  // than or equal to the PST, switch to the SDO upload protocol.
1366  if (sdo->req.size <= 4) {
1367  // Perform an expedited transfer.
1368  // clang-format off
1369  if (__unlikely((ac = co_ssdo_up_buf(sdo, sdo->req.size))
1370  != 0))
1371  // clang-format on
1372  return co_ssdo_abort_res(sdo, ac);
1374  return co_ssdo_abort_ind(sdo);
1375  } else {
1377  if (sdo->timeout)
1378  can_timer_timeout(sdo->timer, sdo->net,
1379  sdo->timeout);
1380  return co_ssdo_up_seg_state;
1381  }
1382  } else {
1384  if (sdo->timeout)
1385  can_timer_timeout(sdo->timer, sdo->net, sdo->timeout);
1386  return co_ssdo_blk_up_sub_state;
1387  }
1388 }
1389 
1390 static co_ssdo_state_t *
1391 co_ssdo_blk_up_sub_on_abort(co_ssdo_t *sdo, co_unsigned32_t ac)
1392 {
1393  return co_ssdo_abort_res(sdo, ac);
1394 }
1395 
1396 static co_ssdo_state_t *
1397 co_ssdo_blk_up_sub_on_time(co_ssdo_t *sdo, const struct timespec *tp)
1398 {
1399  (void)tp;
1400 
1401  return co_ssdo_abort_res(sdo, CO_SDO_AC_TIMEOUT);
1402 }
1403 
1404 static co_ssdo_state_t *
1406 {
1407  assert(sdo);
1408  assert(msg);
1409 
1410  if (__unlikely(msg->len < 1))
1411  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1412  uint8_t cs = msg->data[0];
1413 
1414  // Check the client command specifier.
1415  switch (cs & CO_SDO_CS_MASK) {
1416  case CO_SDO_CCS_BLK_UP_REQ: break;
1417  case CO_SDO_CS_ABORT: return co_ssdo_abort_ind(sdo);
1418  default: return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1419  }
1420 
1421  // Check the client subcommand.
1422  switch (cs & CO_SDO_SC_MASK) {
1423  case CO_SDO_SC_BLK_RES:
1424  if (__unlikely(co_sdo_req_first(&sdo->req) && !sdo->nbyte))
1425  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1426 
1427  if (__unlikely(msg->len < 3))
1428  return co_ssdo_abort_res(sdo, CO_SDO_AC_BLK_SEQ);
1429 
1430  // Flush the successfully sent segments from the buffer.
1431  uint8_t ackseq = msg->data[1];
1432  membuf_flush(&sdo->buf, ackseq * 7);
1433 
1434  // Read the number of segments in the next block.
1435  sdo->blksize = msg->data[2];
1436  // clang-format off
1437  if (__unlikely(!sdo->blksize
1438  || sdo->blksize > CO_SDO_MAX_SEQNO))
1439  // clang-format on
1441 
1442  break;
1443  case CO_SDO_SC_START_UP:
1444  if (__unlikely(!(co_sdo_req_first(&sdo->req) && !sdo->nbyte)))
1445  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1446  break;
1447  default: return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1448  }
1449 
1450  ptrdiff_t n = sdo->blksize * 7 - membuf_size(&sdo->buf);
1451  if (n > 0) {
1452  if (__unlikely(!membuf_reserve(&sdo->buf, n)))
1453  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_MEM);
1454  co_unsigned32_t ac = co_ssdo_up_buf(sdo, n);
1455  if (__unlikely(ac))
1456  return co_ssdo_abort_res(sdo, ac);
1457  sdo->blksize = (uint8_t)((membuf_size(&sdo->buf) + 6) / 7);
1458  }
1459  int last = co_sdo_req_last(&sdo->req) && sdo->nbyte == sdo->req.nbyte;
1460 
1461  if (sdo->timeout)
1462  can_timer_timeout(sdo->timer, sdo->net, sdo->timeout);
1463 
1464  if (sdo->blksize) {
1465  // Send all segments in the current block.
1466  co_ssdo_send_blk_up_sub_res(sdo, last);
1467  return co_ssdo_blk_up_sub_state;
1468  } else {
1470  return co_ssdo_blk_up_end_state;
1471  }
1472 }
1473 
1474 static co_ssdo_state_t *
1475 co_ssdo_blk_up_end_on_abort(co_ssdo_t *sdo, co_unsigned32_t ac)
1476 {
1477  return co_ssdo_abort_res(sdo, ac);
1478 }
1479 
1480 static co_ssdo_state_t *
1481 co_ssdo_blk_up_end_on_time(co_ssdo_t *sdo, const struct timespec *tp)
1482 {
1483  (void)tp;
1484 
1485  return co_ssdo_abort_res(sdo, CO_SDO_AC_TIMEOUT);
1486 }
1487 
1488 static co_ssdo_state_t *
1490 {
1491  assert(sdo);
1492  assert(msg);
1493 
1494  if (__unlikely(msg->len < 1))
1495  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1496  uint8_t cs = msg->data[0];
1497 
1498  // Check the client command specifier.
1499  switch (cs & CO_SDO_CS_MASK) {
1500  case CO_SDO_CCS_BLK_UP_REQ: break;
1501  case CO_SDO_CS_ABORT: return co_ssdo_abort_ind(sdo);
1502  default: return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1503  }
1504 
1505  // Check the client subcommand.
1507  return co_ssdo_abort_res(sdo, CO_SDO_AC_NO_CS);
1508 
1509  return co_ssdo_abort_ind(sdo);
1510 }
1511 
1512 static co_ssdo_state_t *
1514 {
1515  assert(sdo);
1516 
1517  if (sdo->timeout)
1518  can_timer_stop(sdo->timer);
1519 
1520  sdo->idx = 0;
1521  sdo->subidx = 0;
1522 
1523  sdo->toggle = 0;
1524  sdo->blksize = 0;
1525  sdo->ackseq = 0;
1526  sdo->gencrc = 0;
1527  sdo->crc = 0;
1528 
1529  co_sdo_req_clear(&sdo->req);
1530  membuf_clear(&sdo->buf);
1531  sdo->nbyte = 0;
1532 
1533  return co_ssdo_wait_state;
1534 }
1535 
1536 static co_ssdo_state_t *
1537 co_ssdo_abort_res(co_ssdo_t *sdo, co_unsigned32_t ac)
1538 {
1539  trace("SSDO: abort code %08" PRIX32 " (%s)", ac, co_sdo_ac2str(ac));
1540  co_ssdo_send_abort(sdo, ac);
1541  return co_ssdo_abort_ind(sdo);
1542 }
1543 
1544 static co_unsigned32_t
1546 {
1547  assert(sdo);
1548 
1549  // Find the object in the object dictionary.
1550  co_obj_t *obj = co_dev_find_obj(sdo->dev, sdo->idx);
1551  if (__unlikely(!obj))
1552  return CO_SDO_AC_NO_OBJ;
1553 
1554  // Find the sub-object.
1555  co_sub_t *sub = co_obj_find_sub(obj, sdo->subidx);
1556  if (__unlikely(!sub))
1557  return CO_SDO_AC_NO_SUB;
1558 
1559  return co_sub_dn_ind(sub, &sdo->req);
1560 }
1561 
1562 static co_unsigned32_t
1564 {
1565  assert(sdo);
1566 
1567  // Find the object in the object dictionary.
1568  const co_obj_t *obj = co_dev_find_obj(sdo->dev, sdo->idx);
1569  if (__unlikely(!obj))
1570  return CO_SDO_AC_NO_OBJ;
1571 
1572  // Find the sub-object.
1573  const co_sub_t *sub = co_obj_find_sub(obj, sdo->subidx);
1574  if (__unlikely(!sub))
1575  return CO_SDO_AC_NO_SUB;
1576 
1577  // If the object is an array, check whether the element exists.
1578  // clang-format off
1579  if (co_obj_get_code(obj) == CO_OBJECT_ARRAY && __unlikely(sdo->subidx
1580  > co_obj_get_val_u8(obj, 0x00)))
1581  // clang-format on
1582  return CO_SDO_AC_NO_DATA;
1583 
1584  sdo->nbyte = 0;
1585  return co_sub_up_ind(sub, &sdo->req);
1586 }
1587 
1588 static co_unsigned32_t
1589 co_ssdo_up_buf(co_ssdo_t *sdo, size_t nbyte)
1590 {
1591  co_unsigned32_t ac = 0;
1592 
1593  if (__unlikely(nbyte && !membuf_reserve(&sdo->buf, nbyte)))
1594  return CO_SDO_AC_NO_MEM;
1595 
1596  while (nbyte) {
1597  if (sdo->nbyte >= sdo->req.nbyte) {
1598  if (co_sdo_req_last(&sdo->req)
1599  || __unlikely(ac = co_ssdo_up_ind(sdo)))
1600  break;
1601  sdo->nbyte = 0;
1602  }
1603  const char *src = (const char *)sdo->req.buf + sdo->nbyte;
1604  size_t n = MIN(nbyte, sdo->req.nbyte - sdo->nbyte);
1605 
1606  if (sdo->gencrc)
1607  sdo->crc = co_crc(sdo->crc, src, n);
1608 
1609  membuf_write(&sdo->buf, src, n);
1610  nbyte -= n;
1611  sdo->nbyte += n;
1612  }
1613 
1614  return ac;
1615 }
1616 
1617 static void
1618 co_ssdo_send_abort(co_ssdo_t *sdo, co_unsigned32_t ac)
1619 {
1620  assert(sdo);
1621 
1622  struct can_msg msg;
1624  stle_u32(msg.data + 4, ac);
1625  can_net_send(sdo->net, &msg);
1626 }
1627 
1628 static void
1630 {
1631  assert(sdo);
1632 
1633  uint8_t cs = CO_SDO_SCS_DN_INI_RES;
1634 
1635  struct can_msg msg;
1636  co_ssdo_init_ini_res(sdo, &msg, cs);
1637  can_net_send(sdo->net, &msg);
1638 }
1639 
1640 static void
1642 {
1643  assert(sdo);
1644 
1645  uint8_t cs = CO_SDO_SCS_DN_SEG_RES | sdo->toggle;
1646  sdo->toggle ^= CO_SDO_SEG_TOGGLE;
1647 
1648  struct can_msg msg;
1649  co_ssdo_init_seg_res(sdo, &msg, cs);
1650  can_net_send(sdo->net, &msg);
1651 }
1652 
1653 static void
1655 {
1656  assert(sdo);
1657  assert(sdo->req.size && sdo->req.size <= 4);
1658 
1659  const char *buf = membuf_begin(&sdo->buf);
1660  size_t nbyte = membuf_size(&sdo->buf);
1661  assert(nbyte == sdo->req.size);
1662 
1663  uint8_t cs = CO_SDO_SCS_UP_INI_RES | CO_SDO_INI_SIZE_EXP_SET(nbyte);
1664 
1665  struct can_msg msg;
1666  co_ssdo_init_ini_res(sdo, &msg, cs);
1667  memcpy(msg.data + 4, buf, nbyte);
1668  can_net_send(sdo->net, &msg);
1669 }
1670 
1671 static void
1673 {
1674  assert(sdo);
1675  assert(!sdo->req.size || sdo->req.size > 4);
1676 
1678 
1679  struct can_msg msg;
1680  co_ssdo_init_ini_res(sdo, &msg, cs);
1681  stle_u32(msg.data + 4, sdo->req.size);
1682  can_net_send(sdo->net, &msg);
1683 }
1684 
1685 static void
1687 {
1688  assert(sdo);
1689  assert(!sdo->req.size || sdo->req.size > 4);
1690 
1691  const char *buf = membuf_begin(&sdo->buf);
1692  size_t nbyte = membuf_size(&sdo->buf);
1693  assert(nbyte <= 7);
1694 
1695  uint8_t cs = CO_SDO_SCS_UP_SEG_RES | sdo->toggle
1696  | CO_SDO_SEG_SIZE_SET(nbyte);
1697  sdo->toggle ^= CO_SDO_SEG_TOGGLE;
1698  if (last)
1699  cs |= CO_SDO_SEG_LAST;
1700 
1701  struct can_msg msg;
1702  co_ssdo_init_seg_res(sdo, &msg, cs);
1703  memcpy(msg.data + 1, buf, nbyte);
1704  can_net_send(sdo->net, &msg);
1705 }
1706 
1707 static void
1709 {
1710  assert(sdo);
1711 
1713 
1714  struct can_msg msg;
1715  co_ssdo_init_ini_res(sdo, &msg, cs);
1716  msg.data[4] = sdo->blksize;
1717  can_net_send(sdo->net, &msg);
1718 }
1719 
1720 static void
1722 {
1723  assert(sdo);
1724 
1726 
1727  struct can_msg msg;
1728  co_ssdo_init_seg_res(sdo, &msg, cs);
1729  msg.data[1] = sdo->ackseq;
1730  msg.data[2] = sdo->blksize;
1731  can_net_send(sdo->net, &msg);
1732 }
1733 
1734 static void
1736 {
1737  assert(sdo);
1738 
1740 
1741  struct can_msg msg;
1742  co_ssdo_init_seg_res(sdo, &msg, cs);
1743  can_net_send(sdo->net, &msg);
1744 }
1745 
1746 static void
1748 {
1749  assert(sdo);
1750 
1751  uint8_t cs = CO_SDO_SCS_BLK_UP_RES | CO_SDO_BLK_CRC
1753 
1754  struct can_msg msg;
1755  co_ssdo_init_ini_res(sdo, &msg, cs);
1756  stle_u32(msg.data + 4, sdo->req.size);
1757  can_net_send(sdo->net, &msg);
1758 }
1759 
1760 static void
1762 {
1763  assert(sdo);
1764 
1765  const char *buf = membuf_begin(&sdo->buf);
1766  size_t nbyte = membuf_size(&sdo->buf);
1767 
1768  for (uint8_t seqno = 1; seqno <= sdo->blksize;
1769  seqno++, buf += 7, nbyte -= 7) {
1770  uint8_t cs = seqno;
1771  if (last && nbyte <= 7)
1772  cs |= CO_SDO_SEQ_LAST;
1773 
1774  struct can_msg msg;
1775  co_ssdo_init_seg_res(sdo, &msg, cs);
1776  memcpy(msg.data + 1, buf, MIN(nbyte, 7));
1777  can_net_send(sdo->net, &msg);
1778  }
1779 }
1780 
1781 static void
1783 {
1784  assert(sdo);
1785 
1786  // Compute the number of bytes in the last segment containing data.
1787  uint8_t n = sdo->req.size ? (sdo->req.size - 1) % 7 + 1 : 0;
1788 
1790  | CO_SDO_BLK_SIZE_SET(n);
1791 
1792  struct can_msg msg;
1793  co_ssdo_init_seg_res(sdo, &msg, cs);
1794  stle_u16(msg.data + 1, sdo->crc);
1795  can_net_send(sdo->net, &msg);
1796 }
1797 
1798 static void
1799 co_ssdo_init_ini_res(co_ssdo_t *sdo, struct can_msg *msg, uint8_t cs)
1800 {
1801  assert(sdo);
1802  assert(msg);
1803 
1804  *msg = (struct can_msg)CAN_MSG_INIT;
1805  msg->id = sdo->par.cobid_res;
1806  if (sdo->par.cobid_res & CO_SDO_COBID_FRAME) {
1807  msg->id &= CAN_MASK_EID;
1808  msg->flags |= CAN_FLAG_IDE;
1809  } else {
1810  msg->id &= CAN_MASK_BID;
1811  }
1812  msg->len = CAN_MAX_LEN;
1813  msg->data[0] = cs;
1814  stle_u16(msg->data + 1, sdo->idx);
1815  msg->data[3] = sdo->subidx;
1816 }
1817 
1818 static void
1819 co_ssdo_init_seg_res(co_ssdo_t *sdo, struct can_msg *msg, uint8_t cs)
1820 {
1821  assert(sdo);
1822  assert(msg);
1823 
1824  *msg = (struct can_msg)CAN_MSG_INIT;
1825  msg->id = sdo->par.cobid_res;
1826  if (sdo->par.cobid_res & CO_SDO_COBID_FRAME) {
1827  msg->id &= CAN_MASK_EID;
1828  msg->flags |= CAN_FLAG_IDE;
1829  } else {
1830  msg->id &= CAN_MASK_BID;
1831  }
1832  msg->len = CAN_MAX_LEN;
1833  msg->data[0] = cs;
1834 }
static co_ssdo_state_t *const co_ssdo_blk_dn_end_state
The &#39;block download end&#39; state.
Definition: ssdo.c:317
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_ssdo_state_t * co_ssdo_dn_ini_on_recv(co_ssdo_t *sdo, const struct can_msg *msg)
The &#39;CAN frame received&#39; transition function of the &#39;download initiate&#39; state.
Definition: ssdo.c:949
static int co_ssdo_update(co_ssdo_t *sdo)
Updates and (de)activates a Server-SDO service.
Definition: ssdo.c:718
co_unsigned8_t subidx
The current object sub-index.
Definition: ssdo.c:63
A CAN or CAN FD format frame.
Definition: msg.h:88
static void co_ssdo_send_blk_up_ini_res(co_ssdo_t *sdo)
Sends a Server-SDO &#39;block upload initiate&#39; response.
Definition: ssdo.c:1747
void stle_u32(void *ptr, uint_least32_t x)
Stores a 32-bit unsigned integer in little-endian byte order.
Definition: endian.h:534
co_ssdo_state_t *(* on_recv)(co_ssdo_t *sdo, const struct can_msg *msg)
A pointer to the transition function invoked when a CAN frame has been received.
Definition: ssdo.c:173
static co_unsigned32_t co_1200_dn_ind(co_sub_t *sub, struct co_sdo_req *req, void *data)
The download indication function for (all sub-objects of) CANopen objects 1200..127F (SDO server para...
Definition: ssdo.c:745
size_t membuf_write(struct membuf *buf, const void *ptr, size_t size)
Writes data to a memory buffer.
Definition: membuf.h:192
static co_ssdo_state_t *const co_ssdo_blk_dn_sub_state
The &#39;block download sub-block&#39; state.
Definition: ssdo.c:293
static co_ssdo_state_t *const co_ssdo_up_seg_state
The &#39;upload segment&#39; state.
Definition: ssdo.c:257
static co_ssdo_state_t *const co_ssdo_dn_seg_state
The &#39;download segment&#39; state.
Definition: ssdo.c:227
static co_unsigned32_t co_ssdo_up_buf(co_ssdo_t *sdo, size_t nbyte)
Copies at most nbyte bytes from a CANopen SDO upload request, obtaining more bytes with co_ssdo_up_in...
Definition: ssdo.c:1589
can_timer_t * timer
A pointer to the CAN timer.
Definition: ssdo.c:57
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
#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
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
can_recv_t * recv
A pointer to the CAN frame receiver.
Definition: ssdo.c:53
static co_ssdo_state_t * co_ssdo_up_seg_on_time(co_ssdo_t *sdo, const struct timespec *tp)
The &#39;timeout&#39; transition function of the &#39;upload segment&#39; state.
Definition: ssdo.c:1107
co_unsigned32_t cobid_res
COB-ID server -> client.
Definition: sdo.h:51
A CAN network interface.
Definition: net.c:37
uint8_t toggle
The current value of the toggle bit.
Definition: ssdo.c:65
static co_ssdo_state_t *const co_ssdo_blk_up_end_state
The &#39;block upload end&#39; state.
Definition: ssdo.c:376
#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
can_net_t * co_ssdo_get_net(const co_ssdo_t *sdo)
Returns a pointer to the CAN network of a Server-SDO.
Definition: ssdo.c:667
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
static void co_ssdo_init_seg_res(co_ssdo_t *sdo, struct can_msg *msg, uint8_t cs)
Initializes a Server-SDO download/upload segment response CAN frame.
Definition: ssdo.c:1819
static co_ssdo_state_t * co_ssdo_up_ini_on_recv(co_ssdo_t *sdo, const struct can_msg *msg)
The &#39;CAN frame received&#39; transition function of the &#39;upload initiate&#39; state.
Definition: ssdo.c:1064
static void co_ssdo_init_ini_res(co_ssdo_t *sdo, struct can_msg *msg, uint8_t cs)
Initializes a Server-SDO download/upload initiate response CAN frame.
Definition: ssdo.c:1799
static int co_ssdo_timer(const struct timespec *tp, void *data)
The CAN timer callback function for a Server-SDO service.
Definition: ssdo.c:869
static co_ssdo_state_t * co_ssdo_blk_up_sub_on_time(co_ssdo_t *sdo, const struct timespec *tp)
The &#39;timeout&#39; transition function of the &#39;block upload sub-block&#39; state.
Definition: ssdo.c:1397
static co_ssdo_state_t * co_ssdo_abort_ind(co_ssdo_t *sdo)
Processes an abort transfer indication by aborting any ongoing transfer of a Server-SDO and returning...
Definition: ssdo.c:1513
#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
A CANopen Server-SDO.
Definition: ssdo.c:43
int errnum2c(errnum_t errnum)
Transforms a platform-independent error number to a native error code.
Definition: errnum.c:825
co_unsigned16_t idx
The current object index.
Definition: ssdo.c:61
size_t nbyte
The number of bytes in req already copied to buf.
Definition: ssdo.c:79
int timeout
The SDO timeout (in milliseconds).
Definition: ssdo.c:55
void co_sdo_req_clear(struct co_sdo_req *req)
Clears a CANopen SDO upload/download request, including its buffer.
Definition: sdo.c:129
static void co_ssdo_send_dn_seg_res(co_ssdo_t *sdo)
Sends a Server-SDO &#39;download segment&#39; response.
Definition: ssdo.c:1641
static co_ssdo_state_t * co_ssdo_blk_up_end_on_abort(co_ssdo_t *sdo, co_unsigned32_t ac)
The &#39;abort&#39; transition function of the &#39;block upload end&#39; state.
Definition: ssdo.c:1475
#define CAN_MSG_INIT
The static initializer for can_msg.
Definition: msg.h:114
static co_ssdo_state_t * co_ssdo_blk_dn_sub_on_time(co_ssdo_t *sdo, const struct timespec *tp)
The &#39;timeout&#39; transition function of the &#39;block download sub-block&#39; state.
Definition: ssdo.c:1206
#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 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
A union of the CANopen static data types.
Definition: val.h:163
static co_ssdo_state_t * co_ssdo_blk_dn_end_on_abort(co_ssdo_t *sdo, co_unsigned32_t ac)
The &#39;abort&#39; transition function of the &#39;block download end&#39; state.
Definition: ssdo.c:1266
static co_unsigned32_t co_ssdo_up_ind(co_ssdo_t *sdo)
Processes an upload indication of a Server-SDO by checking access to the requested sub-object and wri...
Definition: ssdo.c:1563
static co_ssdo_state_t * co_ssdo_blk_dn_sub_on_recv(co_ssdo_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: ssdo.c:1214
co_unsigned8_t num
The SDO number.
Definition: ssdo.c:49
#define CO_SDO_SCS_UP_SEG_RES
The SDO server command specifier &#39;upload segment&#39; response.
Definition: sdo.h:46
uint_least16_t ldle_u16(const void *ptr)
Loads a 16-bit unsigned integer in little-endian byte order.
Definition: endian.h:487
#define CO_SDO_CS_ABORT
The SDO client/server command specifier &#39;abort transfer&#39; request.
Definition: sdo.h:34
co_unsigned8_t co_dev_get_id(const co_dev_t *dev)
Returns the node-ID of a CANopen device.
Definition: dev.c:198
static void co_ssdo_send_abort(co_ssdo_t *sdo, co_unsigned32_t ac)
Sends an abort transfer request.
Definition: ssdo.c:1618
#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
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 co_ssdo_state_t * co_ssdo_blk_up_end_on_time(co_ssdo_t *sdo, const struct timespec *tp)
The &#39;timeout&#39; transition function of the &#39;block upload end&#39; state.
Definition: ssdo.c:1481
static co_ssdo_state_t *const co_ssdo_wait_state
The &#39;waiting&#39; state.
Definition: ssdo.c:192
static void co_ssdo_send_blk_dn_sub_res(co_ssdo_t *sdo)
Sends a Server-SDO &#39;block upload sub-block&#39; response.
Definition: ssdo.c:1721
int co_ssdo_get_timeout(const co_ssdo_t *sdo)
Returns the timeout (in milliseconds) of a Server-SDO.
Definition: ssdo.c:699
#define CO_SDO_AC_BLK_CRC
SDO abort code: CRC error (block mode only).
Definition: sdo.h:78
#define CO_SDO_AC_NO_SDO
SDO abort code: Resource not available: SDO connection.
Definition: sdo.h:147
static co_ssdo_state_t * co_ssdo_blk_dn_sub_on_abort(co_ssdo_t *sdo, co_unsigned32_t ac)
The &#39;block download initiate&#39; state.
Definition: ssdo.c:1200
co_unsigned8_t co_ssdo_get_num(const co_ssdo_t *sdo)
Returns the SDO number of a Server-SDO.
Definition: ssdo.c:683
This header file is part of the CANopen library; it contains the CANopen value declarations.
static co_ssdo_state_t * co_ssdo_blk_dn_end_on_recv(co_ssdo_t *sdo, const struct can_msg *msg)
The &#39;CAN frame received&#39; transition function of the &#39;block download end&#39; state.
Definition: ssdo.c:1280
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
static void co_ssdo_emit_recv(co_ssdo_t *sdo, const struct can_msg *msg)
Invokes the &#39;CAN frame received&#39; transition function of the current state of a Server-SDO service...
Definition: ssdo.c:910
#define CO_SDO_INI_SIZE_EXP
The SDO size indicator flag indicating expedited transfer.
Definition: sdo.h:97
#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_ssdo_state_t * co_ssdo_blk_up_sub_on_abort(co_ssdo_t *sdo, co_unsigned32_t ac)
The &#39;block upload initiate&#39; state.
Definition: ssdo.c:1391
static co_ssdo_state_t * co_ssdo_dn_seg_on_recv(co_ssdo_t *sdo, const struct can_msg *msg)
The &#39;CAN frame received&#39; transition function of the &#39;download segment&#39; state.
Definition: ssdo.c:1015
void co_ssdo_set_timeout(co_ssdo_t *sdo, int timeout)
Sets the timeout of a Server-SDO.
Definition: ssdo.c:707
#define CO_DEFTYPE_UNSIGNED32
The data type (and object index) of a 32-bit unsigned integer.
Definition: type.h:50
static co_ssdo_state_t * co_ssdo_abort_res(co_ssdo_t *sdo, co_unsigned32_t ac)
Sends an abort transfer request and aborts any ongoing transfer by invoking co_ssdo_abort_ind().
Definition: ssdo.c:1537
#define CO_SDO_AC_TOGGLE
SDO abort code: Toggle bit not altered.
Definition: sdo.h:63
uint_least8_t data[CAN_MSG_MAX_LEN]
The frame payload (in case of a data frame).
Definition: msg.h:103
void * membuf_begin(const struct membuf *buf)
Returns a pointer to the first byte in a memory buffer.
Definition: membuf.h:144
This header file is part of the utilities library; it contains the native and platform-independent er...
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
int co_sdo_req_first(const struct co_sdo_req *req)
Returns 1 if the specified request includes the first segment, and 0 otherwise.
Definition: sdo.h:340
void can_timer_stop(can_timer_t *timer)
Stops a CAN timer and unregisters it with a network interface.
Definition: net.c:468
#define CAN_FLAG_IDE
The Identifier Extension (IDE) flag.
Definition: msg.h:41
uint8_t blksize
The number of segments per block.
Definition: ssdo.c:67
struct co_sdo_par par
The SDO parameter record.
Definition: ssdo.c:51
#define CAN_FLAG_EDL
The Extended Data Length (EDL) flag.
Definition: msg.h:55
static co_ssdo_state_t * co_ssdo_dn_seg_on_abort(co_ssdo_t *sdo, co_unsigned32_t ac)
The &#39;download initiate&#39; state.
Definition: ssdo.c:1001
static co_ssdo_state_t * co_ssdo_blk_dn_end_on_time(co_ssdo_t *sdo, const struct timespec *tp)
The &#39;timeout&#39; transition function of the &#39;block download end&#39; state.
Definition: ssdo.c:1272
uint16_t crc
The generated CRC.
Definition: ssdo.c:73
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
#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.
const struct co_sdo_par * co_ssdo_get_par(const co_ssdo_t *sdo)
Returns a pointer to the SDO parameter record of a Server-SDO.
Definition: ssdo.c:691
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
co_ssdo_state_t *(* on_abort)(co_ssdo_t *sdo, co_unsigned32_t ac)
A pointer to the transition function invoked when an abort code has been received.
Definition: ssdo.c:154
#define CO_SDO_INI_SIZE_IND
The SDO size indicator flag indicating that the data set size is indicated.
Definition: sdo.h:94
can_net_t * net
A pointer to a CAN network interface.
Definition: ssdo.c:45
void co_ssdo_destroy(co_ssdo_t *ssdo)
Destroys a CANopen Server-SDO service.
Definition: ssdo.c:657
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
#define MAX(a, b)
Returns the maximum of a and b.
Definition: util.h:65
co_dev_t * co_ssdo_get_dev(const co_ssdo_t *sdo)
Returns a pointer to the CANopen device of a Server-SDO.
Definition: ssdo.c:675
#define CO_SDO_CCS_DN_SEG_REQ
The SDO client command specifier &#39;download segment&#39; request.
Definition: sdo.h:58
co_ssdo_t * co_ssdo_create(can_net_t *net, co_dev_t *dev, co_unsigned8_t num)
Creates a new CANopen Server-SDO service.
Definition: ssdo.c:630
void can_timer_destroy(can_timer_t *timer)
Destroys a CAN timer.
Definition: net.c:407
co_ssdo_state_t * state
A pointer to the current state.
Definition: ssdo.c:59
static void co_ssdo_send_up_seg_res(co_ssdo_t *sdo, int last)
Sends a Server-SDO &#39;upload segment&#39; response.
Definition: ssdo.c:1686
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_ssdo_enter(co_ssdo_t *sdo, co_ssdo_state_t *next)
Enters the specified state of a Server-SDO service.
Definition: ssdo.c:881
static void co_ssdo_emit_time(co_ssdo_t *sdo, const struct timespec *tp)
Invokes the &#39;timeout&#39; transition function of the current state of a Server-SDO service.
Definition: ssdo.c:900
struct co_sdo_req req
The SDO request.
Definition: ssdo.c:75
size_t offset
The offset of the bytes at buf.
Definition: sdo.h:193
static co_ssdo_state_t * co_ssdo_blk_up_ini_on_recv(co_ssdo_t *sdo, const struct can_msg *msg)
The &#39;CAN frame received&#39; transition function of the &#39;block upload initiate&#39; state.
Definition: ssdo.c:1321
static co_ssdo_state_t * co_ssdo_blk_up_sub_on_recv(co_ssdo_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: ssdo.c:1405
Invalid argument.
Definition: errnum.h:129
void stle_u16(void *ptr, uint_least16_t x)
Stores a 16-bit unsigned integer in little-endian byte order.
Definition: endian.h:480
static co_ssdo_state_t * co_ssdo_blk_up_end_on_recv(co_ssdo_t *sdo, const struct can_msg *msg)
The &#39;CAN frame received&#39; transition function of the &#39;block upload end&#39; state.
Definition: ssdo.c:1489
#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 void co_ssdo_emit_abort(co_ssdo_t *sdo, co_unsigned32_t ac)
Invokes the &#39;abort&#39; transition function of the current state of a Server-SDO service.
Definition: ssdo.c:890
uint16_t co_crc(uint16_t crc, const void *ptr, size_t n)
Computes a CRC-16 checksum.
Definition: crc.c:28
static void co_ssdo_send_blk_dn_ini_res(co_ssdo_t *sdo)
Sends a Server-SDO &#39;block download initiate&#39; response.
Definition: ssdo.c:1708
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
static co_ssdo_state_t * co_ssdo_up_seg_on_abort(co_ssdo_t *sdo, co_unsigned32_t ac)
The &#39;upload initiate&#39; state.
Definition: ssdo.c:1101
static co_ssdo_state_t * co_ssdo_wait_on_recv(co_ssdo_t *sdo, const struct can_msg *msg)
The &#39;CAN frame received&#39; transition function of the &#39;waiting&#39; state.
Definition: ssdo.c:929
A CANopen device.
Definition: dev.c:38
#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
const char * co_sdo_ac2str(co_unsigned32_t ac)
Returns a string describing an SDO abort code.
Definition: sdo.c:57
#define CO_SDO_SCS_DN_SEG_RES
The SDO server command specifier &#39;download segment&#39; response.
Definition: sdo.h:40
uint8_t ackseq
The sequence number of the last successfully received segment.
Definition: ssdo.c:69
struct membuf buf
The buffer.
Definition: ssdo.c:77
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
can_timer_t * can_timer_create(void)
Creates a new CAN timer.
Definition: net.c:382
#define CO_SDO_SCS_BLK_UP_RES
The SDO server command specifier &#39;block upload&#39; response.
Definition: sdo.h:52
co_ssdo_state_t *(* on_time)(co_ssdo_t *sdo, const struct timespec *tp)
A pointer to the transition function invoked when a timeout occurs.
Definition: ssdo.c:163
void membuf_fini(struct membuf *buf)
Finalizes a memory buffer.
Definition: membuf.c:38
static co_unsigned32_t co_ssdo_dn_ind(co_ssdo_t *sdo)
Processes a download indication of a Server-SDO by checking access to the requested sub-object and re...
Definition: ssdo.c:1545
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
void membuf_flush(struct membuf *buf, size_t size)
Flushes size bytes from the beginning of a memory buffer.
Definition: membuf.c:75
static co_ssdo_state_t * co_ssdo_up_seg_on_recv(co_ssdo_t *sdo, const struct can_msg *msg)
The &#39;CAN frame received&#39; transition function of the &#39;upload segment&#39; state.
Definition: ssdo.c:1115
void co_val_fini(co_unsigned16_t type, void *val)
Finalizes a value of the specified data type.
Definition: val.c:273
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
static void co_ssdo_send_up_exp_res(co_ssdo_t *sdo)
Sends a Server-SDO &#39;upload initiate&#39; (expedited) response.
Definition: ssdo.c:1654
static void co_ssdo_send_up_ini_res(co_ssdo_t *sdo)
Sends a Server-SDO &#39;upload initiate&#39; response.
Definition: ssdo.c:1672
#define CO_SDO_CS_MASK
The mask to extract the command specifier (CS) from an SDO command byte.
Definition: sdo.h:31
static void co_ssdo_send_blk_dn_end_res(co_ssdo_t *sdo)
Sends a Server-SDO &#39;block download end&#39; response.
Definition: ssdo.c:1735
#define CO_SDO_AC_NO_SUB
SDO abort code: Sub-index does not exist.
Definition: sdo.h:132
#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
void co_sdo_req_init(struct co_sdo_req *req)
Initializes a CANopen SDO upload/download request.
Definition: sdo.c:109
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
size_t nbyte
The number of bytes available at buf.
Definition: sdo.h:188
A CANopen Server-SDO state.
Definition: ssdo.c:144
const void * buf
A pointer to the next bytes to be uploaded/downloaded.
Definition: sdo.h:186
unsigned gencrc
A flag indicating whether a CRC should be generated.
Definition: ssdo.c:71
#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_ssdo_state_t *const co_ssdo_blk_up_sub_state
The &#39;block upload sub-block&#39; state.
Definition: ssdo.c:353
#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
This header file is part of the CANopen library; it contains the Server-SDO declarations.
co_unsigned8_t co_sub_get_subidx(const co_sub_t *sub)
Returns the sub-index of a CANopen sub-object.
Definition: obj.c:466
#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
static void co_ssdo_send_dn_ini_res(co_ssdo_t *sdo)
Sends a Server-SDO &#39;download initiate&#39; response.
Definition: ssdo.c:1629
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
size_t membuf_size(const struct membuf *buf)
Returns the total number of bytes written to a memory buffer.
Definition: membuf.h:156
co_dev_t * dev
A pointer to a CANopen device.
Definition: ssdo.c:47
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
static co_ssdo_state_t * co_ssdo_dn_seg_on_time(co_ssdo_t *sdo, const struct timespec *tp)
The &#39;timeout&#39; transition function of the &#39;download segment&#39; state.
Definition: ssdo.c:1007
#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_ssdo_recv(const struct can_msg *msg, void *data)
The CAN receive callback function for a Server-SDO service.
Definition: ssdo.c:847
static void co_ssdo_send_blk_up_end_res(co_ssdo_t *sdo)
Sends a Server-SDO &#39;block upload end&#39; response.
Definition: ssdo.c:1782
#define CO_SDO_AC_BLK_SEQ
SDO abort code: Invalid sequence number (block mode only).
Definition: sdo.h:75
A CANopen object.
Definition: obj.h:32
This header file is part of the CANopen library; it contains the object dictionary declarations...
static co_ssdo_state_t * co_ssdo_wait_on_abort(co_ssdo_t *sdo, co_unsigned32_t ac)
The &#39;abort&#39; transition function of the &#39;waiting&#39; state.
Definition: ssdo.c:920
#define CO_SDO_SEG_TOGGLE
The mask to get/set the toggle bit from an SDO command byte.
Definition: sdo.h:117
static void co_ssdo_send_blk_up_sub_res(co_ssdo_t *sdo, int last)
Sends a Server-SDO &#39;block upload sub-block&#39; response.
Definition: ssdo.c:1761
#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
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
static co_ssdo_state_t * co_ssdo_blk_dn_ini_on_recv(co_ssdo_t *sdo, const struct can_msg *msg)
The &#39;CAN frame received&#39; transition function of the &#39;block download initiate&#39; state.
Definition: ssdo.c:1154
#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
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
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
This is the internal header file of the Service Data Object (SDO) declarations.
#define CO_SDO_AC_NO_OBJ
SDO abort code: Object does not exist in the object dictionary.
Definition: sdo.h:93
#define CO_SDO_SEQ_LAST
The mask to get/set the last segment bit from an SDO command byte.
Definition: sdo.h:151