Dawn Framework 1.0
Universal data acquisition framework for embedded systems
nxscope.cxx
1// dawn/src/proto/nxscope/nxscope.cxx
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5
6#include "dawn/proto/nxscope/nxscope.hxx"
7
8#include <algorithm>
9#include <cstring>
10#include <new>
11#include <time.h>
12
13#include "dawn/io/common.hxx"
14#include "dawn/io/ddata.hxx"
15
16using namespace dawn;
17
18#if defined(CONFIG_DAWN_PROTO_NXSCOPE_SAMPLE_THREAD) + defined(CONFIG_DAWN_IO_NOTIFY) != 1
19# error one sampling method can be supported
20#endif
21
22// doStop() stops threadRecv() before the transport - needs non-blocking recv
23
24static_assert(NXSCOPE_RECV_NONBLOCK == 1, "doStop() relies on non-blocking recv");
25
26// Interval in us for sample thread
27
28#define NXSCOPE_SAMPLE_INTERVAL (1000000)
29
30static inline uint16_t nxscopeU16Le(const uint8_t *p)
31{
32 return (uint16_t)p[0] | ((uint16_t)p[1] << 8);
33}
34
35static inline uint32_t nxscopeU32Le(const uint8_t *p)
36{
37 return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
38}
39
40#ifdef CONFIG_DAWN_IO_NOTIFY
41int CProtoNxscope::ioNotifierCb(void *priv, io_ddata_t *data)
42{
43 SProtoNxscopeIochan *chan = (SProtoNxscopeIochan *)priv;
44 int ret;
45
46 if (!chan || !chan->obj || !data || !chan->put)
47 {
48 return -EINVAL;
49 }
50
51 {
52 std::lock_guard<std::recursive_mutex> lock(chan->obj->streamLock);
53
54 ret = chan->put(&chan->obj->nxs, chan->chan, data, chan->dim);
55 }
56
57 // Wake threadRecv to flush this batch (cheap signal, not a flush).
58
59 sem_post(&chan->obj->streamSem);
60
61 return ret;
62}
63#endif
64
65int CProtoNxscope::sendAck(int ret)
66{
67 size_t len;
68
69 if (nxs.proto_cmd == nullptr || nxs.intf_cmd == nullptr)
70 {
71 return -EINVAL;
72 }
73
74 if (nxs.proto_cmd->ops == nullptr || nxs.intf_cmd->ops == nullptr)
75 {
76 return -EINVAL;
77 }
78
79 if (nxs.proto_cmd->ops->frame_final == nullptr || nxs.intf_cmd->ops->send == nullptr)
80 {
81 return -EINVAL;
82 }
83
84 if (nxs.txbuf == nullptr)
85 {
86 return -EINVAL;
87 }
88
89 if (nxs.txbuf_len < nxs.proto_cmd->hdrlen + sizeof(ret) + nxs.proto_cmd->footlen)
90 {
91 return -ENOBUFS;
92 }
93
94 len = nxs.proto_cmd->hdrlen;
95 std::memcpy(&nxs.txbuf[len], &ret, sizeof(ret));
96 len += sizeof(ret);
97
98 ret = nxs.proto_cmd->ops->frame_final(nxs.proto_cmd, NXSCOPE_HDRID_ACK, nxs.txbuf, &len);
99 if (ret < 0)
100 {
101 return ret;
102 }
103
104 return nxs.intf_cmd->ops->send(nxs.intf_cmd, nxs.txbuf, (int)len);
105}
106
107int CProtoNxscope::userIdCb(void *priv, uint8_t id, uint8_t *buff)
108{
109 CProtoNxscope *obj;
110 int ret;
111 bool handled;
112
113 obj = static_cast<CProtoNxscope *>(priv);
114 if (obj == nullptr || buff == nullptr)
115 {
116 return -EINVAL;
117 }
118
119 handled = (id == NXSCOPE_USER_SET_IO || id == NXSCOPE_USER_SET_IO_SEEK ||
120 id == NXSCOPE_USER_GET_IO || id == NXSCOPE_USER_GET_IO_SEEK);
121 if (!handled)
122 {
123 /* Ignore non-extension IDs routed through userid callback.
124 * In particular, ACK frames may be seen here and must not be ACKed again.
125 */
126 return OK;
127 }
128
129 ret = obj->handleUserCommand(id, buff);
130
131#ifdef CONFIG_LOGGING_NXSCOPE_ACKFRAMES
132 {
133 int ack;
134
135 ack = obj->sendAck(ret);
136 if (ack < 0)
137 {
138 DAWNERR("nxscope user ack failed: %d\n", ack);
139 return ack;
140 }
141 }
142#endif
143
144 return ret;
145}
146
147int CProtoNxscope::handleUserCommand(uint8_t id, uint8_t *buff)
148{
149 switch (id)
150 {
151 case NXSCOPE_USER_SET_IO:
152 {
153 return userSetIO(buff);
154 }
155
156 case NXSCOPE_USER_SET_IO_SEEK:
157 {
158 return userSetIOSeek(buff);
159 }
160
161 case NXSCOPE_USER_GET_IO:
162 {
163 return userGetIO(buff);
164 }
165
166 case NXSCOPE_USER_GET_IO_SEEK:
167 {
168 return userGetIOSeek(buff);
169 }
170
171 default:
172 {
173 return -ENOTSUP;
174 }
175 }
176}
177
178CProtoNxscope::SProtoNxscopeIochan *CProtoNxscope::findIochan(SObjectId::ObjectId objid)
179{
180 for (auto &iochan : vio)
181 {
182 if (iochan.io != nullptr && iochan.io->getIdV() == objid)
183 {
184 return &iochan;
185 }
186 }
187
188 return nullptr;
189}
190
191int CProtoNxscope::userSetIO(uint8_t *buff)
192{
194 uint16_t size;
195 SProtoNxscopeIochan *iochan;
196 CIOCommon *io;
197 io_ddata_t *iodata;
198 size_t oldSize;
199 int ret;
200
201 objid = nxscopeU32Le(buff);
202 size = nxscopeU16Le(&buff[4]);
203
204 iochan = findIochan(objid);
205 if (iochan == nullptr || iochan->io == nullptr)
206 {
207 return -ENOENT;
208 }
209
210 io = iochan->io;
211 if (!io->isWrite())
212 {
213 return -EPERM;
214 }
215
216 iodata = iochan->setData;
217 if (iodata == nullptr)
218 {
219 return -ENOMEM;
220 }
221
222 if (!io->isSeekable())
223 {
224 if (size != io->getDataSize())
225 {
226 return -EINVAL;
227 }
228
229 std::memcpy(iodata->getDataPtr(), &buff[6], size);
230 ret = io->setData(*iodata);
231 }
232 else
233 {
234 if (size > iodata->getDataSize())
235 {
236 return -EINVAL;
237 }
238
239 std::memcpy(iodata->getDataPtr(), &buff[6], size);
240
241 oldSize = iodata->N;
242 iodata->N = size;
243 ret = io->setData(*iodata);
244 iodata->N = oldSize;
245 }
246
247 return ret;
248}
249
250int CProtoNxscope::userSetIOSeek(uint8_t *buff)
251{
253 size_t offset;
254 uint16_t size;
255 SProtoNxscopeIochan *iochan;
256 CIOCommon *io;
257 io_ddata_t *iodata;
258 size_t oldSize;
259 int ret;
260
261 objid = nxscopeU32Le(buff);
262 offset = nxscopeU32Le(&buff[4]);
263 size = nxscopeU16Le(&buff[8]);
264
265 iochan = findIochan(objid);
266 if (iochan == nullptr || iochan->io == nullptr)
267 {
268 return -ENOENT;
269 }
270
271 io = iochan->io;
272 if (!io->isWrite())
273 {
274 return -EPERM;
275 }
276
277 if (!io->isSeekable())
278 {
279 return -ENOTSUP;
280 }
281
282 iodata = iochan->setData;
283 if (iodata == nullptr)
284 {
285 return -ENOMEM;
286 }
287
288 if (size > iodata->getDataSize())
289 {
290 return -EINVAL;
291 }
292
293 std::memcpy(iodata->getDataPtr(), &buff[10], size);
294
295 oldSize = iodata->N;
296 iodata->N = size;
297 ret = io->setData(*iodata, offset);
298 iodata->N = oldSize;
299
300 return ret;
301}
302
303int CProtoNxscope::userGetIO(uint8_t *buff)
304{
306 SProtoNxscopeIochan *iochan;
307 CIOCommon *io;
308 io_ddata_t *iodata;
309 int ret;
310
311 objid = nxscopeU32Le(buff);
312
313 iochan = findIochan(objid);
314 if (iochan == nullptr || iochan->io == nullptr)
315 {
316 return -ENOENT;
317 }
318
319 io = iochan->io;
320 if (!io->isRead())
321 {
322 return -EPERM;
323 }
324
325 if (io->isSeekable())
326 {
327 return -ENOTSUP;
328 }
329
330 if (iochan->getData == nullptr)
331 {
332 iochan->getData = io->ddata_alloc(1);
333 if (iochan->getData == nullptr)
334 {
335 return -ENOMEM;
336 }
337 }
338
339 iodata = iochan->getData;
340 ret = io->getData(*iodata, 1);
341 if (ret < 0)
342 {
343 return ret;
344 }
345
346 ret = sendUserData(objid, iodata->getDataPtr(), (uint16_t)io->getDataSize());
347 if (ret < 0)
348 {
349 return ret;
350 }
351
352 // A positive value is the intf send byte count - the user command ACK
353 // must carry a plain status.
354
355 return OK;
356}
357
358int CProtoNxscope::userGetIOSeek(uint8_t *buff)
359{
361 size_t offset;
362 uint16_t size;
363 SProtoNxscopeIochan *iochan;
364 CIOCommon *io;
365 io_ddata_t *iodata;
366 int ret;
367
368 objid = nxscopeU32Le(buff);
369 offset = nxscopeU32Le(&buff[4]);
370 size = nxscopeU16Le(&buff[8]);
371
372 iochan = findIochan(objid);
373 if (iochan == nullptr || iochan->io == nullptr)
374 {
375 return -ENOENT;
376 }
377
378 io = iochan->io;
379 if (!io->isRead())
380 {
381 return -EPERM;
382 }
383
384 if (!io->isSeekable())
385 {
386 return -ENOTSUP;
387 }
388
389 if (iochan->getData == nullptr)
390 {
391 iochan->getData = io->ddata_alloc(1, CONFIG_DAWN_PROTO_NXSCOPE_RXBUF_LEN);
392 if (iochan->getData == nullptr)
393 {
394 return -ENOMEM;
395 }
396 }
397
398 iodata = iochan->getData;
399 if (size > iodata->getDataSize())
400 {
401 return -EINVAL;
402 }
403
404 // A window past the end would ship a stale tail (short reads are silent)
405
406 if (io->getDataSize() != 0 && offset + size > io->getDataSize())
407 {
408 return -EINVAL;
409 }
410
411 ret = io->getData(*iodata, 1, offset);
412 if (ret < 0)
413 {
414 return ret;
415 }
416
417 ret = sendUserData(objid, iodata->getDataPtr(), size);
418 if (ret < 0)
419 {
420 return ret;
421 }
422
423 return OK;
424}
425
428int CProtoNxscope::sendUserData(SObjectId::ObjectId objid, const void *data, uint16_t size)
429{
430 size_t len;
431 int ret;
432
433 if (nxs.proto_cmd == nullptr || nxs.intf_cmd == nullptr || nxs.proto_cmd->ops == nullptr ||
434 nxs.intf_cmd->ops == nullptr || nxs.proto_cmd->ops->frame_final == nullptr ||
435 nxs.intf_cmd->ops->send == nullptr || userTxBuf == nullptr)
436 {
437 return -EINVAL;
438 }
439
440 if (userTxLen < nxs.proto_cmd->hdrlen + NXSCOPE_USER_DATA_HDR + size + nxs.proto_cmd->footlen)
441 {
442 return -ENOBUFS;
443 }
444
445 len = nxs.proto_cmd->hdrlen;
446 userTxBuf[len++] = (uint8_t)(objid & 0xff);
447 userTxBuf[len++] = (uint8_t)((objid >> 8) & 0xff);
448 userTxBuf[len++] = (uint8_t)((objid >> 16) & 0xff);
449 userTxBuf[len++] = (uint8_t)((objid >> 24) & 0xff);
450 userTxBuf[len++] = (uint8_t)(size & 0xff);
451 userTxBuf[len++] = (uint8_t)((size >> 8) & 0xff);
452 std::memcpy(&userTxBuf[len], data, size);
453 len += size;
454
455 ret = nxs.proto_cmd->ops->frame_final(nxs.proto_cmd, NXSCOPE_USER_GET_IO, userTxBuf, &len);
456 if (ret < 0)
457 {
458 return ret;
459 }
460
461 return nxs.intf_cmd->ops->send(nxs.intf_cmd, userTxBuf, (int)len);
462}
463
464int CProtoNxscope::configureNxscope()
465{
466 int ret;
467
468 // Default serial protocol implementation
469
470 ret = nxscope_proto_ser_init(&nxsProto, nullptr);
471 if (ret != OK)
472 {
473 DAWNERR("nxscope_proto_ser_init failed: %d\n", ret);
474 return ret;
475 }
476
477 // Connect callbacks
478
479 nxsCbs.userid_priv = this;
480 nxsCbs.userid = userIdCb;
481 nxsCbs.start_priv = nullptr;
482 nxsCbs.start = nullptr;
483
484 // Initialize nxscope
485
486 nxsCfg.streambuf_len = CONFIG_DAWN_PROTO_NXSCOPE_STREAMBUF_LEN;
487 nxsCfg.rxbuf_len = CONFIG_DAWN_PROTO_NXSCOPE_RXBUF_LEN;
488 nxsCfg.rx_padding = CONFIG_DAWN_PROTO_NXSCOPE_RX_PADDING;
489#ifdef CONFIG_LOGGING_NXSCOPE_CRICHANNELS
490 nxsCfg.cribuf_len = CONFIG_DAWN_PROTO_NXSCOPE_CRIBUF_LEN;
491#endif
492
493 nxsCfg.intf_cmd = &nxsIntf;
494 nxsCfg.intf_stream = &nxsIntf;
495 nxsCfg.proto_cmd = &nxsProto;
496 nxsCfg.proto_stream = &nxsProto;
497 nxsCfg.callbacks = &nxsCbs;
498 nxsCfg.channels = vchannels.size();
499
500 ret = nxscope_init(&nxs, &nxsCfg);
501 if (ret != OK)
502 {
503 DAWNERR("nxscope_init failed: %d\n", ret);
504 return ret;
505 }
506
507 return OK;
508}
509
510int CProtoNxscope::allocObject(SProtoNxscopeIOBind *alloc)
511{
512 DAWNINFO("allocate object 0x%" PRIx32 "\n", alloc->objid.v);
513
514 // Allocate object in map
515
516 setObjectMapItem(alloc->objid.v, nullptr);
517
518 // Store reference to chanel configuration
519
520 vchannels.push_back(alloc);
521
522 return OK;
523};
524
525int CProtoNxscope::allocNames(size_t j, SProtoNxscopeNames *alloc)
526{
527 DAWNINFO("channel %zu with name %s\n", j, alloc->name);
528
529 // Store reference to chanel configuration
530
531 vnames.push_back(alloc);
532
533 return OK;
534};
535
536uint8_t CProtoNxscope::getChannelDim(const CIOCommon &io)
537{
538 return io.getDataDim();
539}
540
541uint8_t CProtoNxscope::getChannelDtype(const CIOCommon &io)
542{
543 uint8_t dtype = io.getDtype();
544
545 switch (dtype)
546 {
547#ifdef CONFIG_DAWN_DTYPE_UINT8
549 {
550 return NXSCOPE_TYPE_UINT8;
551 }
552#endif
553
554#ifdef CONFIG_DAWN_DTYPE_INT8
556 {
557 return NXSCOPE_TYPE_INT8;
558 }
559#endif
560
561#ifdef CONFIG_DAWN_DTYPE_UINT16
563 {
564 return NXSCOPE_TYPE_UINT16;
565 }
566#endif
567
568#ifdef CONFIG_DAWN_DTYPE_INT16
570 {
571 return NXSCOPE_TYPE_INT16;
572 }
573#endif
574
575#ifdef CONFIG_DAWN_DTYPE_INT32
577 {
578 return NXSCOPE_TYPE_INT32;
579 }
580#endif
581
582#ifdef CONFIG_DAWN_DTYPE_UINT32
584 {
585 return NXSCOPE_TYPE_UINT32;
586 }
587#endif
588
589#ifdef CONFIG_DAWN_DTYPE_UINT64
591 {
592 return NXSCOPE_TYPE_UINT64;
593 }
594#endif
595
596#ifdef CONFIG_DAWN_DTYPE_FLOAT
598 {
599 return NXSCOPE_TYPE_FLOAT;
600 }
601#endif
602
603#ifdef CONFIG_DAWN_DTYPE_B16
605 {
606 return NXSCOPE_TYPE_B16;
607 }
608#endif
609
610#ifdef CONFIG_DAWN_DTYPE_UB16
612 {
613 return NXSCOPE_TYPE_UB16;
614 }
615#endif
616
617#ifdef CONFIG_DAWN_DTYPE_CHAR
619 {
620 return NXSCOPE_TYPE_CHAR;
621 }
622#endif
623
624 default:
625 {
626 DAWNERR("unsuported nxscope type %d\n", dtype);
627 return NXSCOPE_TYPE_NONE;
628 }
629 }
630}
631
632int CProtoNxscope::nxscopeChannelsCreate()
633{
634 union nxscope_chinfo_type_u u;
635 const SProtoNxscopeIOBind *alloc;
636 size_t bindidx;
637 size_t getmax = 0;
638 int chanid = 0;
639 int ret;
640
641 hasStreamChannels = false;
642
643 for (bindidx = 0; bindidx < vchannels.size(); bindidx++)
644 {
645 alloc = vchannels[bindidx];
646 const CIOCommon *io = getIO(alloc->objid.v);
647
648 if (io != nullptr)
649 {
650 SProtoNxscopeIochan iochan;
651 const char *name;
652
653 if (bindidx < vnames.size())
654 {
655 name = vnames[bindidx]->name;
656 }
657 else
658 {
659 name = "no-name";
660 }
661
662 DAWNINFO("nxscope channel %s %p 0x%x\n", name, io, alloc->objid.v);
663
664 // Store IO to nxscope chan map
665
666 iochan.chan = chanid;
667 iochan.dim = getChannelDim(*io);
668 iochan.stream = io->isRead();
669 iochan.io = (CIOCommon *)io;
670 iochan.obj = this;
671 iochan.setData = nullptr;
672 iochan.getData = nullptr;
673#ifdef CONFIG_DAWN_IO_NOTIFY
674 iochan.put = nullptr;
675#endif
676#ifdef CONFIG_DAWN_PROTO_NXSCOPE_SAMPLE_THREAD
677 iochan.sample = nullptr;
678 iochan.data = nullptr;
679#endif
680
681 if (io->isRead() == false && io->isWrite() == false)
682 {
683 DAWNERR("IO 0x%" PRIx32 " has neither read nor write support\n", alloc->objid.v);
684 return -EPERM;
685 }
686
687 // GET_IO returns the whole IO, GET_IO_SEEK at most one RXBUF chunk
688
689 if (io->isRead())
690 {
691 size_t n = io->getDataSize();
692
693 if (io->isSeekable())
694 {
695 n = std::min(n, (size_t)CONFIG_DAWN_PROTO_NXSCOPE_RXBUF_LEN);
696 }
697
698 getmax = std::max(getmax, n);
699 }
700
701 if (iochan.io->isWrite())
702 {
703 if (iochan.io->isSeekable())
704 {
705 iochan.setData = iochan.io->ddata_alloc(1, CONFIG_DAWN_PROTO_NXSCOPE_RXBUF_LEN);
706 }
707 else
708 {
709 iochan.setData = iochan.io->ddata_alloc(1);
710 }
711
712 if (iochan.setData == nullptr)
713 {
714 DAWNERR("failed to allocate set buffer for objid=0x%" PRIx32 "\n",
715 alloc->objid.v);
716#ifdef CONFIG_DAWN_PROTO_NXSCOPE_SAMPLE_THREAD
717 delete iochan.data;
718 iochan.data = nullptr;
719#endif
720 return -ENOMEM;
721 }
722 }
723
724 if (iochan.stream)
725 {
726 ret = bindChannelCallbacks(iochan, io->getDtype());
727 if (ret < 0)
728 {
729 if (iochan.io->isWrite())
730 {
731 DAWNINFO("set-only nxscope channel for objid=0x%" PRIx32 "\n",
732 alloc->objid.v);
733 iochan.stream = false;
734 }
735 else
736 {
737 DAWNERR("Unsupported data type %d for objid=0x%" PRIx32 "\n",
738 io->getDtype(),
739 alloc->objid.v);
740#ifdef CONFIG_DAWN_PROTO_NXSCOPE_SAMPLE_THREAD
741 delete iochan.data;
742 iochan.data = nullptr;
743#endif
744 delete iochan.setData;
745 iochan.setData = nullptr;
746 return ret;
747 }
748 }
749 }
750
751#ifdef CONFIG_DAWN_IO_NOTIFY
752 if (iochan.stream && iochan.io->isNotify() == false)
753 {
754 // Not streamable - still reachable with SET_IO / GET_IO
755
756 DAWNWARN("%s-only nxscope channel (no notify) for objid=0x%" PRIx32 "\n",
757 iochan.io->isWrite() ? "set" : "get",
758 alloc->objid.v);
759 iochan.stream = false;
760 }
761#endif
762
763#ifdef CONFIG_DAWN_PROTO_NXSCOPE_SAMPLE_THREAD
764 if (iochan.stream && iochan.io->isSeekable())
765 {
766 if (iochan.io->isWrite())
767 {
768 DAWNINFO("set-only nxscope channel (seekable) for "
769 "objid=0x%" PRIx32 "\n",
770 alloc->objid.v);
771 iochan.stream = false;
772 }
773 else
774 {
775 DAWNERR("seekable IO not supported by NXScope "
776 "(objid=0x%08" PRIx32 ")\n",
777 iochan.io->getIdV());
778 delete iochan.setData;
779 iochan.setData = nullptr;
780 return -ENOTSUP;
781 }
782 }
783
784 if (iochan.stream)
785 {
786 iochan.data = iochan.io->ddata_alloc(1);
787 if (iochan.data == nullptr)
788 {
789 DAWNERR("failed to allocate sample buffer for objid=0x%" PRIx32 "\n",
790 alloc->objid.v);
791 delete iochan.setData;
792 iochan.setData = nullptr;
793 return -ENOMEM;
794 }
795 }
796#endif
797
798 if (iochan.stream)
799 {
800 // Initialize nxscope stream channel
801 u.s.dtype = getChannelDtype(*io);
802 u.s._res = 0;
803 u.s.cri = 0;
804 // NOTE: nxscope_chan_init declares name as char* (non-const)
805 // but never modifies it. The char* in the struct is only
806 // stored and compared, so casting away const is safe here.
807 nxscope_chan_init(&nxs, chanid, const_cast<char *>(name), u.u8, iochan.dim, 0);
808 iochan.chan = chanid;
809 chanid += 1;
810 hasStreamChannels = true;
811 }
812 else
813 {
814 iochan.chan = 0;
815 }
816
817 vio.push_back(iochan);
818 }
819 else
820 {
821 DAWNERR("IO not found for objid=0x%" PRIx32 "\n", alloc->objid.v);
822 return -EIO;
823 }
824 }
825
826 // GET_IO response buffer: the lib txbuf only fits a CHINFO reply
827
828 delete[] userTxBuf;
829 userTxLen = nxsProto.hdrlen + NXSCOPE_USER_DATA_HDR + getmax + nxsProto.footlen;
830 userTxBuf = new (std::nothrow) uint8_t[userTxLen];
831 if (userTxBuf == nullptr)
832 {
833 userTxLen = 0;
834 return -ENOMEM;
835 }
836
837 return OK;
838};
839
840#ifdef CONFIG_DAWN_IO_NOTIFY
841template<typename T, CProtoNxscope::nxscope_put_t<T> Put>
842int CProtoNxscope::putBatch(struct nxscope_s *nxs, uint8_t chan, io_ddata_t *data, uint8_t dim)
843{
844 int ret = OK;
845
846 // Address each batch through the buffer so timestamp padding is honoured
847
848 for (size_t i = 0; i < data->getBatch() && ret >= 0; i++)
849 {
850 ret = Put(nxs, chan, static_cast<T *>(data->getDataPtr(i)), dim);
851 }
852
853 return ret;
854}
855#endif
856
857#ifdef CONFIG_DAWN_PROTO_NXSCOPE_SAMPLE_THREAD
858template<typename T, CProtoNxscope::nxscope_put_t<T> Put>
859int CProtoNxscope::sampleTyped(CProtoNxscope *obj, SProtoNxscopeIochan *iochan)
860{
861 int ret;
862
863 if (!iochan || !iochan->data)
864 {
865 return -EINVAL;
866 }
867
868 ret = iochan->io->getData(*iochan->data, 1);
869 if (ret == OK)
870 {
871 ret = Put(&obj->nxs, iochan->chan, static_cast<T *>(iochan->data->getDataPtr()), iochan->dim);
872 }
873
874 return ret;
875}
876#endif
877
878template<typename T, CProtoNxscope::nxscope_put_t<T> Put>
879void CProtoNxscope::bindTyped(SProtoNxscopeIochan &iochan)
880{
881#ifdef CONFIG_DAWN_IO_NOTIFY
882 iochan.put = &putBatch<T, Put>;
883#endif
884#ifdef CONFIG_DAWN_PROTO_NXSCOPE_SAMPLE_THREAD
885 iochan.sample = &sampleTyped<T, Put>;
886#endif
887}
888
889int CProtoNxscope::bindChannelCallbacks(SProtoNxscopeIochan &iochan, uint8_t dtype)
890{
891 switch (dtype)
892 {
893#ifdef CONFIG_DAWN_DTYPE_UINT8
895 {
896 bindTyped<uint8_t, nxscope_put_vuint8>(iochan);
897 break;
898 }
899#endif
900
901#ifdef CONFIG_DAWN_DTYPE_INT8
903 {
904 bindTyped<int8_t, nxscope_put_vint8>(iochan);
905 break;
906 }
907#endif
908
909#ifdef CONFIG_DAWN_DTYPE_UINT16
911 {
912 bindTyped<uint16_t, nxscope_put_vuint16>(iochan);
913 break;
914 }
915#endif
916
917#ifdef CONFIG_DAWN_DTYPE_INT16
919 {
920 bindTyped<int16_t, nxscope_put_vint16>(iochan);
921 break;
922 }
923#endif
924
925#ifdef CONFIG_DAWN_DTYPE_INT32
927 {
928 bindTyped<int32_t, nxscope_put_vint32>(iochan);
929 break;
930 }
931#endif
932
933#ifdef CONFIG_DAWN_DTYPE_UINT32
935 {
936 bindTyped<uint32_t, nxscope_put_vuint32>(iochan);
937 break;
938 }
939#endif
940
941#ifdef CONFIG_DAWN_DTYPE_UINT64
943 {
944 bindTyped<uint64_t, nxscope_put_vuint64>(iochan);
945 break;
946 }
947#endif
948
949#ifdef CONFIG_DAWN_DTYPE_FLOAT
951 {
952 bindTyped<float, nxscope_put_vfloat>(iochan);
953 break;
954 }
955#endif
956
957 default:
958 {
959 return -ENOTSUP;
960 }
961 }
962
963 return OK;
964}
965
966#ifdef CONFIG_DAWN_PROTO_NXSCOPE_SAMPLE_THREAD
967void CProtoNxscope::getAndPut(SProtoNxscopeIochan *iochan)
968{
969 int ret;
970
971 if (!iochan || !iochan->sample)
972 {
973 return;
974 }
975
976 std::lock_guard<std::recursive_mutex> lock(streamLock);
977
978 ret = iochan->sample(this, iochan);
979 if (ret < 0 && ret != -EAGAIN)
980 {
981 DAWNERR("sampling failed for chan=%d ret=%d\n", iochan->chan, ret);
982 }
983}
984#endif
985
986void CProtoNxscope::threadRecv()
987{
988 // Loop until stop called
989
990 do
991 {
992 bool waited = false;
993 int ret;
994
995 if (hasStreamChannels)
996 {
997#ifdef CONFIG_DAWN_IO_NOTIFY
998 struct timespec ts;
999
1000 /* Wait until the notifier signals a batch is ready, or a short
1001 * timeout elapses so client commands are still serviced when idle.
1002 * This paces flushing at the data rate instead of a fixed tick.
1003 */
1004
1005 clock_gettime(CLOCK_MONOTONIC, &ts);
1006 ts.tv_nsec += CONFIG_DAWN_PROTO_NXSCOPE_RECV_INTERVAL * 1000L;
1007 ts.tv_sec += ts.tv_nsec / 1000000000L;
1008 ts.tv_nsec %= 1000000000L;
1009
1010 sem_clockwait(&streamSem, CLOCK_MONOTONIC, &ts);
1011 waited = true;
1012
1013 /* Coalesce any further pending batches into this one flush */
1014
1015 while (sem_trywait(&streamSem) == 0)
1016 {
1017 }
1018#endif
1019
1020 /* Flush stream data */
1021
1022 std::lock_guard<std::recursive_mutex> lock(streamLock);
1023
1024 ret = nxscope_stream(&nxs);
1025 if (ret < 0)
1026 {
1027 DAWNERR("ERROR: nxscope_stream failed %d\n", ret);
1028 }
1029 }
1030
1031 /* Handle recv data - command handlers share streamLock with the
1032 * producers (see nxscope.hxx), which is only safe with non-blocking recv
1033 */
1034
1035 {
1036 std::lock_guard<std::recursive_mutex> lock(streamLock);
1037
1038 ret = nxscope_recv(&nxs);
1039 }
1040
1041 if (ret < 0)
1042 {
1043 DAWNERR("ERROR: nxscope_recv failed %d\n", ret);
1044 }
1045
1046 // Pace command polling when there is no stream to wait on
1047
1048 if (!waited)
1049 {
1050 usleep(CONFIG_DAWN_PROTO_NXSCOPE_RECV_INTERVAL);
1051 }
1052 }
1053 while (!threadRecvMember.shouldQuit());
1054
1055 // Mark thread quit done
1056
1057 threadRecvMember.markThreadFinished();
1058}
1059
1060#ifdef CONFIG_DAWN_PROTO_NXSCOPE_SAMPLE_THREAD
1061void CProtoNxscope::threadSample()
1062{
1063 // Loop until stop called
1064
1065 do
1066 {
1067 for (auto &io : vio)
1068 {
1069 if (!io.stream)
1070 {
1071 continue;
1072 }
1073 getAndPut(&io);
1074 }
1075
1076 usleep(NXSCOPE_SAMPLE_INTERVAL);
1077 }
1078 while (!threadSampleMember.shouldQuit());
1079
1080 // Mark thread quit done
1081
1082 threadSampleMember.markThreadFinished();
1083}
1084
1085#endif // CONFIG_DAWN_PROTO_NXSCOPE_SAMPLE_THREAD
1086
1087CProtoNxscope::~CProtoNxscope()
1088{
1089 for (auto &io : vio)
1090 {
1091#ifdef CONFIG_DAWN_PROTO_NXSCOPE_SAMPLE_THREAD
1092 delete io.data;
1093 io.data = nullptr;
1094#endif
1095 delete io.setData;
1096 io.setData = nullptr;
1097 delete io.getData;
1098 io.getData = nullptr;
1099 }
1100
1101 vchannels.clear();
1102 vnames.clear();
1103 vio.clear();
1104
1105 delete[] userTxBuf;
1106 userTxBuf = nullptr;
1107
1108#ifdef CONFIG_DAWN_IO_NOTIFY
1109 sem_destroy(&streamSem);
1110#endif
1111
1112 // Deinit nxscope
1113
1114 nxscope_deinit(&nxs);
1115}
1116
1118{
1119 int ret;
1120
1121 ret = initPriv();
1122 if (ret != OK)
1123 {
1124 return ret;
1125 }
1126
1127 return OK;
1128}
1129
1131{
1132 // Allocated objects are mapped after handler bind, so create channels here
1133 return nxscopeChannelsCreate();
1134}
1135
1137{
1138 int ret;
1139
1140 ret = deinitPriv();
1141 if (ret != OK)
1142 {
1143 return ret;
1144 }
1145
1146 return OK;
1147}
1148
1150{
1151 int ret;
1152
1153#ifdef CONFIG_DAWN_IO_NOTIFY
1154 // Register notifications first - an advertised stream channel that cannot
1155 // notify is a start failure, not a silent downgrade
1156
1157 for (auto &io : vio)
1158 {
1159 if (!io.stream)
1160 {
1161 continue;
1162 }
1163 ret = io.io->setNotifier(ioNotifierCb, 0, (void *)&io);
1164 if (ret < 0)
1165 {
1166 DAWNERR("set notifier failed for objId = 0x%" PRIx32 " (%d)\n", io.io->getIdV(), ret);
1167 return ret;
1168 }
1169 }
1170#endif
1171
1172 ret = startPriv();
1173 if (ret != OK)
1174 {
1175 return ret;
1176 }
1177
1178 // Start recv thread
1179
1180 threadRecvMember.setThreadFunc([this]() { threadRecv(); });
1181 ret = threadRecvMember.threadStart();
1182 if (ret < 0)
1183 {
1184 return ret;
1185 }
1186
1187#ifdef CONFIG_DAWN_PROTO_NXSCOPE_SAMPLE_THREAD
1188 // Start sample thread
1189
1190 threadSampleMember.setThreadFunc([this]() { threadSample(); });
1191 ret = threadSampleMember.threadStart();
1192 if (ret < 0)
1193 {
1194 return ret;
1195 }
1196#endif
1197
1198 return OK;
1199};
1200
1202{
1203 int ret;
1204
1205 // Stop recv thread first - recv is non-blocking, so the thread quits on
1206 // its own and can't touch the transport after it's torn down
1207
1208 threadRecvMember.threadStop();
1209
1210#ifdef CONFIG_DAWN_PROTO_NXSCOPE_SAMPLE_THREAD
1211 // Stop sample thread
1212
1213 threadSampleMember.threadStop();
1214#endif
1215
1216 // Now the transport is unused - tear it down
1217
1218 ret = stopPriv();
1219
1220 return ret;
1221};
1222
1224{
1225#ifdef CONFIG_DAWN_PROTO_NXSCOPE_SAMPLE_THREAD
1226 return !(threadRecvMember.isStopped() & threadSampleMember.isStopped());
1227#else
1228 return !threadRecvMember.isStopped();
1229#endif
1230}
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
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:418
int getData(IODataCmn &data, size_t len, size_t offset=0)
Get data from I/O (public interface with stats tracking).
Definition common.hxx:379
virtual bool isSeekable() const
Check if IO supports partial (seekable) access.
Definition common.hxx:528
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.
SObjectId::ObjectId getIdV() const
Get object identifier as raw 32-bit value.
Definition object.cxx:155
uint8_t getDtype() const
Get data type field.
Definition object.cxx:175
Real-time data visualization protocol (base class).
Definition nxscope.hxx:43
int doStop()
Stop implementation hook.
Definition nxscope.cxx:1201
bool hasThread() const
Check if a background thread is active.
Definition nxscope.cxx:1223
struct nxscope_intf_s nxsIntf
NXScope interface structure (from logging library).
Definition nxscope.hxx:90
int configure()
Configure object from descriptor data.
Definition nxscope.cxx:1117
int doStart()
Start implementation hook.
Definition nxscope.cxx:1149
int init()
One-time initialize object after bindings are resolved.
Definition nxscope.cxx:1130
int deinit()
De-initialize object.
Definition nxscope.cxx:1136
int threadStop()
Stop the worker thread.
Definition thread.cxx:246
int threadStart()
Start the worker thread.
Definition thread.cxx:172
void setThreadFunc(Func &&func)
Assign the function executed by threadStart().
Definition thread.hxx:100
Out-of-tree user-extension hooks for Dawn.
Definition bindable.hxx:13
@ DTYPE_FLOAT
IEEE 754 single-precision floating point (32-bit).
Definition objectid.hxx:112
@ DTYPE_INT32
Signed 32-bit integer (-2147483648 to 2147483647).
Definition objectid.hxx:92
@ DTYPE_UINT8
Unsigned 8-bit integer (0 to 255).
Definition objectid.hxx:80
@ DTYPE_INT16
Signed 16-bit integer (-32768 to 32767).
Definition objectid.hxx:84
@ DTYPE_INT8
Signed 8-bit integer (-128 to 127).
Definition objectid.hxx:76
@ DTYPE_UINT64
Unsigned 64-bit integer.
Definition objectid.hxx:104
@ DTYPE_UINT16
Unsigned 16-bit integer (0 to 65535).
Definition objectid.hxx:88
@ DTYPE_UB16
Unsigned 16.16 fixed-point (32-bit).
Definition objectid.hxx:132
@ DTYPE_CHAR
Character/string type (null-terminated, 4-byte aligned).
Definition objectid.hxx:144
@ DTYPE_UINT32
Unsigned 32-bit integer (0 to 4294967295).
Definition objectid.hxx:96
@ DTYPE_B16
Signed 16.16 fixed-point (32-bit).
Definition objectid.hxx:128
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
size_t getBatch()
Get number of batches.
Definition ddata.hxx:135