GCC Code Coverage Report


Directory: ./
File: Drv/PosixUartDriver/PosixUartDriver.cpp
Date: 2026-09-23 22:11:34
Exec Total Coverage
Lines: 0 234 0.0%
Functions: 0 10 0.0%
Branches: 0 158 0.0%

Line Branch Exec Source
1 // ======================================================================
2 // \title PosixUartDriverImpl.cpp
3 // \author tcanham
4 // \brief cpp file for PosixUartDriver component implementation class
5 //
6 // \copyright
7 // Copyright 2009-2015, by the California Institute of Technology.
8 // ALL RIGHTS RESERVED. United States Government Sponsorship
9 // acknowledged.
10 //
11 // ======================================================================
12
13 #include <unistd.h>
14 #include <Drv/PosixUartDriver/PosixUartDriver.hpp>
15 #include <Os/TaskString.hpp>
16
17 #include "Fw/Types/BasicTypes.hpp"
18
19 #include <fcntl.h>
20 #include <termios.h>
21 #include <cerrno>
22 #include <cstring>
23
24 namespace Drv {
25
26 // ----------------------------------------------------------------------
27 // Construction, initialization, and destruction
28 // ----------------------------------------------------------------------
29
30 ✗ PosixUartDriver ::PosixUartDriver(const char* const compName)
31 : PosixUartDriverComponentBase(compName),
32 ✗ m_fd(-1),
33 ✗ m_allocationSize(0),
34 ✗ m_device("NOT_EXIST"),
35 ✗ m_bytesSent(0),
36 ✗ m_bytesReceived(0),
37 ✗ m_quitReadThread(false) {}
38
39 ✗ bool PosixUartDriver::open(const char* const device,
40 UartBaudRate baud,
41 UartFlowControl fc,
42 UartParity parity,
43 FwSizeType allocationSize) {
44 ✗ FW_ASSERT(device != nullptr);
45 ✗ int fd = -1;
46 ✗ int stat = -1;
47 ✗ this->m_allocationSize = allocationSize;
48
49 ✗ this->m_device = device;
50
51 /*
52 The O_NOCTTY flag tells UNIX that this program doesn't want to be the "controlling terminal" for that port. If you
53 don't specify this then any input (such as keyboard abort signals and so forth) will affect your process. Programs
54 like getty(1M/8) use this feature when starting the login process, but normally a user program does not want this
55 behavior.
56 */
57 ✗ fd = ::open(device, O_RDWR | O_NOCTTY);
58
59 ✗ if (fd == -1) {
60 ✗ Fw::LogStringArg _arg = device;
61 ✗ Fw::LogStringArg _err = strerror(errno);
62 ✗ this->log_WARNING_HI_OpenError(_arg, fd, _err);
63 ✗ return false;
64 ✗ }
65
66 // Configure blocking reads
67 struct termios cfg;
68
69 ✗ stat = tcgetattr(fd, &cfg);
70 ✗ if (-1 == stat) {
71 ✗ (void)close(fd);
72 ✗ Fw::LogStringArg _arg = device;
73 ✗ Fw::LogStringArg _err = strerror(errno);
74 ✗ this->log_WARNING_HI_OpenError(_arg, fd, _err);
75 ✗ return false;
76 ✗ }
77
78 /*
79 If MIN > 0 and TIME = 0, MIN sets the number of characters to receive before the read is satisfied. As TIME is
80 zero, the timer is not used.
81
82 If MIN = 0 and TIME > 0, TIME serves as a timeout value. The read will be satisfied if a single character is read,
83 or TIME is exceeded (t = TIME *0.1 s). If TIME is exceeded, no character will be returned.
84
85 If MIN > 0 and TIME > 0, TIME serves as an inter-character timer. The read will be satisfied if MIN characters are
86 received, or the time between two characters exceeds TIME. The timer is restarted every time a character is
87 received and only becomes active after the first character has been received.
88
89 If MIN = 0 and TIME = 0, read will be satisfied immediately. The number of characters currently available, or the
90 number of characters requested will be returned. According to Antonino (see contributions), you could issue a
91 fcntl(fd, F_SETFL, FNDELAY); before reading to get the same result.
92 */
93 ✗ cfg.c_cc[VMIN] = 0;
94 ✗ cfg.c_cc[VTIME] = 10; // 1 sec timeout on no-data
95
96 ✗ stat = tcsetattr(fd, TCSANOW, &cfg);
97 ✗ if (-1 == stat) {
98 ✗ (void)close(fd);
99 ✗ Fw::LogStringArg _arg = device;
100 ✗ Fw::LogStringArg _err = strerror(errno);
101 ✗ this->log_WARNING_HI_OpenError(_arg, fd, _err);
102 ✗ return false;
103 ✗ }
104
105 // Set flow control
106 ✗ if (fc == HW_FLOW) {
107 struct termios t;
108
109 ✗ stat = tcgetattr(fd, &t);
110 ✗ if (-1 == stat) {
111 ✗ (void)close(fd);
112 ✗ Fw::LogStringArg _arg = device;
113 ✗ Fw::LogStringArg _err = strerror(errno);
114 ✗ this->log_WARNING_HI_OpenError(_arg, fd, _err);
115 ✗ return false;
116 ✗ }
117
118 // modify flow control flags
119 ✗ t.c_cflag |= CRTSCTS;
120
121 ✗ stat = tcsetattr(fd, TCSANOW, &t);
122 ✗ if (-1 == stat) {
123 ✗ (void)close(fd);
124 ✗ Fw::LogStringArg _arg = device;
125 ✗ Fw::LogStringArg _err = strerror(errno);
126 ✗ this->log_WARNING_HI_OpenError(_arg, fd, _err);
127 ✗ return false;
128 ✗ }
129 }
130
131 ✗ int relayRate = B0;
132 ✗ switch (baud) {
133 ✗ case BAUD_9600:
134 ✗ relayRate = B9600;
135 ✗ break;
136 ✗ case BAUD_19200:
137 ✗ relayRate = B19200;
138 ✗ break;
139 ✗ case BAUD_38400:
140 ✗ relayRate = B38400;
141 ✗ break;
142 ✗ case BAUD_57600:
143 ✗ relayRate = B57600;
144 ✗ break;
145 ✗ case BAUD_115K:
146 ✗ relayRate = B115200;
147 ✗ break;
148 ✗ case BAUD_230K:
149 ✗ relayRate = B230400;
150 ✗ break;
151 #ifdef B460800
152 ✗ case BAUD_460K:
153 ✗ relayRate = B460800;
154 ✗ break;
155 #endif
156 #ifdef B921600
157 ✗ case BAUD_921K:
158 ✗ relayRate = B921600;
159 ✗ break;
160 #endif
161 #ifdef B1000000
162 ✗ case BAUD_1000K:
163 ✗ relayRate = B1000000;
164 ✗ break;
165 #endif
166 #ifdef B1152000
167 ✗ case BAUD_1152K:
168 ✗ relayRate = B1152000;
169 ✗ break;
170 #endif
171 #ifdef B1500000
172 ✗ case BAUD_1500K:
173 ✗ relayRate = B1500000;
174 ✗ break;
175 #endif
176 #ifdef B2000000
177 ✗ case BAUD_2000K:
178 ✗ relayRate = B2000000;
179 ✗ break;
180 #endif
181 #ifdef B2500000
182 ✗ case BAUD_2500K:
183 ✗ relayRate = B2500000;
184 ✗ break;
185 #endif
186 #ifdef B3000000
187 ✗ case BAUD_3000K:
188 ✗ relayRate = B3000000;
189 ✗ break;
190 #endif
191 #ifdef B3500000
192 ✗ case BAUD_3500K:
193 ✗ relayRate = B3500000;
194 ✗ break;
195 #endif
196 #ifdef B4000000
197 ✗ case BAUD_4000K:
198 ✗ relayRate = B4000000;
199 ✗ break;
200 #endif
201 ✗ default:
202 ✗ FW_ASSERT(false, static_cast<FwAssertArgType>(baud));
203 ✗ break;
204 }
205
206 struct termios newtio;
207
208 ✗ stat = tcgetattr(fd, &newtio);
209 ✗ if (-1 == stat) {
210 ✗ (void)close(fd);
211 ✗ Fw::LogStringArg _arg = device;
212 ✗ Fw::LogStringArg _err = strerror(errno);
213 ✗ this->log_WARNING_HI_OpenError(_arg, fd, _err);
214 ✗ return false;
215 ✗ }
216
217 // CS8 = 8 data bits, CLOCAL = Local line, CREAD = Enable Receiver
218 /*
219 Even parity (7E1):
220 options.c_cflag |= PARENB
221 options.c_cflag &= ~PARODD
222 options.c_cflag &= ~CSTOPB
223 options.c_cflag &= ~CSIZE;
224 options.c_cflag |= CS7;
225 Odd parity (7O1):
226 options.c_cflag |= PARENB
227 options.c_cflag |= PARODD
228 options.c_cflag &= ~CSTOPB
229 options.c_cflag &= ~CSIZE;
230 options.c_cflag |= CS7;
231 */
232 ✗ newtio.c_cflag |= CS8 | CLOCAL | CREAD;
233
234 ✗ switch (parity) {
235 ✗ case PARITY_ODD:
236 ✗ newtio.c_cflag |= (PARENB | PARODD);
237 ✗ break;
238 ✗ case PARITY_EVEN:
239 ✗ newtio.c_cflag |= PARENB;
240 ✗ break;
241 ✗ case PARITY_NONE:
242 ✗ newtio.c_cflag &= static_cast<unsigned int>(~PARENB);
243 ✗ break;
244 ✗ default:
245 ✗ FW_ASSERT(false, parity);
246 ✗ break;
247 }
248
249 // Set baud rate:
250 ✗ stat = cfsetispeed(&newtio, static_cast<speed_t>(relayRate));
251 ✗ if (stat) {
252 ✗ (void)close(fd);
253 ✗ Fw::LogStringArg _arg = device;
254 ✗ Fw::LogStringArg _err = strerror(errno);
255 ✗ this->log_WARNING_HI_OpenError(_arg, fd, _err);
256 ✗ return false;
257 ✗ }
258 ✗ stat = cfsetospeed(&newtio, static_cast<speed_t>(relayRate));
259 ✗ if (stat) {
260 ✗ (void)close(fd);
261 ✗ Fw::LogStringArg _arg = device;
262 ✗ Fw::LogStringArg _err = strerror(errno);
263 ✗ this->log_WARNING_HI_OpenError(_arg, fd, _err);
264 ✗ return false;
265 ✗ }
266
267 // Raw output:
268 ✗ newtio.c_oflag = 0;
269
270 // set input mode (non-canonical, no echo,...)
271 ✗ newtio.c_lflag = 0;
272
273 ✗ newtio.c_iflag = INPCK;
274
275 // Flush old data:
276 ✗ (void)tcflush(fd, TCIFLUSH);
277
278 // Set attributes:
279 ✗ stat = tcsetattr(fd, TCSANOW, &newtio);
280 ✗ if (-1 == stat) {
281 ✗ (void)close(fd);
282 ✗ Fw::LogStringArg _arg = device;
283 ✗ Fw::LogStringArg _err = strerror(errno);
284 ✗ this->log_WARNING_HI_OpenError(_arg, fd, _err);
285 ✗ return false;
286 ✗ }
287
288 // All done!
289 ✗ this->m_fd = fd;
290 ✗ Fw::LogStringArg _arg = device;
291 ✗ this->log_ACTIVITY_HI_PortOpened(_arg);
292 ✗ if (this->isConnected_ready_OutputPort(0)) {
293 ✗ this->ready_out(0); // Indicate the driver is connected
294 }
295 ✗ return true;
296 }
297
298 ✗ PosixUartDriver ::~PosixUartDriver() {
299 ✗ if (this->m_fd != -1) {
300 ✗ (void)close(this->m_fd);
301 }
302 ✗ }
303
304 // ----------------------------------------------------------------------
305 // Handler implementations for user-defined typed input ports
306 // ----------------------------------------------------------------------
307
308 ✗ void PosixUartDriver ::run_handler(FwIndexType portNum, U32 context) {
309 ✗ this->tlmWrite_BytesSent(this->m_bytesSent);
310 ✗ this->tlmWrite_BytesRecv(this->m_bytesReceived);
311 ✗ }
312
313 ✗ Drv::ByteStreamStatus PosixUartDriver ::send_handler(const FwIndexType portNum, Fw::Buffer& serBuffer) {
314 ✗ Drv::ByteStreamStatus status = Drv::ByteStreamStatus::OP_OK;
315 ✗ if (this->m_fd == -1 || serBuffer.getData() == nullptr || serBuffer.getSize() == 0) {
316 ✗ status = Drv::ByteStreamStatus::OTHER_ERROR;
317 } else {
318 ✗ unsigned char* data = serBuffer.getData();
319 ✗ FW_ASSERT_NO_OVERFLOW(serBuffer.getSize(), size_t);
320 ✗ size_t xferSize = static_cast<size_t>(serBuffer.getSize());
321
322 ✗ ssize_t stat = ::write(this->m_fd, data, xferSize);
323
324 ✗ if (-1 == stat || static_cast<size_t>(stat) != xferSize) {
325 ✗ Fw::LogStringArg _arg = this->m_device;
326 ✗ this->log_WARNING_HI_WriteError(_arg, static_cast<I32>(stat));
327 ✗ status = Drv::ByteStreamStatus::OTHER_ERROR;
328 ✗ } else {
329 ✗ this->m_bytesSent += static_cast<FwSizeType>(stat);
330 }
331 }
332 ✗ return status;
333 ✗ }
334
335 ✗ void PosixUartDriver::recvReturnIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer) {
336 ✗ this->deallocate_out(0, fwBuffer);
337 ✗ }
338
339 ✗ void PosixUartDriver ::serialReadTaskEntry(void* ptr) {
340 ✗ FW_ASSERT(ptr != nullptr);
341 ✗ Drv::ByteStreamStatus status = ByteStreamStatus::OTHER_ERROR; // added by m.chase 03.06.2017
342 ✗ PosixUartDriver* comp = reinterpret_cast<PosixUartDriver*>(ptr);
343 // @non-terminating@: read thread runs until quit is requested
344 ✗ while (!comp->m_quitReadThread) {
345 ✗ Fw::Buffer buff = comp->allocate_out(0, comp->m_allocationSize);
346
347 // On failed allocation, error
348 ✗ if (buff.getData() == nullptr) {
349 ✗ Fw::LogStringArg _arg = comp->m_device;
350 ✗ comp->log_WARNING_HI_NoBuffers(_arg);
351 ✗ status = ByteStreamStatus::OTHER_ERROR;
352 ✗ comp->recv_out(0, buff, status);
353 // to avoid spinning, wait 50 ms
354 ✗ (void)Os::Task::delay(Fw::TimeInterval(0, 50000)); // best-effort delay
355 ✗ continue;
356 ✗ }
357
358 ✗ int stat = 0;
359
360 // Read until something is received or an error occurs. Only loop when
361 // stat == 0 as this is the timeout condition and the read should spin
362 ✗ FW_ASSERT_NO_OVERFLOW(buff.getSize(), size_t);
363 // @non-terminating@: retry read until data arrives or quit is requested
364 ✗ while ((stat == 0) && !comp->m_quitReadThread) {
365 ✗ stat = static_cast<int>(::read(comp->m_fd, buff.getData(), static_cast<size_t>(buff.getSize())));
366 }
367 ✗ buff.setSize(0);
368
369 // On error stat (-1) must mark the read as error
370 // On normal stat (>0) pass a recv ok
371 // On timeout stat (0) and m_quitReadThread, error to return the buffer
372 ✗ if (stat == -1) {
373 ✗ Fw::LogStringArg _arg = comp->m_device;
374 ✗ comp->log_WARNING_HI_ReadError(_arg, stat);
375 ✗ status = ByteStreamStatus::OTHER_ERROR;
376 ✗ } else if (stat > 0) {
377 ✗ buff.setSize(static_cast<U32>(stat));
378 ✗ status = ByteStreamStatus::OP_OK; // added by m.chase 03.06.2017
379 ✗ comp->m_bytesReceived += static_cast<FwSizeType>(stat);
380 } else {
381 ✗ status = ByteStreamStatus::OTHER_ERROR; // Simply to return the buffer
382 }
383
384 ✗ comp->recv_out(0, buff, status); // added by m.chase 03.06.2017
385 ✗ }
386 ✗ }
387
388 ✗ void PosixUartDriver ::start(FwTaskPriorityType priority,
389 Os::Task::ParamType stackSize,
390 Os::Task::ParamType cpuAffinity) {
391 ✗ Os::TaskString task("SerReader");
392 ✗ Os::Task::Arguments arguments(task, serialReadTaskEntry, this, priority, stackSize, cpuAffinity);
393 ✗ Os::Task::Status stat = this->m_readTask.start(arguments);
394 ✗ FW_ASSERT(stat == Os::Task::OP_OK, stat);
395 ✗ }
396
397 ✗ void PosixUartDriver ::quitReadThread() {
398 ✗ this->m_quitReadThread = true;
399 ✗ }
400
401 ✗ Os::Task::Status PosixUartDriver ::join() {
402 ✗ return m_readTask.join();
403 }
404
405 } // end namespace Drv
406