Dawn Framework 1.0
Universal data acquisition framework for embedded systems
regs.cxx
1// dawn/src/proto/modbus/regs.cxx
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5
6#include "dawn/proto/modbus/regs.hxx"
7
8#include <cstring>
9#include <inttypes.h>
10#include <new>
11
12#include "dawn/io/common.hxx"
13#include "dawn/io/viewdata.hxx"
14
15using namespace dawn;
16
17static inline void modbus_set_bit(uint8_t *buf, uint16_t bit, uint8_t value)
18{
19 uint8_t *byte = &buf[bit / 8];
20 uint8_t mask = (uint8_t)(1u << (bit % 8));
21
22 if (value)
23 {
24 *byte |= mask;
25 }
26 else
27 {
28 *byte &= (uint8_t)~mask;
29 }
30}
31
32static inline uint8_t modbus_get_bit(const uint8_t *buf, uint16_t bit)
33{
34 return (uint8_t)((buf[bit / 8] >> (bit % 8)) & 0x1u);
35}
36
37static inline size_t modbus_seekable_window_regs(const CProtoModbusRegs::SProtoModbusRegs *reg)
38{
39 static constexpr size_t default_window = 8;
40 return reg->cfg->config > 0 ? reg->cfg->config : default_window;
41}
42
43#ifdef CONFIG_DAWN_PROTO_MODBUS_SEEKABLE
44CProtoModbusRegs::SSeekableState *CProtoModbusRegs::findSeekableState(SProtoModbusRegs *reg)
45{
46 for (auto *state : vseekable)
47 {
48 if (state->reg == reg)
49 {
50 return state;
51 }
52 }
53
54 return nullptr;
55}
56#endif
57
59CProtoModbusRegs::findGroup(uint16_t addr, uint16_t n, const std::vector<SProtoModbusRegs *> &regs)
60{
61 SProtoModbusRegs *reg = nullptr;
62
63 // Search from the end so later descriptor groups override earlier ones
64 // when address ranges overlap.
65
66 for (auto it = regs.rbegin(); it != regs.rend(); ++it)
67 {
68 auto *v = *it;
69
70 if (v == nullptr || v->cfg == nullptr)
71 {
72 continue;
73 }
74
75 // Found
76
77 if ((addr >= v->cfg->start) && ((addr + n) <= (v->cfg->start + v->regs)))
78 {
79 reg = v;
80 break;
81 }
82 }
83
84 return reg;
85}
86
87const CProtoModbusRegs::SProtoModbusRegs *CProtoModbusRegs::hasOverlap(
88 const std::vector<SProtoModbusRegs *> &regs,
89 uint32_t start,
90 size_t count) const
91{
92 uint32_t end = start + static_cast<uint32_t>(count);
93
94 for (auto const *existing : regs)
95 {
96 if (existing == nullptr || existing->cfg == nullptr)
97 {
98 continue;
99 }
100
101 uint32_t ex_start = existing->cfg->start;
102 uint32_t ex_end = ex_start + static_cast<uint32_t>(existing->regs);
103
104 if (start < ex_end && ex_start < end)
105 {
106 return existing;
107 }
108 }
109
110 return nullptr;
111}
112
113void CProtoModbusRegs::cleanupGroups(std::vector<SProtoModbusRegs *> &groups)
114{
115 for (auto *v : groups)
116 {
117 delete[] reinterpret_cast<uint8_t *>(v->data);
118
119 for (auto *b : v->iodata)
120 {
121 delete b;
122 }
123
124 v->iodata.clear();
125 delete v;
126 }
127
128 groups.clear();
129}
130
131static size_t getIoRegBytes(const CProtoModbusRegs::SProtoModbusRegs *reg, size_t index)
132{
134 {
135 return (modbus_seekable_window_regs(reg) + 1) * sizeof(uint16_t);
136 }
137
138 return reg->iodata[index]->getDataSize();
139}
140
141// Map a byte/bit offset in Modbus register space back to the bound IO and
142// offset inside that IO. Packed coil/discrete groups can expose one vector IO
143// as multiple Modbus bits, so the Modbus index is not always the IO index.
144
145static int findIoIndexByByteOffset(const CProtoModbusRegs::SProtoModbusRegs *reg,
146 int index,
147 size_t *offsetInIo)
148{
149 size_t regOffset = 0;
150
151 for (size_t j = 0; j < reg->iodata.size(); j++)
152 {
153 size_t bytes = getIoRegBytes(reg, j);
154
155 if (index >= static_cast<int>(regOffset) && index < static_cast<int>(regOffset + bytes))
156 {
157 *offsetInIo = static_cast<size_t>(index) - regOffset;
158 return static_cast<int>(j);
159 }
160
161 regOffset += bytes;
162 }
163
164 return -1;
165}
166
167#if defined(CONFIG_DAWN_PROTO_MODBUS_COIL) || defined(CONFIG_DAWN_PROTO_MODBUS_DISCRETE)
168int CProtoModbusRegs::regReadWriteCoil(uint8_t *buff,
169 uint16_t n,
170 int index,
171 bool read,
172 SProtoModbusRegs *reg)
173{
174 io_ddata_t *iodata = nullptr;
175 CIOCommon *io = nullptr;
176 uint8_t *ptr = nullptr;
177 int err = 0;
178 int ret;
179 size_t size;
180
181 reg->mutex.lock();
182
183 while (n > 0)
184 {
185 if (index >= reg->ios)
186 {
187 err = -EIO;
188 break;
189 }
190
191 io = getIO_(reg->cfg->objid[index]);
192 iodata = reg->iodata[index];
193 ptr = reinterpret_cast<uint8_t *>(iodata->getDataPtr());
194 size = iodata->getDataSize();
195
196 if (size > n)
197 {
198 err = -EINVAL;
199 break;
200 }
201
202 if (read)
203 {
204 ret = io->getData(*iodata, 1);
205 if (ret != OK)
206 {
207 err = -EIO;
208 break;
209 }
210
211 for (size_t i = 0; i < size; i++)
212 {
213 *buff++ = ptr[i];
214 }
215 }
216 else
217 {
218 for (size_t i = 0; i < size; i++)
219 {
220 ptr[i] = *buff++;
221 }
222
223 ret = io->setData(*iodata);
224 if (ret != OK)
225 {
226 err = -EIO;
227 break;
228 }
229 }
230
231 index += static_cast<int>(size);
232 n -= static_cast<uint16_t>(size);
233 }
234
235 reg->mutex.unlock();
236 return err;
237}
238
239int CProtoModbusRegs::regReadWriteCoilPacked(uint8_t *buff,
240 uint16_t n,
241 int index,
242 bool read,
243 SProtoModbusRegs *reg)
244{
245 io_ddata_t *iodata = nullptr;
246 CIOCommon *io = nullptr;
247 uint8_t *ptr = nullptr;
248 int err = 0;
249 int ret;
250
251 reg->mutex.lock();
252
253 if (read)
254 {
255 memset(buff, 0, (n + 7) / 8);
256 }
257
258 for (uint16_t bit = 0; bit < n; bit++)
259 {
260 size_t offsetInIo = 0;
261 const int io_index = findIoIndexByByteOffset(reg, index + bit, &offsetInIo);
262
263 if (io_index < 0 || io_index >= reg->ios)
264 {
265 if (read)
266 {
267 continue;
268 }
269
270 err = -EIO;
271 break;
272 }
273
274 io = getIO_(reg->cfg->objid[io_index]);
275 iodata = reg->iodata[io_index];
276 ptr = reinterpret_cast<uint8_t *>(iodata->getDataPtr());
277 size_t size = iodata->getDataSize();
278
279 if (offsetInIo >= size)
280 {
281 err = -EINVAL;
282 break;
283 }
284
285 if (read)
286 {
287 ret = io->getData(*iodata, 1);
288 if (ret != OK)
289 {
290 err = -EIO;
291 break;
292 }
293
294 modbus_set_bit(buff, bit, (ptr[offsetInIo] == 0) ? 0 : 1);
295 }
296 else
297 {
298 ret = io->getData(*iodata, 1);
299 if (ret != OK)
300 {
301 err = -EIO;
302 break;
303 }
304
305 ptr[offsetInIo] = modbus_get_bit(buff, bit) ? 1 : 0;
306
307 ret = io->setData(*iodata);
308 if (ret != OK)
309 {
310 err = -EIO;
311 break;
312 }
313 }
314 }
315
316 reg->mutex.unlock();
317 return err;
318}
319#endif
320
321#ifdef CONFIG_DAWN_PROTO_MODBUS_SEEKABLE
322int CProtoModbusRegs::seekableRegRW(uint8_t *buff,
323 uint16_t n,
324 int index,
325 bool read,
326 SProtoModbusRegs *reg,
327 SSeekableState *seekState)
328{
329 io_ddata_t *iodata = nullptr;
330 CIOCommon *io = nullptr;
331 uint8_t *ptr = nullptr;
332 uint16_t value = 0;
333 int err = 0;
334 int ret;
335 size_t i;
336
337 size_t windowRegs = seekState->windowRegs;
338 size_t regsPerIo = windowRegs + 1;
339
340 while (n > 0)
341 {
342 if (index < 0)
343 {
344 err = -EINVAL;
345 break;
346 }
347
348 size_t ioIndex = static_cast<size_t>(index) / regsPerIo;
349 size_t inIo = static_cast<size_t>(index) % regsPerIo;
350 if (ioIndex >= static_cast<size_t>(reg->ios))
351 {
352 err = -EIO;
353 break;
354 }
355
356 // Seek register access
357
358 if (inIo == windowRegs)
359 {
360 if (read)
361 {
362 seekState->readProgressWords[ioIndex] = 0;
363 value = static_cast<uint16_t>(seekState->seekOffsets[ioIndex]);
364 *buff++ = static_cast<uint8_t>((value >> 8) & 0xff);
365 *buff++ = static_cast<uint8_t>(value & 0xff);
366 }
367 else
368 {
369 seekState->seekOffsets[ioIndex] =
370 static_cast<size_t>(((uint16_t)buff[0] << 8) | (uint16_t)buff[1]);
371 seekState->readProgressWords[ioIndex] = 0;
372 buff += sizeof(uint16_t);
373 }
374
375 index += 1;
376 n -= 1;
377 continue;
378 }
379
380 // Data window access
381
382 io = getIO_(reg->cfg->objid[ioIndex]);
383 if (!read && !io->isWrite())
384 {
385 err = -EINVAL;
386 break;
387 }
388
389 size_t accessWords = windowRegs - inIo;
390 if (accessWords > n)
391 {
392 accessWords = n;
393 }
394
395 iodata = reg->iodata[ioIndex];
396 ptr = reinterpret_cast<uint8_t *>(iodata->getDataPtr());
397 size_t accessBytes = accessWords * sizeof(uint16_t);
398 io_data_view_t windowData(ptr, accessBytes, accessBytes);
399 std::memset(ptr, 0, accessBytes);
400
401 size_t byteOffset = seekState->seekOffsets[ioIndex] + (inIo * sizeof(uint16_t));
402
403 if (read)
404 {
405 size_t totalBytes = io->getDataSize();
406 if (byteOffset < totalBytes)
407 {
408 ret = io->getData(windowData, 1, byteOffset);
409 if (ret != OK)
410 {
411 err = -EIO;
412 break;
413 }
414 }
415
416 for (i = 0; i < accessWords; i++)
417 {
418 value =
419 static_cast<uint16_t>(ptr[i * 2]) | (static_cast<uint16_t>(ptr[i * 2 + 1]) << 8);
420 *buff++ = static_cast<uint8_t>((value >> 8) & 0xff);
421 *buff++ = static_cast<uint8_t>(value & 0xff);
422 }
423
424 size_t progressedWords = seekState->readProgressWords[ioIndex];
425 if (inIo == 0)
426 {
427 progressedWords = accessWords;
428 }
429 else if (progressedWords == inIo)
430 {
431 progressedWords += accessWords;
432 }
433 else
434 {
435 progressedWords = 0;
436 }
437
438 if (progressedWords >= windowRegs)
439 {
440 seekState->seekOffsets[ioIndex] += windowRegs * sizeof(uint16_t);
441 progressedWords = 0;
442 }
443
444 seekState->readProgressWords[ioIndex] = progressedWords;
445 }
446 else
447 {
448 for (i = 0; i < accessWords; i++)
449 {
450 ptr[i * 2] = buff[1];
451 ptr[i * 2 + 1] = buff[0];
452 buff += sizeof(uint16_t);
453 }
454
455 ret = io->setData(windowData, byteOffset);
456 if (ret != OK)
457 {
458 err = -EIO;
459 break;
460 }
461
462 seekState->readProgressWords[ioIndex] = 0;
463 }
464
465 index += static_cast<int>(accessWords);
466 n -= static_cast<uint16_t>(accessWords);
467 }
468
469 return err;
470}
471#endif
472
473#if defined(CONFIG_DAWN_PROTO_MODBUS_INPUT) || defined(CONFIG_DAWN_PROTO_MODBUS_HOLDING)
474static int findIoIndexByRegOffset(const CProtoModbusRegs::SProtoModbusRegs *reg, int index)
475{
476 size_t regOffset = 0;
477 for (size_t j = 0; j < reg->iodata.size(); j++)
478 {
479 size_t words = getIoRegBytes(reg, j) / sizeof(uint16_t);
480
481 if (index == static_cast<int>(regOffset))
482 {
483 return static_cast<int>(j);
484 }
485
486 regOffset += words;
487 }
488
489 return -1;
490}
491
492int CProtoModbusRegs::standardRegRW(uint8_t *buff,
493 uint16_t n,
494 int index,
495 bool read,
496 SProtoModbusRegs *reg)
497{
498 io_ddata_t *iodata = nullptr;
499 CIOCommon *io = nullptr;
500 uint8_t *ptr = nullptr;
501 uint16_t value = 0;
502 int err = 0;
503 int ret;
504 size_t words;
505 size_t i;
506
507 while (n > 0)
508 {
509 int ioIndex = findIoIndexByRegOffset(reg, index);
510 if (ioIndex < 0 || ioIndex >= reg->ios)
511 {
512 err = -EIO;
513 break;
514 }
515
516 io = getIO_(reg->cfg->objid[ioIndex]);
517 iodata = reg->iodata[ioIndex];
518 ptr = reinterpret_cast<uint8_t *>(iodata->getDataPtr());
519 words = iodata->getDataSize() / sizeof(uint16_t);
520
521 if (words > n)
522 {
523 err = -EINVAL;
524 break;
525 }
526
527 if (read)
528 {
529 ret = io->getData(*iodata, 1);
530 if (ret != OK)
531 {
532 err = -EIO;
533 break;
534 }
535
536 for (i = 0; i < words; i++)
537 {
538 value =
539 static_cast<uint16_t>(ptr[i * 2]) | (static_cast<uint16_t>(ptr[i * 2 + 1]) << 8);
540 *buff++ = static_cast<uint8_t>((value >> 8) & 0xff);
541 *buff++ = static_cast<uint8_t>(value & 0xff);
542 }
543 }
544 else
545 {
546 for (i = 0; i < words; i++)
547 {
548 ptr[i * 2] = buff[1];
549 ptr[i * 2 + 1] = buff[0];
550 buff += sizeof(uint16_t);
551 }
552
553 ret = io->setData(*iodata);
554 if (ret != OK)
555 {
556 err = -EIO;
557 break;
558 }
559 }
560
561 index += static_cast<int>(words);
562 n -= static_cast<uint16_t>(words);
563 }
564
565 return err;
566}
567
568int CProtoModbusRegs::regReadWriteHolding(uint8_t *buff,
569 uint16_t n,
570 int index,
571 bool read,
572 SProtoModbusRegs *reg)
573{
574 reg->mutex.lock();
575 int err;
576
577# ifdef CONFIG_DAWN_PROTO_MODBUS_SEEKABLE
578 if (reg->cfg->type == MODBUS_TYPE_SEEKABLE)
579 {
580 SSeekableState *seekState = findSeekableState(reg);
581 err = (seekState != nullptr) ? seekableRegRW(buff, n, index, read, reg, seekState) : -EIO;
582 }
583 else
584# endif
585 {
586 err = standardRegRW(buff, n, index, read, reg);
587 }
588
589 reg->mutex.unlock();
590 return err;
591}
592#endif
593
594CProtoModbusRegs::~CProtoModbusRegs()
595{
596}
597
598void CProtoModbusRegs::valloc_push_back(SProtoModbusIOBind *alloc)
599{
600 valloc.push_back(alloc);
601}
602
603#ifdef CONFIG_DAWN_PROTO_MODBUS_SEEKABLE
604static int setupSeekableRegs(CProtoModbusRegs::SProtoModbusRegs *tmp,
605 size_t *seekWindowRegs,
606 bool *addSeekableState)
607{
608 *seekWindowRegs = modbus_seekable_window_regs(tmp);
609 if (*seekWindowRegs == 0)
610 {
611 DAWNERR("invalid seekable window size\n");
612 return -EINVAL;
613 }
614
615 *addSeekableState = true;
616 return OK;
617}
618#else
619static int setupSeekableRegs(CProtoModbusRegs::SProtoModbusRegs *tmp,
620 size_t *seekWindowRegs,
621 bool *addSeekableState)
622{
623 (void)tmp;
624 *seekWindowRegs = 0;
625 *addSeekableState = false;
626 DAWNERR("seekable register type disabled at compile time\n");
627 return -ENOTSUP;
628}
629#endif
630
631static int validateRegisterIo(CIOCommon *io, uint32_t type, size_t seekWindowRegs)
632{
633 switch (type)
634 {
635#ifdef CONFIG_DAWN_PROTO_MODBUS_COIL
637 {
638 if (io->isSeekable())
639 {
640 DAWNERR("seekable IO not supported for coil regs\n");
641 return -ENOTSUP;
642 }
643
644 if (io->isWrite() == false)
645 {
646 DAWNERR("coil reg doesn't support write\n");
647 return -EINVAL;
648 }
649
650 if (io->isRead() == false)
651 {
652 DAWNERR("coil reg doesn't support read\n");
653 return -EINVAL;
654 }
655
656 if (io->getDataSize() == 0 || io->getDataDim() == 0)
657 {
658 DAWNERR("unsupported IO type for coil\n");
659 return -EINVAL;
660 }
661
662 return static_cast<int>(io->getDataSize());
663 }
664
666 {
667 if (io->isSeekable())
668 {
669 DAWNERR("seekable IO not supported for coil regs\n");
670 return -ENOTSUP;
671 }
672
673 if (io->isWrite() == false)
674 {
675 DAWNERR("coil reg doesn't support write\n");
676 return -EINVAL;
677 }
678
679 if (io->isRead() == false)
680 {
681 DAWNERR("coil reg doesn't support read\n");
682 return -EINVAL;
683 }
684
685 if (io->getDataSize() == 0 || io->getDataDim() == 0)
686 {
687 DAWNERR("unsupported IO type for packed coil\n");
688 return -EINVAL;
689 }
690
691 return static_cast<int>(io->getDataSize());
692 }
693#endif
694
695#ifdef CONFIG_DAWN_PROTO_MODBUS_DISCRETE
697 {
698 if (io->isSeekable())
699 {
700 DAWNERR("seekable IO not supported for discrete regs\n");
701 return -ENOTSUP;
702 }
703
704 if (io->isRead() == false)
705 {
706 DAWNERR("discrete reg doesn't support read\n");
707 return -EINVAL;
708 }
709
710 if (io->getDataSize() == 0 || io->getDataDim() == 0)
711 {
712 DAWNERR("unsupported IO type for discrete\n");
713 return -EINVAL;
714 }
715
716 return static_cast<int>(io->getDataSize());
717 }
718
720 {
721 if (io->isSeekable())
722 {
723 DAWNERR("seekable IO not supported for discrete regs\n");
724 return -ENOTSUP;
725 }
726
727 if (io->isRead() == false)
728 {
729 DAWNERR("discrete reg doesn't support read\n");
730 return -EINVAL;
731 }
732
733 if (io->getDataSize() == 0 || io->getDataDim() == 0)
734 {
735 DAWNERR("unsupported IO type for packed discrete\n");
736 return -EINVAL;
737 }
738
739 return static_cast<int>(io->getDataSize());
740 }
741#endif
742
743#ifdef CONFIG_DAWN_PROTO_MODBUS_INPUT
745 {
746 if (io->isSeekable())
747 {
748 DAWNERR("seekable IO not supported for input regs\n");
749 return -ENOTSUP;
750 }
751
752 if (io->isRead() == false)
753 {
754 DAWNERR("input reg doesn't support read\n");
755 return -EINVAL;
756 }
757
758 size_t bytes = io->getDataSize();
759
760 if (bytes == 0 || bytes % sizeof(uint16_t) != 0)
761 {
762 DAWNERR("unsupported IO type for input\n");
763 return -EINVAL;
764 }
765
766 return static_cast<int>(bytes);
767 }
768#endif
769
770#ifdef CONFIG_DAWN_PROTO_MODBUS_HOLDING
772 {
773 if (io->isSeekable())
774 {
775 DAWNERR("seekable IO requires seekable reg type\n");
776 return -ENOTSUP;
777 }
778
779 if (!io->isRead() || !io->isWrite())
780 {
781 DAWNERR("holding reg requires rw IO\n");
782 return -EINVAL;
783 }
784
785 size_t bytes = io->getDataSize();
786
787 if (bytes == 0 || bytes % sizeof(uint16_t) != 0)
788 {
789 DAWNERR("unsupported IO type for holding\n");
790 return -EINVAL;
791 }
792
793 return static_cast<int>(bytes);
794 }
795#endif
796
797#ifdef CONFIG_DAWN_PROTO_MODBUS_SEEKABLE
799 {
800 if (!io->isSeekable())
801 {
802 DAWNERR("seekable reg requires seekable IO\n");
803 return -EINVAL;
804 }
805
806 if (!io->isRead())
807 {
808 DAWNERR("seekable reg doesn't support read\n");
809 return -EINVAL;
810 }
811
812 return static_cast<int>((seekWindowRegs + 1) * sizeof(uint16_t));
813 }
814#endif
815
816 default:
817 {
818 DAWNERR("Unsupported Modbus reg type %" PRIu32 "\n", type);
819 return -EINVAL;
820 }
821 }
822}
823
824int CProtoModbusRegs::finalizeRegGroup(SProtoModbusRegs *tmp,
825 const SProtoModbusIOBind *v,
826 size_t seekWindowRegs,
827 bool addSeekableState)
828{
829 switch (v->type)
830 {
831#ifdef CONFIG_DAWN_PROTO_MODBUS_COIL
832 case MODBUS_TYPE_COIL:
834 {
835 tmp->regs = tmp->size / sizeof(uint8_t);
836 auto *overlap = hasOverlap(vcoils, v->start, tmp->regs);
837 if (overlap != nullptr)
838 {
839 DAWNERR("overlapping coil ranges: start=%" PRIu32 " count=%zu overlaps start=%" PRIu32
840 " count=%zu\n",
841 v->start,
842 tmp->regs,
843 overlap->cfg->start,
844 overlap->regs);
845 return -EINVAL;
846 }
847
848 vcoils.push_back(tmp);
849 break;
850 }
851#endif
852
853#ifdef CONFIG_DAWN_PROTO_MODBUS_DISCRETE
856 {
857 tmp->regs = tmp->size / sizeof(uint8_t);
858 auto *overlap = hasOverlap(vdiscrete, v->start, tmp->regs);
859 if (overlap != nullptr)
860 {
861 DAWNERR("overlapping discrete ranges: start=%" PRIu32
862 " count=%zu overlaps start=%" PRIu32 " count=%zu\n",
863 v->start,
864 tmp->regs,
865 overlap->cfg->start,
866 overlap->regs);
867 return -EINVAL;
868 }
869
870 vdiscrete.push_back(tmp);
871 break;
872 }
873#endif
874
875#ifdef CONFIG_DAWN_PROTO_MODBUS_INPUT
877 {
878 tmp->regs = tmp->size / sizeof(uint16_t);
879 auto *overlap = hasOverlap(vinput, v->start, tmp->regs);
880 if (overlap != nullptr)
881 {
882 DAWNERR("overlapping input ranges: start=%" PRIu32
883 " count=%zu overlaps start=%" PRIu32 " count=%zu\n",
884 v->start,
885 tmp->regs,
886 overlap->cfg->start,
887 overlap->regs);
888 return -EINVAL;
889 }
890
891 vinput.push_back(tmp);
892 break;
893 }
894#endif
895
896#ifdef CONFIG_DAWN_PROTO_MODBUS_HOLDING
899 {
900 tmp->regs = tmp->size / sizeof(uint16_t);
901 auto *overlap = hasOverlap(vholding, v->start, tmp->regs);
902
903 if (overlap != nullptr)
904 {
905 DAWNERR("overlapping holding ranges: start=%" PRIu32
906 " count=%zu overlaps start=%" PRIu32 " count=%zu\n",
907 v->start,
908 tmp->regs,
909 overlap->cfg->start,
910 overlap->regs);
911 return -EINVAL;
912 }
913
914 vholding.push_back(tmp);
915
916 if (addSeekableState)
917 {
918# ifdef CONFIG_DAWN_PROTO_MODBUS_SEEKABLE
919 SSeekableState *state = new (std::nothrow) SSeekableState();
920 if (state == nullptr)
921 {
922 DAWNERR("failed to allocate seekable state\n");
923 return -ENOMEM;
924 }
925
926 state->reg = tmp;
927 state->windowRegs = seekWindowRegs;
928 state->seekOffsets.assign(v->size, 0);
929 state->readProgressWords.assign(v->size, 0);
930 vseekable.push_back(state);
931# endif
932 }
933
934 break;
935 }
936#endif
937
938 default:
939 {
940 DAWNERR("Unsupported Modbus reg type %" PRIu32 "\n", v->type);
941 return -EINVAL;
942 }
943 }
944
945 return OK;
946}
947
948int CProtoModbusRegs::createRegs()
949{
950 if (initialized)
951 {
952 return OK;
953 }
954
955 for (auto const *v : valloc)
956 {
957 SProtoModbusRegs *tmp;
958 size_t seekWindowRegs = 0;
959 bool addSeekableState = false;
960
961 tmp = new (std::nothrow) SProtoModbusRegs();
962 if (!tmp)
963 {
964 DAWNERR("failed to allocate register handler\n");
965 return -ENOMEM;
966 }
967
968 tmp->cfg = v;
969 tmp->data = nullptr;
970 tmp->regs = 0;
971 tmp->size = 0;
972 tmp->ios = 0;
973 tmp->iodata.reserve(v->size);
974
975 if (v->type == MODBUS_TYPE_SEEKABLE)
976 {
977 int ret = setupSeekableRegs(tmp, &seekWindowRegs, &addSeekableState);
978 if (ret != OK)
979 {
980 return ret;
981 }
982 }
983
984 for (size_t i = 0; i < v->size; i++)
985 {
986 CIOCommon *io;
987 io_ddata_t *iobuffer;
988
989 io = getIO_(v->objid[i]);
990 if (io == nullptr)
991 {
992 DAWNERR("Failed to get IO 0x%08" PRIx32 "\n", v->objid[i]);
993 return -EIO;
994 }
995
996 int sizeInc = validateRegisterIo(io, v->type, seekWindowRegs);
997 if (sizeInc < 0)
998 {
999 return sizeInc;
1000 }
1001
1002 tmp->size += static_cast<size_t>(sizeInc);
1003 tmp->ios += 1;
1004
1005 if (v->type == MODBUS_TYPE_SEEKABLE)
1006 {
1007 iobuffer = io->ddata_alloc(1, seekWindowRegs * sizeof(uint16_t));
1008 }
1009 else
1010 {
1011 iobuffer = io->ddata_alloc(1);
1012 }
1013
1014 if (!iobuffer)
1015 {
1016 DAWNERR("failed to allocate register buffer\n");
1017 return -ENOMEM;
1018 }
1019
1020 tmp->iodata.push_back(iobuffer);
1021 }
1022
1023 int ret = finalizeRegGroup(tmp, v, seekWindowRegs, addSeekableState);
1024 if (ret != OK)
1025 {
1026 return ret;
1027 }
1028 }
1029
1030 initialized = true;
1031 return OK;
1032}
1033
1034int CProtoModbusRegs::destroyRegs()
1035{
1036 if (!initialized)
1037 {
1038 return OK;
1039 }
1040
1041#ifdef CONFIG_DAWN_PROTO_MODBUS_COIL
1042 cleanupGroups(vcoils);
1043#endif
1044#ifdef CONFIG_DAWN_PROTO_MODBUS_DISCRETE
1045 cleanupGroups(vdiscrete);
1046#endif
1047#ifdef CONFIG_DAWN_PROTO_MODBUS_INPUT
1048 cleanupGroups(vinput);
1049#endif
1050#ifdef CONFIG_DAWN_PROTO_MODBUS_HOLDING
1051 cleanupGroups(vholding);
1052#endif
1053
1054#ifdef CONFIG_DAWN_PROTO_MODBUS_SEEKABLE
1055 for (auto *state : vseekable)
1056 {
1057 delete state;
1058 }
1059
1060 vseekable.clear();
1061#endif
1062
1063 valloc.clear();
1064 initialized = false;
1065
1066 return OK;
1067}
1068
1069#ifdef CONFIG_DAWN_PROTO_MODBUS_COIL
1070int CProtoModbusRegs::coilsCb(uint8_t *buf,
1071 uint16_t addr,
1072 uint16_t ncoils,
1073 enum nxmb_regmode_e mode,
1074 void *priv)
1075{
1076 SProtoModbusRegs *reg = nullptr;
1077 int index;
1078 int ret_helper;
1079 bool read = (mode == NXMB_REG_READ);
1080
1081 (void)priv;
1082
1083 reg = findGroup(addr, ncoils, vcoils);
1084 if (!reg)
1085 {
1086 return -ENOENT;
1087 }
1088
1089 index = (int)(addr - reg->cfg->start);
1090
1091 DAWNINFO("coilsCb %d %d %d\n", addr, ncoils, index);
1092
1093 if (reg->cfg->type == MODBUS_TYPE_COIL_PACKED)
1094 {
1095 ret_helper = regReadWriteCoilPacked(buf, ncoils, index, read, reg);
1096 }
1097 else
1098 {
1099 ret_helper = regReadWriteCoil(buf, ncoils, index, read, reg);
1100 }
1101
1102 return ret_helper;
1103}
1104#endif
1105
1106#ifdef CONFIG_DAWN_PROTO_MODBUS_DISCRETE
1107int CProtoModbusRegs::discreteCb(uint8_t *buf, uint16_t addr, uint16_t ndiscrete, void *priv)
1108{
1109 SProtoModbusRegs *reg = nullptr;
1110 int index;
1111 int ret_helper;
1112
1113 (void)priv;
1114
1115 reg = findGroup(addr, ndiscrete, vdiscrete);
1116 if (!reg)
1117 {
1118 return -ENOENT;
1119 }
1120
1121 index = (int)(addr - reg->cfg->start);
1122
1123 DAWNINFO("discreteCb %d %d %d\n", addr, ndiscrete, index);
1124
1125 if (reg->cfg->type == MODBUS_TYPE_DISCRETE_PACKED)
1126 {
1127 ret_helper = regReadWriteCoilPacked(buf, ndiscrete, index, true, reg);
1128 }
1129 else
1130 {
1131 ret_helper = regReadWriteCoil(buf, ndiscrete, index, true, reg);
1132 }
1133
1134 return ret_helper;
1135}
1136#endif
1137
1138#ifdef CONFIG_DAWN_PROTO_MODBUS_INPUT
1139int CProtoModbusRegs::inputCb(uint8_t *buf, uint16_t addr, uint16_t nregs, void *priv)
1140{
1141 SProtoModbusRegs *reg = nullptr;
1142 int index;
1143 int ret_helper;
1144
1145 (void)priv;
1146
1147 reg = findGroup(addr, nregs, vinput);
1148 if (!reg)
1149 {
1150 return -ENOENT;
1151 }
1152
1153 index = (int)(addr - reg->cfg->start);
1154
1155 DAWNINFO("inputCb %d %d %d\n", addr, nregs, index);
1156
1157 ret_helper = regReadWriteHolding(buf, nregs, index, true, reg);
1158
1159 return ret_helper;
1160}
1161#endif
1162
1163#ifdef CONFIG_DAWN_PROTO_MODBUS_HOLDING
1164int CProtoModbusRegs::holdingCb(uint8_t *buf,
1165 uint16_t addr,
1166 uint16_t nregs,
1167 enum nxmb_regmode_e mode,
1168 void *priv)
1169{
1170 SProtoModbusRegs *reg = nullptr;
1171 int index;
1172 int ret_helper;
1173 bool read = (mode == NXMB_REG_READ);
1174
1175 (void)priv;
1176
1177 reg = findGroup(addr, nregs, vholding);
1178 if (!reg)
1179 {
1180 return -ENOENT;
1181 }
1182
1183 index = (int)(addr - reg->cfg->start);
1184
1185 DAWNINFO("holdingCb %d %d %d\n", addr, nregs, index);
1186
1187 ret_helper = regReadWriteHolding(buf, nregs, index, read, reg);
1188
1189 return ret_helper;
1190}
1191#endif
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 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.
@ MODBUS_TYPE_DISCRETE_PACKED
Read-only bits packed in bytes.
Definition regs.hxx:37
@ MODBUS_TYPE_INPUT
Read-only 16-bit words.
Definition regs.hxx:38
@ MODBUS_TYPE_SEEKABLE
Seekable window over holding regs.
Definition regs.hxx:40
@ MODBUS_TYPE_COIL_PACKED
Read-write bits packed in bytes.
Definition regs.hxx:36
@ MODBUS_TYPE_COIL
Read-write bits (1 byte/bit).
Definition regs.hxx:34
@ MODBUS_TYPE_DISCRETE
Read-only bits (1 byte/bit).
Definition regs.hxx:35
@ MODBUS_TYPE_HOLDING
Read-write 16-bit words.
Definition regs.hxx:39
Out-of-tree user-extension hooks for Dawn.
Definition bindable.hxx:13
uint32_t type
Register type (MODBUS_TYPE_*).
Definition regs.hxx:47
uint32_t config
Register-type private configuration word.
Definition regs.hxx:48
uint32_t start
Start register address (0x0000-0xFFFF).
Definition regs.hxx:49
Runtime register group state.
Definition regs.hxx:57
std::vector< io_ddata_t * > iodata
I/O data for each object.
Definition regs.hxx:59
const SProtoModbusIOBind * cfg
Reference to bind config.
Definition regs.hxx:58
Non-owning I/O data view over caller-provided storage.
Definition viewdata.hxx:18
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