F´ Flight Software - C/C++ Documentation NASA-v1.6.0
A framework for building embedded system applications to NASA flight quality standards.
Loading...
Searching...
No Matches
LinuxUartDriver.cpp
Go to the documentation of this file.
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>
15#include <Os/TaskString.hpp>
16
18
19#include <fcntl.h>
20#include <termios.h>
21#include <cerrno>
22
23//#include <cstdlib>
24//#include <cstdio>
25//#define DEBUG_PRINT(x,...) printf(x,##__VA_ARGS__); fflush(stdout)
26#define DEBUG_PRINT(x, ...)
27
28namespace Drv {
29
30// ----------------------------------------------------------------------
31// Construction, initialization, and destruction
32// ----------------------------------------------------------------------
33
34LinuxUartDriver ::LinuxUartDriver(const char* const compName)
35 : LinuxUartDriverComponentBase(compName), m_fd(-1), m_allocationSize(-1), m_device("NOT_EXIST"), m_quitReadThread(false) {
36}
37
38void LinuxUartDriver ::init(const NATIVE_INT_TYPE instance) {
39 LinuxUartDriverComponentBase::init(instance);
40}
41
42bool LinuxUartDriver::open(const char* const device,
43 UartBaudRate baud,
45 UartParity parity,
46 NATIVE_INT_TYPE allocationSize) {
47 FW_ASSERT(device != nullptr);
48 NATIVE_INT_TYPE fd = -1;
49 NATIVE_INT_TYPE stat = -1;
50 this->m_allocationSize = allocationSize;
51
52 this->m_device = device;
53
54 DEBUG_PRINT("Opening UART device %s\n", device);
55
56 /*
57 The O_NOCTTY flag tells UNIX that this program doesn't want to be the "controlling terminal" for that port. If you
58 don't specify this then any input (such as keyboard abort signals and so forth) will affect your process. Programs
59 like getty(1M/8) use this feature when starting the login process, but normally a user program does not want this
60 behavior.
61 */
62 fd = ::open(device, O_RDWR | O_NOCTTY);
63
64 if (fd == -1) {
65 DEBUG_PRINT("open UART device %s failed.\n", device);
66 Fw::LogStringArg _arg = device;
67 Fw::LogStringArg _err = strerror(errno);
68 this->log_WARNING_HI_OpenError(_arg, this->m_fd, _err);
69 return false;
70 } else {
71 DEBUG_PRINT("Successfully opened UART device %s fd %d\n", device, fd);
72 }
73
74 this->m_fd = fd;
75
76 // Configure blocking reads
77 struct termios cfg;
78
79 stat = tcgetattr(fd, &cfg);
80 if (-1 == stat) {
81 DEBUG_PRINT("tcgetattr failed: (%d): %s\n", stat, strerror(errno));
82 close(fd);
83 Fw::LogStringArg _arg = device;
84 Fw::LogStringArg _err = strerror(errno);
85 this->log_WARNING_HI_OpenError(_arg, fd, _err);
86 return false;
87 } else {
88 DEBUG_PRINT("tcgetattr passed.\n");
89 }
90
91 /*
92 If MIN > 0 and TIME = 0, MIN sets the number of characters to receive before the read is satisfied. As TIME is
93 zero, the timer is not used.
94
95 If MIN = 0 and TIME > 0, TIME serves as a timeout value. The read will be satisfied if a single character is read,
96 or TIME is exceeded (t = TIME *0.1 s). If TIME is exceeded, no character will be returned.
97
98 If MIN > 0 and TIME > 0, TIME serves as an inter-character timer. The read will be satisfied if MIN characters are
99 received, or the time between two characters exceeds TIME. The timer is restarted every time a character is
100 received and only becomes active after the first character has been received.
101
102 If MIN = 0 and TIME = 0, read will be satisfied immediately. The number of characters currently available, or the
103 number of characters requested will be returned. According to Antonino (see contributions), you could issue a
104 fcntl(fd, F_SETFL, FNDELAY); before reading to get the same result.
105 */
106 cfg.c_cc[VMIN] = 0;
107 cfg.c_cc[VTIME] = 10; // 1 sec timeout on no-data
108
109 stat = tcsetattr(fd, TCSANOW, &cfg);
110 if (-1 == stat) {
111 DEBUG_PRINT("tcsetattr failed: (%d): %s\n", stat, strerror(errno));
112 close(fd);
113 Fw::LogStringArg _arg = device;
114 Fw::LogStringArg _err = strerror(errno);
115 this->log_WARNING_HI_OpenError(_arg, fd, _err);
116 return false;
117 } else {
118 DEBUG_PRINT("tcsetattr passed.\n");
119 }
120
121 // Set flow control
122 if (fc == HW_FLOW) {
123 struct termios t;
124
125 int stat = tcgetattr(fd, &t);
126 if (-1 == stat) {
127 DEBUG_PRINT("tcgetattr UART fd %d failed\n", fd);
128 close(fd);
129 Fw::LogStringArg _arg = device;
130 Fw::LogStringArg _err = strerror(errno);
131 this->log_WARNING_HI_OpenError(_arg, fd, _err);
132 return false;
133 }
134
135 // modify flow control flags
136 t.c_cflag |= CRTSCTS;
137
138 stat = tcsetattr(fd, TCSANOW, &t);
139 if (-1 == stat) {
140 DEBUG_PRINT("tcsetattr UART fd %d failed\n", fd);
141 close(fd);
142 Fw::LogStringArg _arg = device;
143 Fw::LogStringArg _err = strerror(errno);
144 this->log_WARNING_HI_OpenError(_arg, fd, _err);
145 return false;
146 }
147 }
148
149 NATIVE_INT_TYPE relayRate = B0;
150 switch (baud) {
151 case BAUD_9600:
152 relayRate = B9600;
153 break;
154 case BAUD_19200:
155 relayRate = B19200;
156 break;
157 case BAUD_38400:
158 relayRate = B38400;
159 break;
160 case BAUD_57600:
161 relayRate = B57600;
162 break;
163 case BAUD_115K:
164 relayRate = B115200;
165 break;
166 case BAUD_230K:
167 relayRate = B230400;
168 break;
169#if defined TGT_OS_TYPE_LINUX
170 case BAUD_460K:
171 relayRate = B460800;
172 break;
173 case BAUD_921K:
174 relayRate = B921600;
175 break;
176#endif
177 default:
178 FW_ASSERT(0, baud);
179 break;
180 }
181
182 struct termios newtio;
183
184 stat = tcgetattr(fd, &newtio);
185 if (-1 == stat) {
186 DEBUG_PRINT("tcgetattr UART fd %d failed\n", fd);
187 close(fd);
188 Fw::LogStringArg _arg = device;
189 Fw::LogStringArg _err = strerror(errno);
190 this->log_WARNING_HI_OpenError(_arg, fd, _err);
191 return false;
192 }
193
194 // CS8 = 8 data bits, CLOCAL = Local line, CREAD = Enable Receiver
195 /*
196 Even parity (7E1):
197 options.c_cflag |= PARENB
198 options.c_cflag &= ~PARODD
199 options.c_cflag &= ~CSTOPB
200 options.c_cflag &= ~CSIZE;
201 options.c_cflag |= CS7;
202 Odd parity (7O1):
203 options.c_cflag |= PARENB
204 options.c_cflag |= PARODD
205 options.c_cflag &= ~CSTOPB
206 options.c_cflag &= ~CSIZE;
207 options.c_cflag |= CS7;
208 */
209 newtio.c_cflag |= CS8 | CLOCAL | CREAD;
210
211 switch (parity) {
212 case PARITY_ODD:
213 newtio.c_cflag |= (PARENB | PARODD);
214 break;
215 case PARITY_EVEN:
216 newtio.c_cflag |= PARENB;
217 break;
218 case PARITY_NONE:
219 newtio.c_cflag &= ~PARENB;
220 break;
221 default:
222 FW_ASSERT(0, parity);
223 break;
224 }
225
226 // Set baud rate:
227 stat = cfsetispeed(&newtio, relayRate);
228 if (stat) {
229 DEBUG_PRINT("cfsetispeed failed\n");
230 close(fd);
231 Fw::LogStringArg _arg = device;
232 Fw::LogStringArg _err = strerror(errno);
233 this->log_WARNING_HI_OpenError(_arg, fd, _err);
234 return false;
235 }
236 stat = cfsetospeed(&newtio, relayRate);
237 if (stat) {
238 DEBUG_PRINT("cfsetospeed failed\n");
239 close(fd);
240 Fw::LogStringArg _arg = device;
241 Fw::LogStringArg _err = strerror(errno);
242 this->log_WARNING_HI_OpenError(_arg, fd, _err);
243 return false;
244 }
245
246 // Raw output:
247 newtio.c_oflag = 0;
248
249 // set input mode (non-canonical, no echo,...)
250 newtio.c_lflag = 0;
251
252 newtio.c_iflag = INPCK;
253
254 // Flush old data:
255 (void)tcflush(fd, TCIFLUSH);
256
257 // Set attributes:
258 stat = tcsetattr(fd, TCSANOW, &newtio);
259 if (-1 == stat) {
260 DEBUG_PRINT("tcsetattr UART fd %d failed\n", fd);
261 close(fd);
262 Fw::LogStringArg _arg = device;
263 Fw::LogStringArg _err = strerror(errno);
264 this->log_WARNING_HI_OpenError(_arg, fd, _err);
265 return false;
266 }
267
268 // All done!
269 Fw::LogStringArg _arg = device;
270 this->log_ACTIVITY_HI_PortOpened(_arg);
271 if (this->isConnected_ready_OutputPort(0)) {
272 this->ready_out(0); // Indicate the driver is connected
273 }
274 return true;
275}
276
277LinuxUartDriver ::~LinuxUartDriver() {
278 if (this->m_fd != -1) {
279 DEBUG_PRINT("Closing UART device %d\n", this->m_fd);
280
281 (void)close(this->m_fd);
282 }
283}
284
285// ----------------------------------------------------------------------
286// Handler implementations for user-defined typed input ports
287// ----------------------------------------------------------------------
288
289Drv::SendStatus LinuxUartDriver ::send_handler(const NATIVE_INT_TYPE portNum, Fw::Buffer& serBuffer) {
290 Drv::SendStatus status = Drv::SendStatus::SEND_OK;
291 if (this->m_fd == -1 || serBuffer.getData() == nullptr || serBuffer.getSize() == 0) {
292 status = Drv::SendStatus::SEND_ERROR;
293 } else {
294 unsigned char *data = serBuffer.getData();
295 NATIVE_INT_TYPE xferSize = serBuffer.getSize();
296
297 NATIVE_INT_TYPE stat = ::write(this->m_fd, data, xferSize);
298
299 if (-1 == stat || stat != xferSize) {
300 Fw::LogStringArg _arg = this->m_device;
301 this->log_WARNING_HI_WriteError(_arg, stat);
302 status = Drv::SendStatus::SEND_ERROR;
303 }
304 }
305 // Deallocate when necessary
306 if (isConnected_deallocate_OutputPort(0)) {
307 deallocate_out(0, serBuffer);
308 }
309 return status;
310}
311
312void LinuxUartDriver ::serialReadTaskEntry(void* ptr) {
313 FW_ASSERT(ptr != nullptr);
314 Drv::RecvStatus status = RecvStatus::RECV_ERROR; // added by m.chase 03.06.2017
315 LinuxUartDriver* comp = reinterpret_cast<LinuxUartDriver*>(ptr);
316 while (!comp->m_quitReadThread) {
317 Fw::Buffer buff = comp->allocate_out(0, comp->m_allocationSize);
318
319 // On failed allocation, error and deallocate
320 if (buff.getData() == nullptr) {
321 Fw::LogStringArg _arg = comp->m_device;
322 comp->log_WARNING_HI_NoBuffers(_arg);
323 status = RecvStatus::RECV_ERROR;
324 comp->recv_out(0, buff, status);
325 // to avoid spinning, wait 50 ms
326 Os::Task::delay(50);
327 continue;
328 }
329
330 // timespec stime;
331 // (void)clock_gettime(CLOCK_REALTIME,&stime);
332 // DEBUG_PRINT("<<< Calling dsp_relay_uart_relay_read() at %d %d\n", stime.tv_sec, stime.tv_nsec);
333
334 int stat = 0;
335
336 // Read until something is received or an error occurs. Only loop when
337 // stat == 0 as this is the timeout condition and the read should spin
338 while ((stat == 0) && !comp->m_quitReadThread) {
339 stat = ::read(comp->m_fd, buff.getData(), buff.getSize());
340 }
341 buff.setSize(0);
342
343 // On error stat (-1) must mark the read as error
344 // On normal stat (>0) pass a recv ok
345 // On timeout stat (0) and m_quitReadThread, error to return the buffer
346 if (stat == -1) {
347 Fw::LogStringArg _arg = comp->m_device;
348 comp->log_WARNING_HI_ReadError(_arg, stat);
349 status = RecvStatus::RECV_ERROR;
350 } else if (stat > 0) {
351 buff.setSize(stat);
352 status = RecvStatus::RECV_OK; // added by m.chase 03.06.2017
353 } else {
354 status = RecvStatus::RECV_ERROR; // Simply to return the buffer
355 }
356 comp->recv_out(0, buff, status); // added by m.chase 03.06.2017
357 }
358}
359
360void LinuxUartDriver ::startReadThread(NATIVE_UINT_TYPE priority,
361 NATIVE_UINT_TYPE stackSize,
362 NATIVE_UINT_TYPE cpuAffinity) {
363 Os::TaskString task("SerReader");
365 this->m_readTask.start(task, serialReadTaskEntry, this, priority, stackSize, cpuAffinity);
366 FW_ASSERT(stat == Os::Task::TASK_OK, stat);
367}
368
369void LinuxUartDriver ::quitReadThread() {
370 this->m_quitReadThread = true;
371}
372
373Os::Task::TaskStatus LinuxUartDriver ::join(void** value_ptr) {
374 return m_readTask.join(value_ptr);
375}
376
377} // end namespace Drv
#define FW_ASSERT(...)
Definition Assert.hpp:7
PlatformIntType NATIVE_INT_TYPE
Definition BasicTypes.h:51
PlatformUIntType NATIVE_UINT_TYPE
Definition BasicTypes.h:52
C++ header for working with basic fprime types.
#define DEBUG_PRINT(x,...)
bool open(const char *const device, UartBaudRate baud, UartFlowControl fc, UartParity parity, NATIVE_INT_TYPE allocationSize)
UartBaudRate
Configure UART parameters.
U8 * getData() const
Definition Buffer.cpp:60
U32 getSize() const
Definition Buffer.cpp:64
void setSize(U32 size)
Definition Buffer.cpp:79
static TaskStatus delay(NATIVE_UINT_TYPE msecs)
delay the task
Definition Task.cpp:43
TaskStatus
Definition Task.hpp:18
@ TASK_OK
message sent/received okay
Definition Task.hpp:19