Dawn Framework 1.0
Universal data acquisition framework for embedded systems
simplebase.cxx
1// dawn/src/proto/simplebase.cxx
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5
6#include "dawn/proto/simplebase.hxx"
7
8#include <cstring>
9#include <new>
10
11#include "dawn/io/common.hxx"
12#include "dawn/io/sdata.hxx"
13
14using namespace dawn;
15
16static inline uint32_t extractU32LE(const uint8_t *p)
17{
18 return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
19}
20
21uint16_t CProtoSimpleBase::calculateCrc(const uint8_t *data, size_t len)
22{
23 uint16_t crc = 0xFFFF;
24
25 for (size_t i = 0; i < len; i++)
26 {
27 crc ^= (uint16_t)data[i] << 8;
28 for (int j = 0; j < 8; j++)
29 {
30 if (crc & 0x8000)
31 {
32 crc = (crc << 1) ^ 0x1021;
33 }
34 else
35 {
36 crc <<= 1;
37 }
38 }
39 }
40
41 return crc;
42}
43
44void CProtoSimpleBase::sendError(uint8_t error_code, uint8_t context)
45{
46 uint8_t payload[2] = {error_code, context};
47 sendFrame(CMD_ERROR, payload, 2);
48}
49
50#ifdef CONFIG_DAWN_IO_NOTIFY
51int CProtoSimpleBase::notifierCb(void *priv, io_ddata_t *data)
52{
53 SProtoSimpleNotify *ctx;
54 CProtoSimpleBase *proto;
55 CIOCommon *io;
57 uint8_t payload[4 + FRAME_MAX_PAYLOAD];
58 size_t data_size;
59
60 ctx = (SProtoSimpleNotify *)priv;
61 proto = ctx->proto;
62 io = ctx->io;
63 objid = ctx->objid;
64
65 {
66 std::lock_guard<std::mutex> lock(proto->subsMutex);
67 if (proto->subscriptions.find(objid) == proto->subscriptions.end())
68 {
69 return OK;
70 }
71 }
72
73 data_size = io->getDataSize();
74
75 if (data_size > FRAME_MAX_PAYLOAD - 4)
76 {
77 DAWNERR("Data too large for notification\n");
78 return -EINVAL;
79 }
80
81 payload[0] = (objid >> 0) & 0xFF;
82 payload[1] = (objid >> 8) & 0xFF;
83 payload[2] = (objid >> 16) & 0xFF;
84 payload[3] = (objid >> 24) & 0xFF;
85
86 std::memcpy(&payload[4], data->getDataPtr(), data_size);
87
88 return proto->sendFrame(CMD_NOTIFY, payload, 4 + data_size);
89}
90#endif
91
92void CProtoSimpleBase::cmdPing()
93{
94 sendFrame(CMD_PONG, nullptr, 0);
95}
96
97io_ddata_t *CProtoSimpleBase::findIOBuffer(SObjectId::ObjectId objid)
98{
99 for (auto *v : iobuffers)
100 {
101 if (objid == v->cfg->objid)
102 {
103 return v->iodata;
104 }
105 }
106
107 return nullptr;
108}
109
110void CProtoSimpleBase::cmdGetIO(const uint8_t *payload, size_t len)
111{
112 const uint8_t *response;
114 CIOCommon *io = NULL;
115 io_ddata_t *iodata = NULL;
116 size_t data_size;
117 int ret;
118
119 if (len < 4)
120 {
121 sendError(STATUS_INVALID_FORMAT, CMD_GET_IO);
122 return;
123 }
124
125 objid = extractU32LE(payload);
126
127 io = getIO(objid);
128 if (!io)
129 {
130 sendError(STATUS_INVALID_OBJ, CMD_GET_IO);
131 return;
132 }
133
134 if (!io->isRead())
135 {
136 sendError(STATUS_WRITE_ONLY, CMD_GET_IO);
137 return;
138 }
139
140 iodata = findIOBuffer(objid);
141 if (!iodata)
142 {
143 DAWNERR("Not found IO buffer\n");
144 sendError(STATUS_ERROR, CMD_GET_IO);
145 return;
146 }
147
148 ret = io->getData(*iodata, 1);
149 if (ret < 0)
150 {
151 DAWNERR("get data failed\n");
152 sendError(STATUS_ERROR, CMD_GET_IO);
153 return;
154 }
155
156 data_size = io->getDataSize();
157 if (data_size > FRAME_MAX_PAYLOAD)
158 {
159 sendError(STATUS_ERROR, CMD_GET_IO);
160 return;
161 }
162
163 response = static_cast<const uint8_t *>(iodata->getDataPtr());
164 sendFrame(CMD_GET_IO, response, data_size);
165}
166
167void CProtoSimpleBase::cmdSetIO(const uint8_t *payload, size_t len)
168{
170 CIOCommon *io = NULL;
171 io_ddata_t *iodata = NULL;
172 uint8_t status;
173 size_t size;
174 int ret;
175
176 if (len < 5)
177 {
178 sendError(STATUS_INVALID_FORMAT, CMD_SET_IO);
179 return;
180 }
181
182 objid = extractU32LE(payload);
183
184 io = getIO(objid);
185 if (!io)
186 {
187 sendError(STATUS_INVALID_OBJ, CMD_SET_IO);
188 return;
189 }
190
191 if (!io->isWrite())
192 {
193 sendError(STATUS_READ_ONLY, CMD_SET_IO);
194 return;
195 }
196
197 iodata = findIOBuffer(objid);
198 if (!iodata)
199 {
200 DAWNERR("Not found IO buffer\n");
201 sendError(STATUS_ERROR, CMD_SET_IO);
202 return;
203 }
204
205 if (io->isSeekable())
206 {
207 size_t old_size;
208
209 if (len <= 4)
210 {
211 sendError(STATUS_INVALID_FORMAT, CMD_SET_IO);
212 return;
213 }
214
215 size = len - 4;
216 if (size > iodata->getDataSize())
217 {
218 sendError(STATUS_ERROR, CMD_SET_IO);
219 return;
220 }
221
222 // Reuse the preallocated seek buffer, but expose only the received
223 // payload length to the seekable IO write handler.
224 old_size = iodata->N;
225 iodata->N = size;
226 std::memcpy(iodata->getDataPtr(), &payload[4], size);
227 ret = io->setData(*iodata);
228 iodata->N = old_size;
229 }
230 else
231 {
232 size = io->getDataSize();
233 if (len != 4 + size)
234 {
235 sendError(STATUS_ERROR, CMD_SET_IO);
236 return;
237 }
238
239 std::memcpy(iodata->getDataPtr(), &payload[4], size);
240 ret = io->setData(*iodata);
241 }
242
243 status = (ret == 0) ? STATUS_OK : STATUS_ERROR;
244 sendFrame(CMD_SET_IO, &status, 1);
245}
246
247void CProtoSimpleBase::cmdGetIOSeek(const uint8_t *payload, size_t len)
248{
249 uint8_t response[FRAME_MAX_PAYLOAD];
251 CIOCommon *io = NULL;
252 io_ddata_t *iodata = NULL;
253 size_t total;
254 size_t offset;
255 size_t avail;
256 size_t chunk;
257 int ret;
258
259 if (len < 8)
260 {
261 sendError(STATUS_INVALID_FORMAT, CMD_GET_IO_SEEK);
262 return;
263 }
264
265 objid = extractU32LE(payload);
266 offset = extractU32LE(&payload[4]);
267
268 io = getIO(objid);
269 if (!io)
270 {
271 sendError(STATUS_INVALID_OBJ, CMD_GET_IO_SEEK);
272 return;
273 }
274
275 if (!io->isRead())
276 {
277 sendError(STATUS_WRITE_ONLY, CMD_GET_IO_SEEK);
278 return;
279 }
280
281 if (!io->isSeekable())
282 {
283 sendError(STATUS_ERROR, CMD_GET_IO_SEEK);
284 return;
285 }
286
287 total = io->getDataSize();
288 if (offset >= total)
289 {
290 sendError(STATUS_INVALID_FORMAT, CMD_GET_IO_SEEK);
291 return;
292 }
293
294 iodata = findIOBuffer(objid);
295 if (!iodata)
296 {
297 DAWNERR("Not found IO buffer\n");
298 sendError(STATUS_ERROR, CMD_GET_IO_SEEK);
299 return;
300 }
301
302 ret = io->getData(*iodata, 1, offset);
303 if (ret < 0)
304 {
305 DAWNERR("get data seek failed %d\n", ret);
306 sendError(STATUS_ERROR, CMD_GET_IO_SEEK);
307 return;
308 }
309
310 avail = total - offset;
311 chunk = iodata->getDataSize() < avail ? iodata->getDataSize() : avail;
312
313 response[0] = (uint8_t)(objid & 0xFF);
314 response[1] = (uint8_t)((objid >> 8) & 0xFF);
315 response[2] = (uint8_t)((objid >> 16) & 0xFF);
316 response[3] = (uint8_t)((objid >> 24) & 0xFF);
317
318 response[4] = (uint8_t)(total & 0xFF);
319 response[5] = (uint8_t)((total >> 8) & 0xFF);
320 response[6] = (uint8_t)((total >> 16) & 0xFF);
321 response[7] = (uint8_t)((total >> 24) & 0xFF);
322
323 std::memcpy(&response[SEEK_HDR_SIZE], iodata->getDataPtr(), chunk);
324
325 sendFrame(CMD_GET_IO_SEEK, response, SEEK_HDR_SIZE + chunk);
326}
327
328void CProtoSimpleBase::cmdSetIOSeek(const uint8_t *payload, size_t len)
329{
331 CIOCommon *io = NULL;
332 io_ddata_t *iodata = NULL;
333 uint8_t status;
334 size_t offset;
335 size_t size;
336 size_t old_size;
337 int ret;
338
339 if (len < 9)
340 {
341 sendError(STATUS_INVALID_FORMAT, CMD_SET_IO_SEEK);
342 return;
343 }
344
345 objid = extractU32LE(payload);
346 offset = extractU32LE(&payload[4]);
347 size = len - 8;
348
349 io = getIO(objid);
350 if (!io)
351 {
352 sendError(STATUS_INVALID_OBJ, CMD_SET_IO_SEEK);
353 return;
354 }
355
356 if (!io->isWrite())
357 {
358 sendError(STATUS_READ_ONLY, CMD_SET_IO_SEEK);
359 return;
360 }
361
362 if (!io->isSeekable())
363 {
364 sendError(STATUS_ERROR, CMD_SET_IO_SEEK);
365 return;
366 }
367
368 iodata = findIOBuffer(objid);
369 if (!iodata)
370 {
371 DAWNERR("Not found IO buffer\n");
372 sendError(STATUS_ERROR, CMD_SET_IO_SEEK);
373 return;
374 }
375
376 if (size > iodata->getDataSize())
377 {
378 sendError(STATUS_ERROR, CMD_SET_IO_SEEK);
379 return;
380 }
381
382 old_size = iodata->N;
383 iodata->N = size;
384 std::memcpy(iodata->getDataPtr(), &payload[8], size);
385 ret = io->setData(*iodata, offset);
386 iodata->N = old_size;
387
388 status = (ret == 0) ? STATUS_OK : STATUS_ERROR;
389 sendFrame(CMD_SET_IO_SEEK, &status, 1);
390}
391
392void CProtoSimpleBase::cmdGetCfg(const uint8_t *payload, size_t len)
393{
395 uint8_t cfgid;
396 CIOCommon *io;
397
398 if (len < 5)
399 {
400 sendError(STATUS_INVALID_CFG, CMD_GET_CFG);
401 return;
402 }
403
404 objid = extractU32LE(payload);
405
406 cfgid = payload[4];
407 UNUSED(cfgid);
408
409 io = getIO(objid);
410 if (!io)
411 {
412 sendError(STATUS_INVALID_OBJ, CMD_GET_CFG);
413 return;
414 }
415
416 sendError(STATUS_INVALID_CFG, CMD_GET_CFG);
417}
418
419void CProtoSimpleBase::cmdSetCfg(const uint8_t *payload, size_t len)
420{
422 uint8_t cfgid;
423 CIOCommon *io;
424
425 if (len < 5)
426 {
427 sendError(STATUS_INVALID_CFG, CMD_SET_CFG);
428 return;
429 }
430
431 objid = extractU32LE(payload);
432
433 cfgid = payload[4];
434 UNUSED(cfgid);
435
436 io = getIO(objid);
437 if (!io)
438 {
439 sendError(STATUS_INVALID_OBJ, CMD_SET_CFG);
440 return;
441 }
442
443 sendError(STATUS_INVALID_CFG, CMD_SET_CFG);
444}
445
446void CProtoSimpleBase::cmdGetInfo(const uint8_t *payload, size_t len)
447{
449 uint8_t response[3];
450 CIOCommon *io;
451
452 if (len < 4)
453 {
454 sendError(STATUS_INVALID_FORMAT, CMD_GET_INFO);
455 return;
456 }
457
458 objid = extractU32LE(payload);
459
460 io = getIO(objid);
461 if (!io)
462 {
463 sendError(STATUS_INVALID_OBJ, CMD_GET_INFO);
464 return;
465 }
466
467 if (io->isRead() && io->isWrite())
468 {
469 response[0] = IO_TYPE_READ_WRITE;
470 }
471 else if (io->isRead())
472 {
473 response[0] = IO_TYPE_READ_ONLY;
474 }
475 else if (io->isWrite())
476 {
477 response[0] = IO_TYPE_WRITE_ONLY;
478 }
479 else
480 {
481 sendError(STATUS_INVALID_OBJ, CMD_GET_INFO);
482 return;
483 }
484
485 response[1] = (uint8_t)io->getDataDim();
486 response[2] = (uint8_t)io->getDtype();
487
488 sendFrame(CMD_GET_INFO, response, 3);
489}
490
491void CProtoSimpleBase::cmdListIOs()
492{
493 uint8_t response[2 + FRAME_MAX_PAYLOAD - 2];
494 size_t resp_len = 0;
495 uint16_t count = (uint16_t)getIOMap().size();
496
497 response[resp_len++] = (uint8_t)(count & 0xFF);
498 response[resp_len++] = (uint8_t)((count >> 8) & 0xFF);
499
500 for (const auto &entry : getIOMap())
501 {
503
504 if (resp_len + 4 > FRAME_MAX_PAYLOAD)
505 {
506 sendError(STATUS_ERROR, CMD_LIST_IOS);
507 return;
508 }
509
510 objid = entry.first;
511
512 response[resp_len++] = (uint8_t)(objid & 0xFF);
513 response[resp_len++] = (uint8_t)((objid >> 8) & 0xFF);
514 response[resp_len++] = (uint8_t)((objid >> 16) & 0xFF);
515 response[resp_len++] = (uint8_t)((objid >> 24) & 0xFF);
516 }
517
518 sendFrame(CMD_LIST_IOS, response, resp_len);
519}
520
521#ifdef CONFIG_DAWN_IO_NOTIFY
522void CProtoSimpleBase::cmdSubscribe(const uint8_t *payload, size_t len)
523{
525 CIOCommon *io;
526 uint8_t status;
527
528 if (len < 4)
529 {
530 sendError(STATUS_INVALID_FORMAT, CMD_SUBSCRIBE);
531 return;
532 }
533
534 objid = extractU32LE(payload);
535
536 io = getIO(objid);
537 if (!io)
538 {
539 sendError(STATUS_INVALID_OBJ, CMD_SUBSCRIBE);
540 return;
541 }
542
543 if (!io->isNotify())
544 {
545 sendError(STATUS_ERROR, CMD_SUBSCRIBE);
546 return;
547 }
548
549 {
550 std::lock_guard<std::mutex> lock(subsMutex);
551 subscriptions.insert(objid);
552 }
553
554 status = STATUS_OK;
555 sendFrame(CMD_SUBSCRIBE, &status, 1);
556}
557
558void CProtoSimpleBase::cmdUnsubscribe(const uint8_t *payload, size_t len)
559{
561 uint8_t status;
562
563 if (len < 4)
564 {
565 sendError(STATUS_INVALID_FORMAT, CMD_UNSUBSCRIBE);
566 return;
567 }
568
569 objid = extractU32LE(payload);
570
571 {
572 std::lock_guard<std::mutex> lock(subsMutex);
573 subscriptions.erase(objid);
574 }
575
576 status = STATUS_OK;
577 sendFrame(CMD_UNSUBSCRIBE, &status, 1);
578}
579#endif
580
581int CProtoSimpleBase::handleFrame(const uint8_t *frame, size_t len)
582{
583 const uint8_t *payload;
584 uint16_t payload_len;
585 uint8_t cmd;
586 uint16_t crc_calc;
587 uint16_t crc_recv;
588
589 if (len < FRAME_MIN_LEN)
590 {
591 return -1;
592 }
593
594 if (frame[0] != FRAME_SYNC)
595 {
596 return -1;
597 }
598
599 payload_len = (uint16_t)(frame[1] | (frame[2] << 8));
600 cmd = frame[3];
601
602 if (len != (size_t)(FRAME_MIN_LEN + payload_len))
603 {
604 return -1;
605 }
606
607 crc_calc = calculateCrc(&frame[3], 1 + payload_len);
608 crc_recv = (uint16_t)(frame[4 + payload_len] | (frame[5 + payload_len] << 8));
609
610 if (crc_calc != crc_recv)
611 {
612 return -1;
613 }
614
615 payload = &frame[4];
616
617 switch (cmd)
618 {
619 case CMD_PING:
620 {
621 cmdPing();
622 break;
623 }
624
625 case CMD_GET_IO:
626 {
627 cmdGetIO(payload, payload_len);
628 break;
629 }
630
631 case CMD_SET_IO:
632 {
633 cmdSetIO(payload, payload_len);
634 break;
635 }
636
637 case CMD_GET_IO_SEEK:
638 {
639 cmdGetIOSeek(payload, payload_len);
640 break;
641 }
642
643 case CMD_SET_IO_SEEK:
644 {
645 cmdSetIOSeek(payload, payload_len);
646 break;
647 }
648
649 case CMD_GET_CFG:
650 {
651 cmdGetCfg(payload, payload_len);
652 break;
653 }
654
655 case CMD_SET_CFG:
656 {
657 cmdSetCfg(payload, payload_len);
658 break;
659 }
660
661 case CMD_GET_INFO:
662 {
663 cmdGetInfo(payload, payload_len);
664 break;
665 }
666
667 case CMD_LIST_IOS:
668 {
669 cmdListIOs();
670 break;
671 }
672
673#ifdef CONFIG_DAWN_IO_NOTIFY
674 case CMD_SUBSCRIBE:
675 {
676 cmdSubscribe(payload, payload_len);
677 break;
678 }
679
680 case CMD_UNSUBSCRIBE:
681 {
682 cmdUnsubscribe(payload, payload_len);
683 break;
684 }
685#endif
686
687 default:
688 {
689 sendError(STATUS_INVALID_CMD, cmd);
690 break;
691 }
692 }
693
694 return 0;
695}
696
698{
699 DAWNINFO("allocate object 0x%" PRIx32 "\n", cfg->objid);
700
701 setObjectMapItem(cfg->objid, nullptr);
702 iobinds.push_back(cfg);
703}
704
706{
707 if (initialized)
708 {
709 return OK;
710 }
711
712 for (auto const *v : iobinds)
713 {
714 CIOCommon *io;
715 SProtoSimpleData *tmp;
716 io_ddata_t *iobuffer;
717
718 io = getIO(v->objid);
719 if (io == nullptr)
720 {
721 DAWNERR("Failed to get IO 0x%08" PRIx32 "\n", v->objid);
722 return -EIO;
723 }
724
725 tmp = new (std::nothrow) SProtoSimpleData[1]();
726 if (!tmp)
727 {
728 DAWNERR("failed to allocate data\n");
729 return -ENOMEM;
730 }
731
732 iobuffers.push_back(tmp);
733 tmp->cfg = v;
734
735 iobuffer = io->ddata_alloc(1, SEEK_CHUNK_CAP);
736 if (!iobuffer)
737 {
738 DAWNERR("failed to allocate register buffer\n");
739 return -ENOMEM;
740 }
741
742 tmp->iodata = iobuffer;
743 }
744
745 initialized = true;
746
747 return OK;
748}
749
751{
752 if (!initialized)
753 {
754 return OK;
755 }
756
757 for (auto *v : iobuffers)
758 {
759 free(v->iodata);
760 delete[] v;
761 }
762
763 iobinds.clear();
764 iobuffers.clear();
765 initialized = false;
766
767 return OK;
768}
769
770#ifdef CONFIG_DAWN_IO_NOTIFY
771int CProtoSimpleBase::setupNotifications()
772{
773 int ret;
774
775 for (auto *v : iobuffers)
776 {
777 CIOCommon *io;
778 SProtoSimpleNotify *ctx;
779
780 io = getIO(v->cfg->objid);
781 if (!io)
782 {
783 continue;
784 }
785
786 if (io->isNotify())
787 {
788 ctx = new (std::nothrow) SProtoSimpleNotify[1]();
789 if (!ctx)
790 {
791 DAWNERR("Failed to allocate notification context\n");
792 return -ENOMEM;
793 }
794
795 ctx->objid = v->cfg->objid;
796 ctx->io = io;
797 ctx->proto = this;
798
799 notifyContexts.push_back(ctx);
800
801 ret = io->setNotifier(notifierCb, 0, ctx);
802 if (ret < 0)
803 {
804 DAWNERR("setNotifier failed for objid=0x%" PRIx32 "\n", ctx->objid);
805 delete[] ctx;
806 notifyContexts.pop_back();
807 }
808 }
809 }
810
811 return OK;
812}
813
814void CProtoSimpleBase::cleanupNotifications()
815{
816 {
817 std::lock_guard<std::mutex> lock(subsMutex);
818 subscriptions.clear();
819 }
820}
821
822void CProtoSimpleBase::destroyNotifications()
823{
824 cleanupNotifications();
825
826 for (auto *ctx : notifyContexts)
827 {
828 delete[] ctx;
829 }
830
831 notifyContexts.clear();
832}
833#endif
CIOCommon * getIO(SObjectId::ObjectId id)
Get an I/O object by ID.
Definition bindable.cxx:41
void setObjectMapItem(SObjectId::ObjectId id, CObject *obj)
Set an item in the object map.
Definition bindable.cxx:23
const std::map< SObjectId::ObjectId, CIOCommon * > & getIOMap() const
Get the I/O map for this object.
Definition bindable.cxx:18
Base class for all I/O objects.
Definition common.hxx:27
virtual bool isWrite() const =0
Check if IO supports write operations.
int setData(IODataCmn &data, size_t offset=0)
Set data for I/O (public interface with stats tracking).
Definition common.hxx:392
int getData(IODataCmn &data, size_t len, size_t offset=0)
Get data from I/O (public interface with stats tracking).
Definition common.hxx:353
virtual bool isNotify() const =0
Check if IO supports notifications.
virtual bool isSeekable() const
Check if IO supports partial (seekable) access.
Definition common.hxx:502
virtual size_t getDataDim() const =0
Get data vector dimension.
io_ddata_t * ddata_alloc(size_t batch, size_t chunk_size=0)
Allocate data buffer for this I/O.
Definition common.cxx:247
virtual size_t getDataSize() const =0
Get data size in bytes.
virtual bool isRead() const =0
Check if IO supports read operations.
uint8_t getDtype() const
Get data type field.
Definition object.cxx:175
Shared base for simple framed protocols.
static uint8_t FRAME_SYNC
Frame structure constants.
void allocObject(SProtoSimpleIOBind *cfg)
Store an allocated IO binding.
uint16_t calculateCrc(const uint8_t *data, size_t len)
Calculate 16-bit CRC checksum.
int handleFrame(const uint8_t *frame, size_t len)
Process a received frame.
int createBuffers()
Allocate shared per-IO data buffers.
static size_t SEEK_HDR_SIZE
Seek response constants.
int destroyBuffers()
Destroy shared per-IO data buffers.
virtual int sendFrame(uint8_t cmd, const uint8_t *payload, size_t len)=0
Send a framed response via the derived transport.
Out-of-tree user-extension hooks for Dawn.
Definition bindable.hxx:13
Internal I/O data mapping for simple protocol handlers.
Configuration for binding an I/O object to a simple protocol.
uint32_t ObjectId
ObjectID type - single 32-bit value.
Definition objectid.hxx:44
Heap-allocated dynamic I/O data buffer.
Definition ddata.hxx:21
size_t getDataSize()
Get data size in bytes.
Definition ddata.hxx:153
void * getDataPtr(size_t batch=0)
Get pointer to data only (skips timestamp if present).
Definition ddata.hxx:180