GCC Code Coverage Report


Directory: ./
File: Drv/LinuxUartDriver/LinuxUartDriver.cpp
Date: 2026-09-03 21:13:48
Exec Total Coverage
Lines: 0 237 0.0%
Functions: 0 10 0.0%
Branches: 0 160 0.0%

Line Branch Exec Source
1 // ======================================================================
2 // \title LinuxUartDriverImpl.cpp
3 // \author tcanham
4 // \brief cpp file for LinuxUartDriver 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/LinuxUartDriver/LinuxUartDriver.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 LinuxUartDriver ::LinuxUartDriver(const char* const compName)
31 : LinuxUartDriverComponentBase(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 LinuxUartDriver::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 LinuxUartDriver ::~LinuxUartDriver() {
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 LinuxUartDriver ::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 LinuxUartDriver ::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 LinuxUartDriver::recvReturnIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer) {
336 this->deallocate_out(0, fwBuffer);
337 }
338
339 void LinuxUartDriver ::serialReadTaskEntry(void* ptr) {
340 FW_ASSERT(ptr != nullptr);
341 Drv::ByteStreamStatus status = ByteStreamStatus::OTHER_ERROR; // added by m.chase 03.06.2017
342 LinuxUartDriver* comp = reinterpret_cast<LinuxUartDriver*>(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 LinuxUartDriver ::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 LinuxUartDriver ::quitReadThread() {
398 this->m_quitReadThread = true;
399 }
400
401 Os::Task::Status LinuxUartDriver ::join() {
402 return m_readTask.join();
403 }
404
405 } // end namespace Drv
406