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