Dawn Framework 1.0
Universal data acquisition framework for embedded systems
simple.cxx
1// dawn/src/proto/serial/simple.cxx
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5
6#include "dawn/proto/serial/simple.hxx"
7
8#include "dawn/common/poll_loop.hxx"
9
10#include <fcntl.h>
11#include <poll.h>
12#include <sys/stat.h>
13#include <termios.h>
14#include <unistd.h>
15
16#include <cstring>
17
18using namespace dawn;
19
20int CProtoSerial::sendFrame(uint8_t cmd, const uint8_t *payload, size_t len)
21{
22 uint8_t frame[FRAME_MAX_PAYLOAD + FRAME_MIN_LEN];
23 size_t tosend;
24 ssize_t ret;
25 uint16_t crc;
26
27 if (len > FRAME_MAX_PAYLOAD || fd < 0)
28 {
29 return -1;
30 }
31
32 tosend = FRAME_MIN_LEN + len;
33
34 frame[0] = FRAME_SYNC;
35 frame[1] = (uint8_t)(len & 0xFF);
36 frame[2] = (uint8_t)((len >> 8) & 0xFF);
37 frame[3] = cmd;
38
39 if (len > 0)
40 {
41 std::memcpy(&frame[4], payload, len);
42 }
43
44 crc = calculateCrc(&frame[3], 1 + len);
45 frame[4 + len] = (uint8_t)(crc & 0xFF);
46 frame[5 + len] = (uint8_t)((crc >> 8) & 0xFF);
47
48 ret = write(fd, frame, tosend);
49 if (ret < 0 || static_cast<size_t>(ret) != tosend)
50 {
51 DAWNERR(
52 "Serial sendto failed cmd=0x%02x ret=%zd exp=%zu errno=%d\n", cmd, ret, tosend, errno);
53 return ret < 0 ? static_cast<int>(ret) : -EIO;
54 }
55
56 return OK;
57}
58
59void CProtoSerial::thread()
60{
61 struct pollfd fds[1];
62 SPollLoopCallbacks callbacks;
63
64 std::memset(fds, 0, sizeof(fds));
65
66 fds[0].fd = fd;
67 fds[0].events = POLLIN;
68 parserPos = 0;
69 parserLen = 0;
70 parserState = 0;
71
72 callbacks.beforePoll = CProtoSerial::cbPollBefore;
73 callbacks.afterPoll = CProtoSerial::cbPollAfter;
74 callbacks.onPollReady = CProtoSerial::cbPollOnReady;
75
76 CPollLoopRunner::run(workerThread(), fds, 1, DAWN_POLL_TIMEOUT_MS, callbacks, this);
77}
78
79int CProtoSerial::pollBefore(struct pollfd *pfds, nfds_t nfds)
80{
81 if (pfds == nullptr || nfds == 0)
82 {
83 return -EINVAL;
84 }
85
86 pfds[0].revents = 0;
87 return OK;
88}
89
90void CProtoSerial::pollAfter(int ret)
91{
92 if (ret < 0)
93 {
94 DAWNERR("serial poll failed %d\n", -errno);
95 }
96}
97
98int CProtoSerial::pollOnReady(struct pollfd *pfds, nfds_t nfds, int pollRet)
99{
100 uint8_t byte;
101 ssize_t ret;
102
103 if (pfds == nullptr || nfds == 0 || pollRet <= 0)
104 {
105 return -EINVAL;
106 }
107
108 if ((pfds[0].revents & POLLIN) == 0)
109 {
110 return OK;
111 }
112
113 ret = read(fd, &byte, 1);
114 if (ret <= 0)
115 {
116 usleep(10000);
117 return static_cast<int>(ret);
118 }
119
120 switch (parserState)
121 {
122 case 0:
123 {
124 if (byte == FRAME_SYNC)
125 {
126 rxbuffer[parserPos] = byte;
127 parserPos = 1;
128 parserState = 1;
129 }
130
131 break;
132 }
133
134 case 1:
135 {
136 parserLen = byte;
137 rxbuffer[parserPos] = byte;
138 parserPos = 2;
139 parserState = 2;
140 break;
141 }
142
143 case 2:
144 {
145 parserLen |= (uint16_t)(byte << 8);
146 if (parserLen > FRAME_MAX_PAYLOAD)
147 {
148 parserState = 0;
149 parserPos = 0;
150 return OK;
151 }
152
153 rxbuffer[parserPos] = byte;
154 parserPos = 3;
155 parserState = 3;
156 break;
157 }
158
159 case 3:
160 {
161 rxbuffer[parserPos++] = byte;
162
163 if (parserPos >= (size_t)(FRAME_MIN_LEN + parserLen))
164 {
165 handleFrame(rxbuffer, parserPos);
166 parserState = 0;
167 parserPos = 0;
168 }
169
170 break;
171 }
172 }
173
174 pfds[0].revents = 0;
175 return OK;
176}
177
178int CProtoSerial::cbPollBefore(void *priv, struct pollfd *pfds, nfds_t nfds)
179{
180 CProtoSerial *self;
181
182 self = static_cast<CProtoSerial *>(priv);
183 if (self == nullptr)
184 {
185 return -EINVAL;
186 }
187
188 return self->pollBefore(pfds, nfds);
189}
190
191void CProtoSerial::cbPollAfter(void *priv, struct pollfd *pfds, nfds_t nfds, int ret)
192{
193 CProtoSerial *self;
194
195 (void)pfds;
196 (void)nfds;
197 self = static_cast<CProtoSerial *>(priv);
198 if (self == nullptr)
199 {
200 return;
201 }
202
203 self->pollAfter(ret);
204}
205
206int CProtoSerial::cbPollOnReady(void *priv, struct pollfd *pfds, nfds_t nfds, int pollRet)
207{
208 CProtoSerial *self;
209
210 self = static_cast<CProtoSerial *>(priv);
211 if (self == nullptr)
212 {
213 return -EINVAL;
214 }
215
216 return self->pollOnReady(pfds, nfds, pollRet);
217}
218
219int CProtoSerial::configureDesc(const CDescObject &desc)
220{
221 SObjectCfg::SObjectCfgItem *item = nullptr;
222 size_t offset;
223
224 offset = 0;
225
226 for (size_t i = 0; i < desc.getSize(); i++)
227 {
228 item = desc.objectCfgItemNext(offset);
229
231 {
232 DAWNERR("Unsupported SERIAL config 0x%08" PRIx32 "\n", item->cfgid.v);
233 return -EINVAL;
234 }
235
236 switch (item->cfgid.s.id)
237 {
238 case PROTO_SERIAL_CFG_IOBIND:
239 {
240 for (size_t j = 0; j < item->cfgid.s.size;)
241 {
242 SProtoSerialIOBind *tmp;
243
244 tmp = reinterpret_cast<SProtoSerialIOBind *>(item->data + j);
245
246 allocObject(tmp);
247 j += sizeof(SProtoSerialIOBind) / 4;
248 }
249
250 break;
251 }
252
253 case PROTO_SERIAL_CFG_PATH:
254 {
255 path = reinterpret_cast<const char *>(&item->data);
256 break;
257 }
258
259 case PROTO_SERIAL_CFG_BAUD:
260 {
261 baud = static_cast<uint32_t>(item->data[0]);
262 break;
263 }
264
265 default:
266 {
267 DAWNERR("Unsupported SERIAL config 0x%08" PRIx32 "\n", item->cfgid.v);
268 return -EINVAL;
269 }
270 }
271 }
272
273 return OK;
274}
275
276int CProtoSerial::serialInit()
277{
278 struct termios tio;
279
280 if (!path)
281 {
282 DAWNERR("Serial path not configured\n");
283 return -1;
284 }
285
286 fd = open(path, O_RDWR);
287 if (fd < 0)
288 {
289 DAWNERR("Failed to open serial port: %s\n", path);
290 return -1;
291 }
292
293 tcgetattr(fd, &tio);
294 cfmakeraw(&tio);
295 tcsetattr(fd, TCSANOW, &tio);
296
297 DAWNINFO("Serial port initialized: %s (baud=%u)\n", path, baud);
298
299 return 0;
300}
301
302CProtoSerial::~CProtoSerial()
303{
304 deinit();
305}
306
308{
309 int ret;
310
311 ret = configureDesc(getDesc());
312 if (ret != OK)
313 {
314 DAWNERR("Serial configure failed (error %d)\n", ret);
315 return ret;
316 }
317
318 return OK;
319}
320
322{
323 int ret;
324
325 ret = serialInit();
326 if (ret < 0)
327 {
328 return ret;
329 }
330
331 ret = createBuffers();
332 if (ret < 0)
333 {
334 DAWNERR("failed to create data %d\n", ret);
335 return ret;
336 }
337
338#ifdef CONFIG_DAWN_IO_NOTIFY
339 ret = setupNotifications();
340 if (ret < 0)
341 {
342 DAWNERR("failed to setup notifications %d\n", ret);
343 }
344#endif
345
346 return OK;
347}
348
350{
351#ifdef CONFIG_DAWN_IO_NOTIFY
352 destroyNotifications();
353#endif
354
356
357 if (fd >= 0)
358 {
359 close(fd);
360 fd = -1;
361 }
362
363 return OK;
364}
365
367{
368 int ret;
369
370 ret = startWorkerThread([this]() { thread(); });
371 if (ret < 0)
372 {
373 DAWNERR("failed to start thread %d\n", ret);
374 return ret;
375 }
376
377 DAWNINFO("Serial protocol started\n");
378
379 return 0;
380}
381
383{
384#ifdef CONFIG_DAWN_IO_NOTIFY
385 cleanupNotifications();
386#endif
387
389
390 DAWNINFO("Serial protocol stopped\n");
391
392 return 0;
393}
394
396{
397 return workerThreadRunning();
398}
Descriptor wrapper for individual object configuration.
size_t getSize() const
Get number of configuration items for this object.
SObjectCfg::SObjectCfgItem * objectCfgItemNext(size_t &offset) const
Get config item at current offset and advance past it.
CDescObject & getDesc()
Get descriptor object for this object.
Definition object.cxx:190
static int run(CThreadedObject &threadCtl, struct pollfd *pfds, nfds_t nfds, int timeoutMs, const SPollLoopCallbacks &callbacks, void *priv)
Run poll loop until quit is requested.
Definition poll_loop.hxx:61
@ PROTO_CLASS_SERIAL
Compact binary protocol over serial port.
Definition common.hxx:63
Simple binary serial protocol for device communication.
Definition simple.hxx:28
int configure()
Configure object from descriptor data.
Definition simple.cxx:307
int init()
One-time initialize object after bindings are resolved.
Definition simple.cxx:321
int doStart()
Start implementation hook.
Definition simple.cxx:366
bool hasThread() const
Check if a background thread is active.
Definition simple.cxx:395
int doStop()
Stop implementation hook.
Definition simple.cxx:382
int deinit()
De-initialize object.
Definition simple.cxx:349
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.
int destroyBuffers()
Destroy shared per-IO data buffers.
bool workerThreadRunning() const
Check if the worker thread is running.
Definition thread.hxx:269
int stopWorkerThread()
Stop the worker thread.
Definition thread.hxx:258
int startWorkerThread(Func &&func)
Start the worker thread with a given function.
Definition thread.hxx:246
CThreadedObject & workerThread()
Get a reference to this thread controller.
Definition thread.hxx:280
Out-of-tree user-extension hooks for Dawn.
Definition bindable.hxx:13
Single configuration item within object.
ObjectCfgData_t data[]
Configuration data array (flexible, size from cfgid.s.size).
UObjectCfgId cfgid
Configuration ID header (type, class, id, size, rw, dtype).
Callback set for poll-based worker loops.
Definition poll_loop.hxx:28
ObjectCfgId v
Raw 32-bit ConfigID value (for storage, comparison).
Definition objectcfg.hxx:82
uint32_t cls
Object class (bits 21-29, max 511).
uint32_t id
Configuration identifier (bits 0-4, max 31).
Definition objectcfg.hxx:94
uint32_t size
Configuration data size in 32-bit words (bits 5-14, max 1023).
struct dawn::SObjectCfg::UObjectCfgId::@10 s
Bit-field structure for named member access.