1 /*
2 * CANopen Service Data Object - server.
3 *
4 * @file CO_SDO.c
5 * @ingroup CO_SDO
6 * @author Janez Paternoster
7 * @copyright 2004 - 2020 Janez Paternoster
8 *
9 * This file is part of CANopenNode, an opensource CANopen Stack.
10 * Project home page is <https://github.com/CANopenNode/CANopenNode>.
11 * For more information on CANopen see <http://www.can-cia.org/>.
12 *
13 * Licensed under the Apache License, Version 2.0 (the "License");
14 * you may not use this file except in compliance with the License.
15 * You may obtain a copy of the License at
16 *
17 * http://www.apache.org/licenses/LICENSE-2.0
18 *
19 * Unless required by applicable law or agreed to in writing, software
20 * distributed under the License is distributed on an "AS IS" BASIS,
21 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 * See the License for the specific language governing permissions and
23 * limitations under the License.
24 */
25
26
27 #include "CO_driver.h"
28 #include "CO_SDO.h"
29 #include "crc16-ccitt.h"
30
31
32 /* Client command specifier, see DS301 */
33 #define CCS_DOWNLOAD_INITIATE 1U
34 #define CCS_DOWNLOAD_SEGMENT 0U
35 #define CCS_UPLOAD_INITIATE 2U
36 #define CCS_UPLOAD_SEGMENT 3U
37 #define CCS_DOWNLOAD_BLOCK 6U
38 #define CCS_UPLOAD_BLOCK 5U
39 #define CCS_ABORT 0x80U
40
41
42 #if CO_SDO_BUFFER_SIZE < 7
43 #error CO_SDO_BUFFER_SIZE must be greater than 7
44 #endif
45
46
47 /* Helper functions. **********************************************************/
CO_memcpy(uint8_t dest[],const uint8_t src[],const uint16_t size)48 void CO_memcpy(uint8_t dest[], const uint8_t src[], const uint16_t size){
49 uint16_t i;
50 for(i = 0; i < size; i++){
51 dest[i] = src[i];
52 }
53 }
54
CO_memset(uint8_t dest[],uint8_t c,const uint16_t size)55 void CO_memset(uint8_t dest[], uint8_t c, const uint16_t size){
56 uint16_t i;
57 for(i = 0; i < size; i++){
58 dest[i] = c;
59 }
60 }
61
CO_getUint16(const uint8_t data[])62 uint16_t CO_getUint16(const uint8_t data[]){
63 CO_bytes_t b;
64 b.u8[0] = data[0];
65 b.u8[1] = data[1];
66 return b.u16[0];
67 }
68
CO_getUint32(const uint8_t data[])69 uint32_t CO_getUint32(const uint8_t data[]){
70 CO_bytes_t b;
71 b.u8[0] = data[0];
72 b.u8[1] = data[1];
73 b.u8[2] = data[2];
74 b.u8[3] = data[3];
75 return b.u32[0];
76 }
77
CO_setUint16(uint8_t data[],const uint16_t value)78 void CO_setUint16(uint8_t data[], const uint16_t value){
79 CO_bytes_t b;
80 b.u16[0] = value;
81 data[0] = b.u8[0];
82 data[1] = b.u8[1];
83 }
84
CO_setUint32(uint8_t data[],const uint32_t value)85 void CO_setUint32(uint8_t data[], const uint32_t value){
86 CO_bytes_t b;
87 b.u32[0] = value;
88 data[0] = b.u8[0];
89 data[1] = b.u8[1];
90 data[2] = b.u8[2];
91 data[3] = b.u8[3];
92 }
93
94 #ifdef CO_LITTLE_ENDIAN
CO_memcpySwap2(void * dest,const void * src)95 void CO_memcpySwap2(void* dest, const void* src){
96 char *cdest;
97 char *csrc;
98 cdest = (char *) dest;
99 csrc = (char *) src;
100 cdest[0] = csrc[0];
101 cdest[1] = csrc[1];
102 }
CO_memcpySwap4(void * dest,const void * src)103 void CO_memcpySwap4(void* dest, const void* src){
104 char *cdest;
105 char *csrc;
106 cdest = (char *) dest;
107 csrc = (char *) src;
108 cdest[0] = csrc[0];
109 cdest[1] = csrc[1];
110 cdest[2] = csrc[2];
111 cdest[3] = csrc[3];
112 }
CO_memcpySwap8(void * dest,const void * src)113 void CO_memcpySwap8(void* dest, const void* src){
114 char *cdest;
115 char *csrc;
116 cdest = (char *) dest;
117 csrc = (char *) src;
118 cdest[0] = csrc[0];
119 cdest[1] = csrc[1];
120 cdest[2] = csrc[2];
121 cdest[3] = csrc[3];
122 cdest[4] = csrc[4];
123 cdest[5] = csrc[5];
124 cdest[6] = csrc[6];
125 cdest[7] = csrc[7];
126 }
127 #endif
128 #ifdef CO_BIG_ENDIAN
CO_memcpySwap2(void * dest,const void * src)129 void CO_memcpySwap2(void* dest, const void* src){
130 char *cdest;
131 char *csrc;
132 cdest = (char *) dest;
133 csrc = (char *) src;
134 cdest[0] = csrc[1];
135 cdest[1] = csrc[0];
136 }
CO_memcpySwap4(void * dest,const void * src)137 void CO_memcpySwap4(void* dest, const void* src){
138 char *cdest;
139 char *csrc;
140 cdest = (char *) dest;
141 csrc = (char *) src;
142 cdest[0] = csrc[3];
143 cdest[1] = csrc[2];
144 cdest[2] = csrc[1];
145 cdest[3] = csrc[0];
146 }
CO_memcpySwap8(void * dest,const void * src)147 void CO_memcpySwap8(void* dest, const void* src){
148 char *cdest;
149 char *csrc;
150 cdest = (char *) dest;
151 csrc = (char *) src;
152 cdest[0] = csrc[7];
153 cdest[1] = csrc[6];
154 cdest[2] = csrc[5];
155 cdest[3] = csrc[4];
156 cdest[4] = csrc[3];
157 cdest[5] = csrc[2];
158 cdest[6] = csrc[1];
159 cdest[7] = csrc[0];
160 }
161 #endif
162
CO_SDO_receive_done(CO_SDO_t * SDO)163 static void CO_SDO_receive_done(CO_SDO_t *SDO){
164 #if CO_SDO_RX_DATA_SIZE > 1
165 uint8_t rcv = SDO->CANrxRcv;
166 uint8_t newRcv = rcv;
167
168 if (++newRcv >= CO_SDO_RX_DATA_SIZE)
169 newRcv = 0;
170 SDO->CANrxRcv = newRcv;
171 SET_CANrxNew(SDO->CANrxNew[rcv]);
172 #else
173 SET_CANrxNew(SDO->CANrxNew[0]);
174 #endif
175 }
176
177 /*
178 * Read received message from CAN module.
179 *
180 * Function will be called (by CAN receive interrupt) every time, when CAN
181 * message with correct identifier will be received. For more information and
182 * description of parameters see file CO_driver.h.
183 */
184 static void CO_SDO_receive(void *object, const CO_CANrxMsg_t *msg);
CO_SDO_receive(void * object,const CO_CANrxMsg_t * msg)185 static void CO_SDO_receive(void *object, const CO_CANrxMsg_t *msg){
186 CO_SDO_t *SDO;
187 uint8_t rcv, *CANrxData;
188
189 SDO = (CO_SDO_t*)object; /* this is the correct pointer type of the first argument */
190 rcv = SDO->CANrxRcv;
191 CANrxData = SDO->CANrxData[rcv];
192
193 /* verify message length and message queue overflow (if previous messages were not processed yet) */
194 if((msg->DLC == 8U) && (!IS_CANrxNew(SDO->CANrxNew[rcv]))){
195 if(SDO->state != CO_SDO_ST_DOWNLOAD_BL_SUBBLOCK) {
196 /* copy data and set 'new message' flag */
197 CANrxData[0] = msg->data[0];
198 CANrxData[1] = msg->data[1];
199 CANrxData[2] = msg->data[2];
200 CANrxData[3] = msg->data[3];
201 CANrxData[4] = msg->data[4];
202 CANrxData[5] = msg->data[5];
203 CANrxData[6] = msg->data[6];
204 CANrxData[7] = msg->data[7];
205
206 CO_SDO_receive_done(SDO);
207 }
208 else {
209 /* block download, copy data directly */
210 uint8_t seqno;
211
212 CANrxData[0] = msg->data[0];
213 seqno = CANrxData[0] & 0x7fU;
214 SDO->timeoutTimer = 0;
215 /* clear timeout in sub-block transfer indication if set before */
216 if (SDO->timeoutSubblockDownolad)
217 SDO->timeoutSubblockDownolad = false;
218
219 /* check correct sequence number. */
220 if(seqno == (SDO->sequence + 1U)) {
221 /* sequence is correct */
222
223 /* check if buffer can store whole message just in case */
224 if (CO_SDO_BUFFER_SIZE - SDO->bufferOffset >= 7) {
225 uint8_t i;
226
227 SDO->sequence++;
228
229 /* copy data */
230 for(i=1; i<8; i++) {
231 SDO->ODF_arg.data[SDO->bufferOffset++] = msg->data[i]; //SDO->ODF_arg.data is equal as SDO->databuffer
232 }
233
234 /* break reception if last segment, block ends or block sequence is too large */
235 if(((CANrxData[0] & 0x80U) == 0x80U) || (SDO->sequence >= SDO->blksize)) {
236 SDO->state = CO_SDO_ST_DOWNLOAD_BL_SUB_RESP;
237 CO_SDO_receive_done(SDO);
238 }
239 } else {
240 /* buffer is full, ignore this segment, send response without resetting sequence */
241 SDO->state = CO_SDO_ST_DOWNLOAD_BL_SUB_RESP_2;
242 CO_SDO_receive_done(SDO);
243 }
244 }
245 else if((seqno == SDO->sequence) || (SDO->sequence == 0U)) {
246 /* Ignore message, if it is duplicate or if sequence didn't started yet. */
247 }
248 else {
249 /* seqno is wrong, send response without resetting sequence */
250 SDO->state = CO_SDO_ST_DOWNLOAD_BL_SUB_RESP_2;
251 CO_SDO_receive_done(SDO);
252 }
253 }
254
255 /* Optional signal to RTOS, which can resume task, which handles SDO server. */
256 if((IS_CANrxNew(SDO->CANrxNew[rcv])) && (SDO->pFunctSignal != NULL)) {
257 SDO->pFunctSignal();
258 }
259 }
260 }
261
262
263 /*
264 * Function for accessing _SDO server parameter_ for default SDO (index 0x1200)
265 * from SDO server.
266 *
267 * For more information see file CO_SDO.h.
268 */
269 static CO_SDO_abortCode_t CO_ODF_1200(CO_ODF_arg_t *ODF_arg);
CO_ODF_1200(CO_ODF_arg_t * ODF_arg)270 static CO_SDO_abortCode_t CO_ODF_1200(CO_ODF_arg_t *ODF_arg){
271 uint8_t *nodeId;
272 uint32_t value;
273 CO_SDO_abortCode_t ret = CO_SDO_AB_NONE;
274
275 nodeId = (uint8_t*) ODF_arg->object;
276 value = CO_getUint32(ODF_arg->data);
277
278 /* if SDO reading Object dictionary 0x1200, add nodeId to the value */
279 if((ODF_arg->reading) && (ODF_arg->subIndex > 0U)){
280 CO_setUint32(ODF_arg->data, value + *nodeId);
281 }
282
283 return ret;
284 }
285
286
287 /******************************************************************************/
CO_SDO_init(CO_SDO_t * SDO,uint32_t COB_IDClientToServer,uint32_t COB_IDServerToClient,uint16_t ObjDictIndex_SDOServerParameter,CO_SDO_t * parentSDO,const CO_OD_entry_t OD[],uint16_t ODSize,CO_OD_extension_t * ODExtensions,uint8_t nodeId,CO_CANmodule_t * CANdevRx,uint16_t CANdevRxIdx,CO_CANmodule_t * CANdevTx,uint16_t CANdevTxIdx)288 CO_ReturnError_t CO_SDO_init(
289 CO_SDO_t *SDO,
290 uint32_t COB_IDClientToServer,
291 uint32_t COB_IDServerToClient,
292 uint16_t ObjDictIndex_SDOServerParameter,
293 CO_SDO_t *parentSDO,
294 const CO_OD_entry_t OD[],
295 uint16_t ODSize,
296 CO_OD_extension_t *ODExtensions,
297 uint8_t nodeId,
298 CO_CANmodule_t *CANdevRx,
299 uint16_t CANdevRxIdx,
300 CO_CANmodule_t *CANdevTx,
301 uint16_t CANdevTxIdx)
302 {
303 /* verify arguments */
304 if(SDO==NULL || CANdevRx==NULL || CANdevTx==NULL){
305 return CO_ERROR_ILLEGAL_ARGUMENT;
306 }
307
308 /* configure own object dictionary */
309 if(parentSDO == NULL){
310 uint16_t i;
311
312 SDO->ownOD = true;
313 SDO->OD = OD;
314 SDO->ODSize = ODSize;
315 SDO->ODExtensions = ODExtensions;
316
317 /* clear pointers in ODExtensions */
318 for(i=0U; i<ODSize; i++){
319 SDO->ODExtensions[i].pODFunc = NULL;
320 SDO->ODExtensions[i].object = NULL;
321 SDO->ODExtensions[i].flags = NULL;
322 }
323 }
324 /* copy object dictionary from parent */
325 else{
326 SDO->ownOD = false;
327 SDO->OD = parentSDO->OD;
328 SDO->ODSize = parentSDO->ODSize;
329 SDO->ODExtensions = parentSDO->ODExtensions;
330 }
331
332 /* Configure object variables */
333 SDO->nodeId = nodeId;
334 SDO->state = CO_SDO_ST_IDLE;
335
336 uint8_t i;
337 for(i=0U; i<CO_SDO_RX_DATA_SIZE; i++){
338 CLEAR_CANrxNew(SDO->CANrxNew[i]);
339 }
340 SDO->CANrxRcv = 0;
341 SDO->CANrxProc = 0;
342
343 SDO->pFunctSignal = NULL;
344
345
346 /* Configure Object dictionary entry at index 0x1200 */
347 if(ObjDictIndex_SDOServerParameter == OD_H1200_SDO_SERVER_PARAM){
348 CO_OD_configure(SDO, ObjDictIndex_SDOServerParameter, CO_ODF_1200, (void*)&SDO->nodeId, 0U, 0U);
349 }
350
351 if((COB_IDClientToServer & 0x80000000) != 0 || (COB_IDServerToClient & 0x80000000) != 0 ){
352 // SDO is invalid
353 COB_IDClientToServer = 0;
354 COB_IDServerToClient = 0;
355 }
356 /* configure SDO server CAN reception */
357 CO_CANrxBufferInit(
358 CANdevRx, /* CAN device */
359 CANdevRxIdx, /* rx buffer index */
360 COB_IDClientToServer, /* CAN identifier */
361 0x7FF, /* mask */
362 0, /* rtr */
363 (void*)SDO, /* object passed to receive function */
364 CO_SDO_receive); /* this function will process received message */
365
366 /* configure SDO server CAN transmission */
367 SDO->CANdevTx = CANdevTx;
368 SDO->CANtxBuff = CO_CANtxBufferInit(
369 CANdevTx, /* CAN device */
370 CANdevTxIdx, /* index of specific buffer inside CAN module */
371 COB_IDServerToClient, /* CAN identifier */
372 0, /* rtr */
373 8, /* number of data bytes */
374 0); /* synchronous message flag bit */
375
376 return CO_ERROR_NO;
377 }
378
379
380 /******************************************************************************/
CO_SDO_initCallback(CO_SDO_t * SDO,void (* pFunctSignal)(void))381 void CO_SDO_initCallback(
382 CO_SDO_t *SDO,
383 void (*pFunctSignal)(void))
384 {
385 if(SDO != NULL){
386 SDO->pFunctSignal = pFunctSignal;
387 }
388 }
389
390
391 /******************************************************************************/
CO_OD_configure(CO_SDO_t * SDO,uint16_t index,CO_SDO_abortCode_t (* pODFunc)(CO_ODF_arg_t * ODF_arg),void * object,uint8_t * flags,uint8_t flagsSize)392 void CO_OD_configure(
393 CO_SDO_t *SDO,
394 uint16_t index,
395 CO_SDO_abortCode_t (*pODFunc)(CO_ODF_arg_t *ODF_arg),
396 void *object,
397 uint8_t *flags,
398 uint8_t flagsSize)
399 {
400 uint16_t entryNo;
401
402 entryNo = CO_OD_find(SDO, index);
403 if(entryNo < 0xFFFFU){
404 CO_OD_extension_t *ext = &SDO->ODExtensions[entryNo];
405 uint8_t maxSubIndex = SDO->OD[entryNo].maxSubIndex;
406
407 ext->pODFunc = pODFunc;
408 ext->object = object;
409 if((flags != NULL) && (flagsSize != 0U) && (flagsSize == maxSubIndex)){
410 uint16_t i;
411 ext->flags = flags;
412 for(i=0U; i<=maxSubIndex; i++){
413 ext->flags[i] = 0U;
414 }
415 }
416 else{
417 ext->flags = NULL;
418 }
419 }
420 }
421
422
423 /******************************************************************************/
CO_OD_find(CO_SDO_t * SDO,uint16_t index)424 uint16_t CO_OD_find(CO_SDO_t *SDO, uint16_t index){
425 /* Fast search in ordered Object Dictionary. If indexes are mixed, this won't work. */
426 /* If Object Dictionary has up to 2^N entries, then N is max number of loop passes. */
427 uint16_t cur, min, max;
428 const CO_OD_entry_t* object;
429
430 min = 0U;
431 max = SDO->ODSize - 1U;
432 while(min < max){
433 cur = (min + max) / 2;
434 object = &SDO->OD[cur];
435 /* Is object matched */
436 if(index == object->index){
437 return cur;
438 }
439 if(index < object->index){
440 max = cur;
441 if(max) max--;
442 }
443 else
444 min = cur + 1U;
445 }
446
447 if(min == max){
448 object = &SDO->OD[min];
449 /* Is object matched */
450 if(index == object->index){
451 return min;
452 }
453 }
454
455 return 0xFFFFU; /* object does not exist in OD */
456 }
457
458
459 /******************************************************************************/
CO_OD_getLength(CO_SDO_t * SDO,uint16_t entryNo,uint8_t subIndex)460 uint16_t CO_OD_getLength(CO_SDO_t *SDO, uint16_t entryNo, uint8_t subIndex){
461 const CO_OD_entry_t* object = &SDO->OD[entryNo];
462
463 if(entryNo == 0xFFFFU){
464 return 0U;
465 }
466
467 if(object->maxSubIndex == 0U){ /* Object type is Var */
468 if(object->pData == 0){ /* data type is domain */
469 return CO_SDO_BUFFER_SIZE;
470 }
471 else{
472 return object->length;
473 }
474 }
475 else if(object->attribute != 0U){ /* Object type is Array */
476 if(subIndex == 0U){
477 return 1U;
478 }
479 else if(object->pData == 0){
480 /* data type is domain */
481 return CO_SDO_BUFFER_SIZE;
482 }
483 else{
484 return object->length;
485 }
486 }
487 else{ /* Object type is Record */
488 if(((const CO_OD_entryRecord_t*)(object->pData))[subIndex].pData == 0){
489 /* data type is domain */
490 return CO_SDO_BUFFER_SIZE;
491 }
492 else{
493 return ((const CO_OD_entryRecord_t*)(object->pData))[subIndex].length;
494 }
495 }
496 }
497
498
499 /******************************************************************************/
CO_OD_getAttribute(CO_SDO_t * SDO,uint16_t entryNo,uint8_t subIndex)500 uint16_t CO_OD_getAttribute(CO_SDO_t *SDO, uint16_t entryNo, uint8_t subIndex){
501 const CO_OD_entry_t* object = &SDO->OD[entryNo];
502
503 if(entryNo == 0xFFFFU){
504 return 0U;
505 }
506
507 if(object->maxSubIndex == 0U){ /* Object type is Var */
508 return object->attribute;
509 }
510 else if(object->attribute != 0U){/* Object type is Array */
511 bool_t exception_1003 = false;
512 uint16_t attr = object->attribute;
513
514 /* Special exception: Object 1003,00 should be writable */
515 if(object->index == 0x1003 && subIndex == 0) {
516 exception_1003 = true;
517 attr |= CO_ODA_WRITEABLE;
518 }
519
520 if(subIndex == 0U && !exception_1003){
521 /* First subIndex is readonly */
522 attr &= ~(CO_ODA_WRITEABLE | CO_ODA_RPDO_MAPABLE);
523 attr |= CO_ODA_READABLE;
524 }
525 return attr;
526 }
527 else{ /* Object type is Record */
528 return ((const CO_OD_entryRecord_t*)(object->pData))[subIndex].attribute;
529 }
530 }
531
532
533 /******************************************************************************/
CO_OD_getDataPointer(CO_SDO_t * SDO,uint16_t entryNo,uint8_t subIndex)534 void* CO_OD_getDataPointer(CO_SDO_t *SDO, uint16_t entryNo, uint8_t subIndex){
535 const CO_OD_entry_t* object = &SDO->OD[entryNo];
536
537 if(entryNo == 0xFFFFU){
538 return 0;
539 }
540
541 if(object->maxSubIndex == 0U){ /* Object type is Var */
542 return object->pData;
543 }
544 else if(object->maxSubIndex < subIndex){
545 /* Object type Array/Record, request is out of bounds */
546 return 0;
547 }
548 else if(object->attribute != 0U){/* Object type is Array */
549 if(subIndex==0){
550 /* this is the data, for the subIndex 0 in the array */
551 return (void*) &object->maxSubIndex;
552 }
553 else if(object->pData == 0){
554 /* data type is domain */
555 return 0;
556 }
557 else{
558 return (void*)(((int8_t*)object->pData) + ((subIndex-1) * object->length));
559 }
560 }
561 else{ /* Object Type is Record */
562 return ((const CO_OD_entryRecord_t*)(object->pData))[subIndex].pData;
563 }
564 }
565
566
567 /******************************************************************************/
CO_OD_getFlagsPointer(CO_SDO_t * SDO,uint16_t entryNo,uint8_t subIndex)568 uint8_t* CO_OD_getFlagsPointer(CO_SDO_t *SDO, uint16_t entryNo, uint8_t subIndex){
569 CO_OD_extension_t* ext;
570
571 if((entryNo == 0xFFFFU) || (SDO->ODExtensions == 0)){
572 return 0;
573 }
574
575 ext = &SDO->ODExtensions[entryNo];
576
577 return &ext->flags[subIndex];
578 }
579
580
581 /******************************************************************************/
CO_SDO_initTransfer(CO_SDO_t * SDO,uint16_t index,uint8_t subIndex)582 uint32_t CO_SDO_initTransfer(CO_SDO_t *SDO, uint16_t index, uint8_t subIndex){
583
584 SDO->ODF_arg.index = index;
585 SDO->ODF_arg.subIndex = subIndex;
586
587 /* find object in Object Dictionary */
588 SDO->entryNo = CO_OD_find(SDO, index);
589 if(SDO->entryNo == 0xFFFFU){
590 return CO_SDO_AB_NOT_EXIST ; /* object does not exist in OD */
591 }
592
593 /* verify existance of subIndex */
594 if(subIndex > SDO->OD[SDO->entryNo].maxSubIndex &&
595 SDO->OD[SDO->entryNo].pData != NULL)
596 {
597 return CO_SDO_AB_SUB_UNKNOWN; /* Sub-index does not exist. */
598 }
599
600 /* pointer to data in Object dictionary */
601 SDO->ODF_arg.ODdataStorage = CO_OD_getDataPointer(SDO, SDO->entryNo, subIndex);
602
603 /* fill ODF_arg */
604 SDO->ODF_arg.object = NULL;
605 if(SDO->ODExtensions){
606 CO_OD_extension_t *ext = &SDO->ODExtensions[SDO->entryNo];
607 SDO->ODF_arg.object = ext->object;
608 }
609 SDO->ODF_arg.data = SDO->databuffer;
610 SDO->ODF_arg.dataLength = CO_OD_getLength(SDO, SDO->entryNo, subIndex);
611 SDO->ODF_arg.attribute = CO_OD_getAttribute(SDO, SDO->entryNo, subIndex);
612 SDO->ODF_arg.pFlags = CO_OD_getFlagsPointer(SDO, SDO->entryNo, subIndex);
613
614 SDO->ODF_arg.firstSegment = true;
615 SDO->ODF_arg.lastSegment = true;
616
617 /* indicate total data length, if not domain */
618 SDO->ODF_arg.dataLengthTotal = (SDO->ODF_arg.ODdataStorage) ? SDO->ODF_arg.dataLength : 0U;
619
620 SDO->ODF_arg.offset = 0U;
621
622 /* verify length */
623 if(SDO->ODF_arg.dataLength > CO_SDO_BUFFER_SIZE){
624 return CO_SDO_AB_DEVICE_INCOMPAT; /* general internal incompatibility in the device */
625 }
626
627 return 0U;
628 }
629
630
631 /******************************************************************************/
CO_SDO_readOD(CO_SDO_t * SDO,uint16_t SDOBufferSize)632 uint32_t CO_SDO_readOD(CO_SDO_t *SDO, uint16_t SDOBufferSize){
633 uint8_t *SDObuffer = SDO->ODF_arg.data;
634 uint8_t *ODdata = (uint8_t*)SDO->ODF_arg.ODdataStorage;
635 uint16_t length = SDO->ODF_arg.dataLength;
636 CO_OD_extension_t *ext = 0;
637
638 /* is object readable? */
639 if((SDO->ODF_arg.attribute & CO_ODA_READABLE) == 0)
640 return CO_SDO_AB_WRITEONLY; /* attempt to read a write-only object */
641
642 /* find extension */
643 if(SDO->ODExtensions != NULL){
644 ext = &SDO->ODExtensions[SDO->entryNo];
645 }
646
647 CO_LOCK_OD();
648
649 /* copy data from OD to SDO buffer if not domain */
650 if(ODdata != NULL){
651 while(length--) *(SDObuffer++) = *(ODdata++);
652 }
653 /* if domain, Object dictionary function MUST exist */
654 else{
655 if(ext->pODFunc == NULL){
656 CO_UNLOCK_OD();
657 return CO_SDO_AB_DEVICE_INCOMPAT; /* general internal incompatibility in the device */
658 }
659 }
660
661 /* call Object dictionary function if registered */
662 SDO->ODF_arg.reading = true;
663 if(ext->pODFunc != NULL){
664 uint32_t abortCode = ext->pODFunc(&SDO->ODF_arg);
665 if(abortCode != 0U){
666 CO_UNLOCK_OD();
667 return abortCode;
668 }
669
670 /* dataLength (upadted by pODFunc) must be inside limits */
671 if((SDO->ODF_arg.dataLength == 0U) || (SDO->ODF_arg.dataLength > SDOBufferSize)){
672 CO_UNLOCK_OD();
673 return CO_SDO_AB_DEVICE_INCOMPAT; /* general internal incompatibility in the device */
674 }
675 }
676
677 CO_UNLOCK_OD();
678
679 SDO->ODF_arg.offset += SDO->ODF_arg.dataLength;
680 SDO->ODF_arg.firstSegment = false;
681
682 /* swap data if processor is not little endian (CANopen is) */
683 #ifdef CO_BIG_ENDIAN
684 if((SDO->ODF_arg.attribute & CO_ODA_MB_VALUE) != 0){
685 uint16_t len = SDO->ODF_arg.dataLength;
686 uint8_t *buf1 = SDO->ODF_arg.data;
687 uint8_t *buf2 = buf1 + len - 1;
688
689 len /= 2;
690 while(len--){
691 uint8_t b = *buf1;
692 *(buf1++) = *buf2;
693 *(buf2--) = b;
694 }
695 }
696 #endif
697
698 return 0U;
699 }
700
701
702 /******************************************************************************/
CO_SDO_writeOD(CO_SDO_t * SDO,uint16_t length)703 uint32_t CO_SDO_writeOD(CO_SDO_t *SDO, uint16_t length){
704 uint8_t *SDObuffer = SDO->ODF_arg.data;
705 uint8_t *ODdata = (uint8_t*)SDO->ODF_arg.ODdataStorage;
706 bool_t exception_1003 = false;
707
708 /* is object writeable? */
709 if((SDO->ODF_arg.attribute & CO_ODA_WRITEABLE) == 0){
710 return CO_SDO_AB_READONLY; /* attempt to write a read-only object */
711 }
712
713 /* length of domain data is application specific and not verified */
714 if(ODdata == 0){
715 SDO->ODF_arg.dataLength = length;
716 }
717
718 /* verify length except for domain data type */
719 else if(SDO->ODF_arg.dataLength != length){
720 return CO_SDO_AB_TYPE_MISMATCH; /* Length of service parameter does not match */
721 }
722
723 /* swap data if processor is not little endian (CANopen is) */
724 #ifdef CO_BIG_ENDIAN
725 if((SDO->ODF_arg.attribute & CO_ODA_MB_VALUE) != 0){
726 uint16_t len = SDO->ODF_arg.dataLength;
727 uint8_t *buf1 = SDO->ODF_arg.data;
728 uint8_t *buf2 = buf1 + len - 1;
729
730 len /= 2;
731 while(len--){
732 uint8_t b = *buf1;
733 *(buf1++) = *buf2;
734 *(buf2--) = b;
735 }
736 }
737 #endif
738
739 CO_LOCK_OD();
740
741 /* call Object dictionary function if registered */
742 SDO->ODF_arg.reading = false;
743 if(SDO->ODExtensions != NULL){
744 CO_OD_extension_t *ext = &SDO->ODExtensions[SDO->entryNo];
745
746 if(ext->pODFunc != NULL){
747 uint32_t abortCode = ext->pODFunc(&SDO->ODF_arg);
748 if(abortCode != 0U){
749 CO_UNLOCK_OD();
750 return abortCode;
751 }
752 }
753 }
754 SDO->ODF_arg.offset += SDO->ODF_arg.dataLength;
755 SDO->ODF_arg.firstSegment = false;
756
757 /* Special exception: 1003,00 is writable from network, but not in OD */
758 if(SDO->ODF_arg.index == 0x1003 && SDO->ODF_arg.subIndex == 0) {
759 exception_1003 = true;
760 }
761
762 /* copy data from SDO buffer to OD if not domain */
763 if((ODdata != NULL) && !exception_1003){
764 while(length--){
765 *(ODdata++) = *(SDObuffer++);
766 }
767 }
768
769 CO_UNLOCK_OD();
770
771 return 0;
772 }
773
774 /******************************************************************************/
CO_SDO_process_done(CO_SDO_t * SDO,uint16_t * timerNext_ms)775 static void CO_SDO_process_done(CO_SDO_t *SDO, uint16_t *timerNext_ms) {
776 #if CO_SDO_RX_DATA_SIZE > 1
777 uint8_t proc = SDO->CANrxProc;
778 uint8_t newProc = proc;
779
780 /* check if buffer needs to be free */
781 if (!IS_CANrxNew(SDO->CANrxNew[proc])){
782 return;
783 }
784
785 if (++newProc >= CO_SDO_RX_DATA_SIZE)
786 newProc = 0;
787
788 SDO->CANrxProc = newProc;
789 CLEAR_CANrxNew(SDO->CANrxNew[proc]);
790
791 if ((timerNext_ms != NULL) && (IS_CANrxNew(SDO->CANrxNew[newProc]))){
792 /* Set timerNext_ms to 0 to inform OS to call CO_SDO_process function again without delay */
793 timerNext_ms = 0;
794 }
795 #else
796 (void)(timerNext_ms);
797 CLEAR_CANrxNew(SDO->CANrxNew[0]);
798 #endif
799 }
800
801 /******************************************************************************/
CO_SDO_abort(CO_SDO_t * SDO,uint32_t code)802 static void CO_SDO_abort(CO_SDO_t *SDO, uint32_t code){
803 SDO->CANtxBuff->data[0] = 0x80;
804 SDO->CANtxBuff->data[1] = SDO->ODF_arg.index & 0xFF;
805 SDO->CANtxBuff->data[2] = (SDO->ODF_arg.index>>8) & 0xFF;
806 SDO->CANtxBuff->data[3] = SDO->ODF_arg.subIndex;
807 CO_memcpySwap4(&SDO->CANtxBuff->data[4], &code);
808 SDO->state = CO_SDO_ST_IDLE;
809
810 /* skip all received message in queue if any */
811 while (IS_CANrxNew(SDO->CANrxNew[SDO->CANrxProc]))
812 CO_SDO_process_done(SDO, NULL);
813
814 CO_CANsend(SDO->CANdevTx, SDO->CANtxBuff);
815 }
816
817 /******************************************************************************/
CO_SDO_process(CO_SDO_t * SDO,bool_t NMTisPreOrOperational,uint16_t timeDifference_ms,uint16_t SDOtimeoutTime,uint16_t * timerNext_ms)818 int8_t CO_SDO_process(
819 CO_SDO_t *SDO,
820 bool_t NMTisPreOrOperational,
821 uint16_t timeDifference_ms,
822 uint16_t SDOtimeoutTime,
823 uint16_t *timerNext_ms)
824 {
825 CO_SDO_state_t state = CO_SDO_ST_IDLE;
826 bool_t sendResponse = false;
827 uint8_t proc, *CANrxData;
828 bool_t isNew;
829
830 proc = SDO->CANrxProc;
831 isNew = IS_CANrxNew(SDO->CANrxNew[proc]);
832
833 /* return if idle */
834 if((SDO->state == CO_SDO_ST_IDLE) && (!isNew)){
835 return 0;
836 }
837
838 /* SDO is allowed to work only in operational or pre-operational NMT state */
839 if(!NMTisPreOrOperational){
840 SDO->state = CO_SDO_ST_IDLE;
841
842 /* free receive buffer if it is not empty */
843 CO_SDO_process_done(SDO, timerNext_ms);
844 return 0;
845 }
846
847 CANrxData = SDO->CANrxData[proc];
848
849 /* Is something new to process? */
850 if((!SDO->CANtxBuff->bufferFull) && (isNew || (SDO->state == CO_SDO_ST_UPLOAD_BL_SUBBLOCK))){
851 /* reset timeout */
852 if(SDO->state != CO_SDO_ST_UPLOAD_BL_SUBBLOCK)
853 SDO->timeoutTimer = 0;
854
855 /* clear response buffer */
856 SDO->CANtxBuff->data[0] = SDO->CANtxBuff->data[1] = SDO->CANtxBuff->data[2] = SDO->CANtxBuff->data[3] = 0;
857 SDO->CANtxBuff->data[4] = SDO->CANtxBuff->data[5] = SDO->CANtxBuff->data[6] = SDO->CANtxBuff->data[7] = 0;
858
859 /* Is abort from client? */
860 if(isNew && (CANrxData[0] == CCS_ABORT)){
861 SDO->state = CO_SDO_ST_IDLE;
862 CO_SDO_process_done(SDO, timerNext_ms);
863 return -1;
864 }
865
866 /* continue with previous SDO communication or start new */
867 if(SDO->state != CO_SDO_ST_IDLE){
868 state = SDO->state;
869 }
870 else{
871 uint32_t abortCode;
872 uint16_t index;
873 uint8_t CCS = CANrxData[0] >> 5; /* Client command specifier */
874
875 /* Is client command specifier valid */
876 if((CCS != CCS_DOWNLOAD_INITIATE) && (CCS != CCS_UPLOAD_INITIATE) &&
877 (CCS != CCS_DOWNLOAD_BLOCK) && (CCS != CCS_UPLOAD_BLOCK)){
878 CO_SDO_abort(SDO, CO_SDO_AB_CMD);/* Client command specifier not valid or unknown. */
879 return -1;
880 }
881
882 /* init ODF_arg */
883 index = CANrxData[2];
884 index = index << 8 | CANrxData[1];
885 abortCode = CO_SDO_initTransfer(SDO, index, CANrxData[3]);
886 if(abortCode != 0U){
887 CO_SDO_abort(SDO, abortCode);
888 return -1;
889 }
890
891 /* download */
892 if((CCS == CCS_DOWNLOAD_INITIATE) || (CCS == CCS_DOWNLOAD_BLOCK)){
893 if((SDO->ODF_arg.attribute & CO_ODA_WRITEABLE) == 0U){
894 CO_SDO_abort(SDO, CO_SDO_AB_READONLY); /* attempt to write a read-only object */
895 return -1;
896 }
897
898 /* set state machine to normal or block download */
899 if(CCS == CCS_DOWNLOAD_INITIATE){
900 state = CO_SDO_ST_DOWNLOAD_INITIATE;
901 }
902 else{
903 state = CO_SDO_ST_DOWNLOAD_BL_INITIATE;
904 }
905 }
906
907 /* upload */
908 else{
909 abortCode = CO_SDO_readOD(SDO, CO_SDO_BUFFER_SIZE);
910 if(abortCode != 0U){
911 CO_SDO_abort(SDO, abortCode);
912 return -1;
913 }
914
915 /* if data size is large enough set state machine to block upload, otherwise set to normal transfer */
916 if((CCS == CCS_UPLOAD_BLOCK) && (SDO->ODF_arg.dataLength > CANrxData[5])){
917 state = CO_SDO_ST_UPLOAD_BL_INITIATE;
918 }
919 else{
920 state = CO_SDO_ST_UPLOAD_INITIATE;
921 }
922 }
923 }
924 }
925
926 /* verify SDO timeout */
927 if(SDO->timeoutTimer < SDOtimeoutTime){
928 SDO->timeoutTimer += timeDifference_ms;
929 }
930 if(SDO->timeoutTimer >= SDOtimeoutTime){
931 if((SDO->state == CO_SDO_ST_DOWNLOAD_BL_SUBBLOCK) && (!SDO->timeoutSubblockDownolad) && (!SDO->CANtxBuff->bufferFull)){
932 /* set indication timeout in sub-block transfer and reset timeout */
933 SDO->timeoutSubblockDownolad = true;
934 SDO->timeoutTimer = 0;
935 /* send response without resetting sequence */
936 state = CO_SDO_ST_DOWNLOAD_BL_SUB_RESP_2;
937 }
938 else{
939 CO_SDO_abort(SDO, CO_SDO_AB_TIMEOUT); /* SDO protocol timed out */
940 return -1;
941 }
942 }
943
944 /* return immediately if still idle */
945 if(state == CO_SDO_ST_IDLE){
946 return 0;
947 }
948
949 /* state machine (buffer is freed with process_done() at the end) */
950 switch(state){
951 uint32_t abortCode;
952 uint16_t len, i;
953 bool_t lastSegmentInSubblock;
954
955 case CO_SDO_ST_DOWNLOAD_INITIATE:{
956 /* default response */
957 SDO->CANtxBuff->data[0] = 0x60;
958 SDO->CANtxBuff->data[1] = CANrxData[1];
959 SDO->CANtxBuff->data[2] = CANrxData[2];
960 SDO->CANtxBuff->data[3] = CANrxData[3];
961
962 /* Expedited transfer */
963 if((CANrxData[0] & 0x02U) != 0U){
964 /* is size indicated? Get message length */
965 if((CANrxData[0] & 0x01U) != 0U){
966 len = 4U - ((CANrxData[0] >> 2U) & 0x03U);
967 }
968 else{
969 len = SDO->ODF_arg.dataLength;
970 }
971
972 /* copy data to SDO buffer */
973 SDO->ODF_arg.data[0] = CANrxData[4];
974 SDO->ODF_arg.data[1] = CANrxData[5];
975 SDO->ODF_arg.data[2] = CANrxData[6];
976 SDO->ODF_arg.data[3] = CANrxData[7];
977
978 /* write data to the Object dictionary */
979 abortCode = CO_SDO_writeOD(SDO, len);
980 if(abortCode != 0U){
981 CO_SDO_abort(SDO, abortCode);
982 return -1;
983 }
984
985 /* finish the communication */
986 SDO->state = CO_SDO_ST_IDLE;
987 sendResponse = true;
988 }
989
990 /* Segmented transfer */
991 else{
992 /* verify length if size is indicated */
993 if((CANrxData[0]&0x01) != 0){
994 uint32_t lenRx;
995 CO_memcpySwap4(&lenRx, &CANrxData[4]);
996 SDO->ODF_arg.dataLengthTotal = lenRx;
997
998 /* verify length except for domain data type */
999 if((lenRx != SDO->ODF_arg.dataLength) && (SDO->ODF_arg.ODdataStorage != 0)){
1000 CO_SDO_abort(SDO, CO_SDO_AB_TYPE_MISMATCH); /* Length of service parameter does not match */
1001 return -1;
1002 }
1003 }
1004 SDO->bufferOffset = 0U;
1005 SDO->sequence = 0U;
1006 SDO->state = CO_SDO_ST_DOWNLOAD_SEGMENTED;
1007 sendResponse = true;
1008 }
1009 break;
1010 }
1011
1012 case CO_SDO_ST_DOWNLOAD_SEGMENTED:{
1013 /* verify client command specifier */
1014 if((CANrxData[0]&0xE0) != 0x00U){
1015 CO_SDO_abort(SDO, CO_SDO_AB_CMD);/* Client command specifier not valid or unknown. */
1016 return -1;
1017 }
1018
1019 /* verify toggle bit */
1020 i = (CANrxData[0]&0x10U) ? 1U : 0U;
1021 if(i != SDO->sequence){
1022 CO_SDO_abort(SDO, CO_SDO_AB_TOGGLE_BIT);/* toggle bit not alternated */
1023 return -1;
1024 }
1025
1026 /* get size of data in message */
1027 len = 7U - ((CANrxData[0] >> 1U) & 0x07U);
1028
1029 /* verify length. Domain data type enables length larger than SDO buffer size */
1030 if((SDO->bufferOffset + len) > SDO->ODF_arg.dataLength){
1031 if(SDO->ODF_arg.ODdataStorage != 0){
1032 CO_SDO_abort(SDO, CO_SDO_AB_DATA_LONG); /* Length of service parameter too high */
1033 return -1;
1034 }
1035 else{
1036 /* empty buffer in domain data type */
1037 SDO->ODF_arg.lastSegment = false;
1038 abortCode = CO_SDO_writeOD(SDO, SDO->bufferOffset);
1039 if(abortCode != 0U){
1040 CO_SDO_abort(SDO, abortCode);
1041 return -1;
1042 }
1043
1044 SDO->ODF_arg.dataLength = CO_SDO_BUFFER_SIZE;
1045 SDO->bufferOffset = 0U;
1046 }
1047 }
1048
1049 /* copy data to buffer */
1050 for(i=0U; i<len; i++)
1051 SDO->ODF_arg.data[SDO->bufferOffset++] = CANrxData[i+1];
1052
1053 /* If no more segments to be downloaded, write data to the Object dictionary */
1054 if((CANrxData[0] & 0x01U) != 0U){
1055 SDO->ODF_arg.lastSegment = true;
1056 abortCode = CO_SDO_writeOD(SDO, SDO->bufferOffset);
1057 if(abortCode != 0U){
1058 CO_SDO_abort(SDO, abortCode);
1059 return -1;
1060 }
1061
1062 /* finish */
1063 SDO->state = CO_SDO_ST_IDLE;
1064 }
1065
1066 /* download segment response and alternate toggle bit */
1067 SDO->CANtxBuff->data[0] = 0x20 | (SDO->sequence ? 0x10 : 0x00);
1068 SDO->sequence = (SDO->sequence) ? 0 : 1;
1069 sendResponse = true;
1070 break;
1071 }
1072
1073 case CO_SDO_ST_DOWNLOAD_BL_INITIATE:{
1074 /* verify client command specifier and subcommand */
1075 if((CANrxData[0]&0xE1U) != 0xC0U){
1076 CO_SDO_abort(SDO, CO_SDO_AB_CMD);/* Client command specifier not valid or unknown. */
1077 return -1;
1078 }
1079
1080 /* prepare response */
1081 SDO->CANtxBuff->data[0] = 0xA4;
1082 SDO->CANtxBuff->data[1] = CANrxData[1];
1083 SDO->CANtxBuff->data[2] = CANrxData[2];
1084 SDO->CANtxBuff->data[3] = CANrxData[3];
1085
1086 /* blksize */
1087 SDO->blksize = (CO_SDO_BUFFER_SIZE > (7*127)) ? 127 : (CO_SDO_BUFFER_SIZE / 7);
1088 SDO->CANtxBuff->data[4] = SDO->blksize;
1089
1090 /* is CRC enabled */
1091 SDO->crcEnabled = (CANrxData[0] & 0x04) ? true : false;
1092 SDO->crc = 0;
1093
1094 /* verify length if size is indicated */
1095 if((CANrxData[0]&0x02) != 0U){
1096 uint32_t lenRx;
1097 CO_memcpySwap4(&lenRx, &CANrxData[4]);
1098 SDO->ODF_arg.dataLengthTotal = lenRx;
1099
1100 /* verify length except for domain data type */
1101 if((lenRx != SDO->ODF_arg.dataLength) && (SDO->ODF_arg.ODdataStorage != 0)){
1102 CO_SDO_abort(SDO, CO_SDO_AB_TYPE_MISMATCH); /* Length of service parameter does not match */
1103 return -1;
1104 }
1105 }
1106
1107 SDO->bufferOffset = 0U;
1108 SDO->sequence = 0U;
1109 SDO->timeoutSubblockDownolad = false;
1110 SDO->state = CO_SDO_ST_DOWNLOAD_BL_SUBBLOCK;
1111
1112 /* send response */
1113 sendResponse = true;
1114 break;
1115 }
1116
1117 case CO_SDO_ST_DOWNLOAD_BL_SUBBLOCK:{
1118 /* data are copied directly in receive function */
1119 break;
1120 }
1121
1122 case CO_SDO_ST_DOWNLOAD_BL_SUB_RESP:
1123 case CO_SDO_ST_DOWNLOAD_BL_SUB_RESP_2:{
1124 /* check if last segment received */
1125 lastSegmentInSubblock = (!SDO->timeoutSubblockDownolad &&
1126 ((CANrxData[0] & 0x80U) == 0x80U)) ? true : false;
1127
1128 /* prepare response */
1129 SDO->CANtxBuff->data[0] = 0xA2;
1130 SDO->CANtxBuff->data[1] = SDO->sequence;
1131
1132 /* reset sequence on reception break */
1133 if (state == CO_SDO_ST_DOWNLOAD_BL_SUB_RESP)
1134 SDO->sequence = 0U;
1135
1136 /* empty buffer in domain data type if not last segment */
1137 if((SDO->ODF_arg.ODdataStorage == 0) && (SDO->bufferOffset != 0) && !lastSegmentInSubblock){
1138 /* calculate CRC on next bytes, if enabled */
1139 if(SDO->crcEnabled){
1140 SDO->crc = crc16_ccitt(SDO->ODF_arg.data, SDO->bufferOffset, SDO->crc);
1141 }
1142
1143 /* write data to the Object dictionary */
1144 SDO->ODF_arg.lastSegment = false;
1145 abortCode = CO_SDO_writeOD(SDO, SDO->bufferOffset);
1146 if(abortCode != 0U){
1147 CO_SDO_abort(SDO, abortCode);
1148 return -1;
1149 }
1150
1151 SDO->ODF_arg.dataLength = CO_SDO_BUFFER_SIZE;
1152 SDO->bufferOffset = 0U;
1153 }
1154
1155 /* blksize */
1156 len = CO_SDO_BUFFER_SIZE - SDO->bufferOffset;
1157 SDO->blksize = (len > (7*127)) ? 127 : (len / 7);
1158 SDO->CANtxBuff->data[2] = SDO->blksize;
1159
1160 /* set next state */
1161 if(lastSegmentInSubblock) {
1162 SDO->state = CO_SDO_ST_DOWNLOAD_BL_END;
1163 }
1164 else if(SDO->bufferOffset >= CO_SDO_BUFFER_SIZE) {
1165 CO_SDO_abort(SDO, CO_SDO_AB_DEVICE_INCOMPAT);
1166 return -1;
1167 }
1168 else {
1169 SDO->state = CO_SDO_ST_DOWNLOAD_BL_SUBBLOCK;
1170 }
1171
1172 /* send response */
1173 sendResponse = true;
1174
1175 break;
1176 }
1177
1178 case CO_SDO_ST_DOWNLOAD_BL_END:{
1179 /* verify client command specifier and subcommand */
1180 if((CANrxData[0]&0xE1U) != 0xC1U){
1181 CO_SDO_abort(SDO, CO_SDO_AB_CMD);/* Client command specifier not valid or unknown. */
1182 return -1;
1183 }
1184
1185 /* number of bytes in the last segment of the last block that do not contain data. */
1186 len = (CANrxData[0]>>2U) & 0x07U;
1187 SDO->bufferOffset -= len;
1188
1189 /* calculate and verify CRC, if enabled */
1190 if(SDO->crcEnabled){
1191 uint16_t crc;
1192 SDO->crc = crc16_ccitt(SDO->ODF_arg.data, SDO->bufferOffset, SDO->crc);
1193
1194 CO_memcpySwap2(&crc, &CANrxData[1]);
1195
1196 if(SDO->crc != crc){
1197 CO_SDO_abort(SDO, CO_SDO_AB_CRC); /* CRC error (block mode only). */
1198 return -1;
1199 }
1200 }
1201
1202 /* write data to the Object dictionary */
1203 SDO->ODF_arg.lastSegment = true;
1204 abortCode = CO_SDO_writeOD(SDO, SDO->bufferOffset);
1205 if(abortCode != 0U){
1206 CO_SDO_abort(SDO, abortCode);
1207 return -1;
1208 }
1209
1210 /* send response */
1211 SDO->CANtxBuff->data[0] = 0xA1;
1212 SDO->state = CO_SDO_ST_IDLE;
1213 sendResponse = true;
1214 break;
1215 }
1216
1217 case CO_SDO_ST_UPLOAD_INITIATE:{
1218 /* default response */
1219 SDO->CANtxBuff->data[1] = CANrxData[1];
1220 SDO->CANtxBuff->data[2] = CANrxData[2];
1221 SDO->CANtxBuff->data[3] = CANrxData[3];
1222
1223 /* Expedited transfer */
1224 if(SDO->ODF_arg.dataLength <= 4U){
1225 for(i=0U; i<SDO->ODF_arg.dataLength; i++)
1226 SDO->CANtxBuff->data[4U+i] = SDO->ODF_arg.data[i];
1227
1228 SDO->CANtxBuff->data[0] = 0x43U | ((4U-SDO->ODF_arg.dataLength) << 2U);
1229 SDO->state = CO_SDO_ST_IDLE;
1230
1231 sendResponse = true;
1232 }
1233
1234 /* Segmented transfer */
1235 else{
1236 SDO->bufferOffset = 0U;
1237 SDO->sequence = 0U;
1238 SDO->state = CO_SDO_ST_UPLOAD_SEGMENTED;
1239
1240 /* indicate data size, if known */
1241 if(SDO->ODF_arg.dataLengthTotal != 0U){
1242 uint32_t dlentot = SDO->ODF_arg.dataLengthTotal;
1243 CO_memcpySwap4(&SDO->CANtxBuff->data[4], &dlentot);
1244 SDO->CANtxBuff->data[0] = 0x41U;
1245 }
1246 else{
1247 SDO->CANtxBuff->data[0] = 0x40U;
1248 }
1249
1250 /* send response */
1251 sendResponse = true;
1252 }
1253 break;
1254 }
1255
1256 case CO_SDO_ST_UPLOAD_SEGMENTED:{
1257 /* verify client command specifier */
1258 if((CANrxData[0]&0xE0U) != 0x60U){
1259 CO_SDO_abort(SDO, CO_SDO_AB_CMD);/* Client command specifier not valid or unknown. */
1260 return -1;
1261 }
1262
1263 /* verify toggle bit */
1264 i = ((CANrxData[0]&0x10U) != 0) ? 1U : 0U;
1265 if(i != SDO->sequence){
1266 CO_SDO_abort(SDO, CO_SDO_AB_TOGGLE_BIT);/* toggle bit not alternated */
1267 return -1;
1268 }
1269
1270 /* calculate length to be sent */
1271 len = SDO->ODF_arg.dataLength - SDO->bufferOffset;
1272 if(len > 7U) len = 7U;
1273
1274 /* If data type is domain, re-fill the data buffer if neccessary and indicated so. */
1275 if((SDO->ODF_arg.ODdataStorage == 0) && (len < 7U) && (!SDO->ODF_arg.lastSegment)){
1276 /* copy previous data to the beginning */
1277 for(i=0U; i<len; i++){
1278 SDO->ODF_arg.data[i] = SDO->ODF_arg.data[SDO->bufferOffset+i];
1279 }
1280
1281 /* move the beginning of the data buffer */
1282 SDO->ODF_arg.data += len;
1283 SDO->ODF_arg.dataLength = CO_OD_getLength(SDO, SDO->entryNo, SDO->ODF_arg.subIndex) - len;
1284
1285 /* read next data from Object dictionary function */
1286 abortCode = CO_SDO_readOD(SDO, CO_SDO_BUFFER_SIZE);
1287 if(abortCode != 0U){
1288 CO_SDO_abort(SDO, abortCode);
1289 return -1;
1290 }
1291
1292 /* return to the original data buffer */
1293 SDO->ODF_arg.data -= len;
1294 SDO->ODF_arg.dataLength += len;
1295 SDO->bufferOffset = 0;
1296
1297 /* re-calculate the length */
1298 len = SDO->ODF_arg.dataLength;
1299 if(len > 7U) len = 7U;
1300 }
1301
1302 /* fill response data bytes */
1303 for(i=0U; i<len; i++)
1304 SDO->CANtxBuff->data[i+1] = SDO->ODF_arg.data[SDO->bufferOffset++];
1305
1306 /* first response byte */
1307 SDO->CANtxBuff->data[0] = 0x00 | (SDO->sequence ? 0x10 : 0x00) | ((7-len)<<1);
1308 SDO->sequence = (SDO->sequence) ? 0 : 1;
1309
1310 /* verify end of transfer */
1311 if((SDO->bufferOffset == SDO->ODF_arg.dataLength) && (SDO->ODF_arg.lastSegment)){
1312 SDO->CANtxBuff->data[0] |= 0x01;
1313 SDO->state = CO_SDO_ST_IDLE;
1314 }
1315
1316 /* send response */
1317 sendResponse = true;
1318 break;
1319 }
1320
1321 case CO_SDO_ST_UPLOAD_BL_INITIATE:{
1322 /* default response */
1323 SDO->CANtxBuff->data[1] = CANrxData[1];
1324 SDO->CANtxBuff->data[2] = CANrxData[2];
1325 SDO->CANtxBuff->data[3] = CANrxData[3];
1326
1327 /* calculate CRC, if enabled */
1328 if((CANrxData[0] & 0x04U) != 0U){
1329 SDO->crcEnabled = true;
1330 SDO->crc = crc16_ccitt(SDO->ODF_arg.data, SDO->ODF_arg.dataLength, 0);
1331 }
1332 else{
1333 SDO->crcEnabled = false;
1334 SDO->crc = 0;
1335 }
1336
1337 /* Number of segments per block */
1338 SDO->blksize = CANrxData[4];
1339
1340 /* verify client subcommand */
1341 if((CANrxData[0]&0x03U) != 0x00U){
1342 CO_SDO_abort(SDO, CO_SDO_AB_CMD);/* Client command specifier not valid or unknown. */
1343 return -1;
1344 }
1345
1346 /* verify blksize and if SDO data buffer is large enough */
1347 if((SDO->blksize < 1U) || (SDO->blksize > 127U) ||
1348 (((SDO->blksize*7U) > SDO->ODF_arg.dataLength) && (!SDO->ODF_arg.lastSegment))){
1349 CO_SDO_abort(SDO, CO_SDO_AB_BLOCK_SIZE); /* Invalid block size (block mode only). */
1350 return -1;
1351 }
1352
1353 /* indicate data size, if known */
1354 if(SDO->ODF_arg.dataLengthTotal != 0U){
1355 uint32_t dlentot = SDO->ODF_arg.dataLengthTotal;
1356 CO_memcpySwap4(&SDO->CANtxBuff->data[4], &dlentot);
1357 SDO->CANtxBuff->data[0] = 0xC6U;
1358 }
1359 else{
1360 SDO->CANtxBuff->data[0] = 0xC4U;
1361 }
1362
1363 /* send response */
1364 SDO->state = CO_SDO_ST_UPLOAD_BL_INITIATE_2;
1365 sendResponse = true;
1366 break;
1367 }
1368
1369 case CO_SDO_ST_UPLOAD_BL_INITIATE_2:{
1370 /* verify client command specifier and subcommand */
1371 if((CANrxData[0]&0xE3U) != 0xA3U){
1372 CO_SDO_abort(SDO, CO_SDO_AB_CMD);/* Client command specifier not valid or unknown. */
1373 return -1;
1374 }
1375
1376 SDO->bufferOffset = 0U;
1377 SDO->sequence = 0U;
1378 SDO->endOfTransfer = false;
1379 CO_SDO_process_done(SDO, timerNext_ms);
1380 isNew = false;
1381 SDO->state = CO_SDO_ST_UPLOAD_BL_SUBBLOCK;
1382 /* continue in next case */
1383 }
1384 // fallthrough
1385
1386 case CO_SDO_ST_UPLOAD_BL_SUBBLOCK:{
1387 /* is block confirmation received */
1388 if(isNew){
1389 uint8_t ackseq;
1390 uint16_t j;
1391
1392 /* verify client command specifier and subcommand */
1393 if((CANrxData[0]&0xE3U) != 0xA2U){
1394 CO_SDO_abort(SDO, CO_SDO_AB_CMD);/* Client command specifier not valid or unknown. */
1395 return -1;
1396 }
1397
1398 ackseq = CANrxData[1]; /* sequence number of the last segment, that was received correctly. */
1399
1400 /* verify if response is too early */
1401 if(ackseq > SDO->sequence){
1402 CO_SDO_abort(SDO, CO_SDO_AB_SEQ_NUM); /* Invalid sequence */
1403 return -1;
1404 }
1405
1406 /* end of transfer */
1407 if((SDO->endOfTransfer) && (ackseq == SDO->blksize)){
1408 /* first response byte */
1409 SDO->CANtxBuff->data[0] = 0xC1 | ((7 - SDO->lastLen) << 2);
1410
1411 /* CRC */
1412 if(SDO->crcEnabled)
1413 CO_memcpySwap2(&SDO->CANtxBuff->data[1], &SDO->crc);
1414
1415 SDO->state = CO_SDO_ST_UPLOAD_BL_END;
1416
1417 /* send response */
1418 sendResponse = true;
1419 break;
1420 }
1421
1422 /* move remaining data to the beginning */
1423 for(i=ackseq*7, j=0; i<SDO->ODF_arg.dataLength; i++, j++)
1424 SDO->ODF_arg.data[j] = SDO->ODF_arg.data[i];
1425
1426 /* set remaining data length in buffer */
1427 SDO->ODF_arg.dataLength -= ackseq * 7U;
1428
1429 /* new block size */
1430 SDO->blksize = CANrxData[2];
1431
1432 /* If data type is domain, re-fill the data buffer if necessary and indicated so. */
1433 if((SDO->ODF_arg.ODdataStorage == 0) && (SDO->ODF_arg.dataLength < (SDO->blksize*7U)) && (!SDO->ODF_arg.lastSegment)){
1434 /* move the beginning of the data buffer */
1435 len = SDO->ODF_arg.dataLength; /* length of valid data in buffer */
1436 SDO->ODF_arg.data += len;
1437 SDO->ODF_arg.dataLength = CO_OD_getLength(SDO, SDO->entryNo, SDO->ODF_arg.subIndex) - len;
1438
1439 /* read next data from Object dictionary function */
1440 abortCode = CO_SDO_readOD(SDO, CO_SDO_BUFFER_SIZE);
1441 if(abortCode != 0U){
1442 CO_SDO_abort(SDO, abortCode);
1443 return -1;
1444 }
1445
1446 /* calculate CRC on next bytes, if enabled */
1447 if(SDO->crcEnabled){
1448 SDO->crc = crc16_ccitt(SDO->ODF_arg.data, SDO->ODF_arg.dataLength, SDO->crc);
1449 }
1450
1451 /* return to the original data buffer */
1452 SDO->ODF_arg.data -= len;
1453 SDO->ODF_arg.dataLength += len;
1454 }
1455
1456 /* verify if SDO data buffer is large enough */
1457 if(((SDO->blksize*7U) > SDO->ODF_arg.dataLength) && (!SDO->ODF_arg.lastSegment)){
1458 CO_SDO_abort(SDO, CO_SDO_AB_BLOCK_SIZE); /* Invalid block size (block mode only). */
1459 return -1;
1460 }
1461
1462 SDO->bufferOffset = 0U;
1463 SDO->sequence = 0U;
1464 SDO->endOfTransfer = false;
1465 }
1466
1467 /* return, if all segments was already transfered or on end of transfer */
1468 if((SDO->sequence == SDO->blksize) || (SDO->endOfTransfer)){
1469 break;
1470 }
1471
1472 /* reset timeout */
1473 SDO->timeoutTimer = 0;
1474
1475 /* calculate length to be sent */
1476 len = SDO->ODF_arg.dataLength - SDO->bufferOffset;
1477 if(len > 7U){
1478 len = 7U;
1479 }
1480
1481 /* fill response data bytes */
1482 for(i=0U; i<len; i++){
1483 SDO->CANtxBuff->data[i+1] = SDO->ODF_arg.data[SDO->bufferOffset++];
1484 }
1485
1486 /* first response byte */
1487 SDO->CANtxBuff->data[0] = ++SDO->sequence;
1488
1489 /* verify end of transfer */
1490 if((SDO->bufferOffset == SDO->ODF_arg.dataLength) && (SDO->ODF_arg.lastSegment)){
1491 SDO->CANtxBuff->data[0] |= 0x80;
1492 SDO->lastLen = len;
1493 SDO->blksize = SDO->sequence;
1494 SDO->endOfTransfer = true;
1495 }
1496
1497 /* send response */
1498 sendResponse = true;
1499
1500 /* Set timerNext_ms to 0 to inform OS to call this function again without delay. */
1501 if(timerNext_ms != NULL){
1502 *timerNext_ms = 0;
1503 }
1504
1505 break;
1506 }
1507
1508 case CO_SDO_ST_UPLOAD_BL_END:{
1509 /* verify client command specifier */
1510 if((CANrxData[0]&0xE1U) != 0xA1U){
1511 CO_SDO_abort(SDO, CO_SDO_AB_CMD);/* Client command specifier not valid or unknown. */
1512 return -1;
1513 }
1514
1515 SDO->state = CO_SDO_ST_IDLE;
1516 break;
1517 }
1518
1519 case CO_SDO_ST_IDLE:
1520 {
1521 /* Nothing to do it seems */
1522 break;
1523 }
1524
1525 default:{
1526 CO_SDO_abort(SDO, CO_SDO_AB_DEVICE_INCOMPAT);/* general internal incompatibility in the device */
1527 return -1;
1528 }
1529 }
1530
1531 /* free receive buffer if it is not empty */
1532 CO_SDO_process_done(SDO, timerNext_ms);
1533
1534 /* send message */
1535 if(sendResponse) {
1536 CO_CANsend(SDO->CANdevTx, SDO->CANtxBuff);
1537 }
1538
1539 if(SDO->state != CO_SDO_ST_IDLE){
1540 return 1;
1541 }
1542
1543 return 0;
1544 }
1545