F´ Flight Software - C/C++ Documentation devel
A framework for building embedded system applications to NASA flight quality standards.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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(...) printf(##__VA_ARGS__); fflush(stdout)
26#define DEBUG_PRINT(...)
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) {
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 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 case BAUD_1000K:
177 relayRate = B1000000;
178 break;
179 case BAUD_1152K:
180 relayRate = B1152000;
181 break;
182 case BAUD_1500K:
183 relayRate = B1500000;
184 break;
185 case BAUD_2000K:
186 relayRate = B2000000;
187 break;
188#ifdef B2500000
189 case BAUD_2500K:
190 relayRate = B2500000;
191 break;
192#endif
193#ifdef B3000000
194 case BAUD_3000K:
195 relayRate = B3000000;
196 break;
197#endif
198#ifdef B3500000
199 case BAUD_3500K:
200 relayRate = B3500000;
201 break;
202#endif
203#ifdef B4000000
204 case BAUD_4000K:
205 relayRate = B4000000;
206 break;
207#endif
208#endif
209 default:
210 FW_ASSERT(0, baud);
211 break;
212 }
213
214 struct termios newtio;
215
216 stat = tcgetattr(fd, &newtio);
217 if (-1 == stat) {
218 DEBUG_PRINT("tcgetattr UART fd %d failed\n", fd);
219 close(fd);
220 Fw::LogStringArg _arg = device;
221 Fw::LogStringArg _err = strerror(errno);
222 this->log_WARNING_HI_OpenError(_arg, fd, _err);
223 return false;
224 }
225
226 // CS8 = 8 data bits, CLOCAL = Local line, CREAD = Enable Receiver
227 /*
228 Even parity (7E1):
229 options.c_cflag |= PARENB
230 options.c_cflag &= ~PARODD
231 options.c_cflag &= ~CSTOPB
232 options.c_cflag &= ~CSIZE;
233 options.c_cflag |= CS7;
234 Odd parity (7O1):
235 options.c_cflag |= PARENB
236 options.c_cflag |= PARODD
237 options.c_cflag &= ~CSTOPB
238 options.c_cflag &= ~CSIZE;
239 options.c_cflag |= CS7;
240 */
241 newtio.c_cflag |= CS8 | CLOCAL | CREAD;
242
243 switch (parity) {
244 case PARITY_ODD:
245 newtio.c_cflag |= (PARENB | PARODD);
246 break;
247 case PARITY_EVEN:
248 newtio.c_cflag |= PARENB;
249 break;
250 case PARITY_NONE:
251 newtio.c_cflag &= ~PARENB;
252 break;
253 default:
254 FW_ASSERT(0, parity);
255 break;
256 }
257
258 // Set baud rate:
259 stat = cfsetispeed(&newtio, relayRate);
260 if (stat) {
261 DEBUG_PRINT("cfsetispeed failed\n");
262 close(fd);
263 Fw::LogStringArg _arg = device;
264 Fw::LogStringArg _err = strerror(errno);
265 this->log_WARNING_HI_OpenError(_arg, fd, _err);
266 return false;
267 }
268 stat = cfsetospeed(&newtio, relayRate);
269 if (stat) {
270 DEBUG_PRINT("cfsetospeed failed\n");
271 close(fd);
272 Fw::LogStringArg _arg = device;
273 Fw::LogStringArg _err = strerror(errno);
274 this->log_WARNING_HI_OpenError(_arg, fd, _err);
275 return false;
276 }
277
278 // Raw output:
279 newtio.c_oflag = 0;
280
281 // set input mode (non-canonical, no echo,...)
282 newtio.c_lflag = 0;
283
284 newtio.c_iflag = INPCK;
285
286 // Flush old data:
287 (void)tcflush(fd, TCIFLUSH);
288
289 // Set attributes:
290 stat = tcsetattr(fd, TCSANOW, &newtio);
291 if (-1 == stat) {
292 DEBUG_PRINT("tcsetattr UART fd %d failed\n", fd);
293 close(fd);
294 Fw::LogStringArg _arg = device;
295 Fw::LogStringArg _err = strerror(errno);
296 this->log_WARNING_HI_OpenError(_arg, fd, _err);
297 return false;
298 }
299
300 // All done!
301 Fw::LogStringArg _arg = device;
302 this->log_ACTIVITY_HI_PortOpened(_arg);
303 if (this->isConnected_ready_OutputPort(0)) {
304 this->ready_out(0); // Indicate the driver is connected
305 }
306 return true;
307}
308
309LinuxUartDriver ::~LinuxUartDriver() {
310 if (this->m_fd != -1) {
311 DEBUG_PRINT("Closing UART device %d\n", this->m_fd);
312
313 (void)close(this->m_fd);
314 }
315}
316
317// ----------------------------------------------------------------------
318// Handler implementations for user-defined typed input ports
319// ----------------------------------------------------------------------
320
321Drv::SendStatus LinuxUartDriver ::send_handler(const NATIVE_INT_TYPE portNum, Fw::Buffer& serBuffer) {
323 if (this->m_fd == -1 || serBuffer.getData() == nullptr || serBuffer.getSize() == 0) {
325 } else {
326 unsigned char *data = serBuffer.getData();
327 NATIVE_INT_TYPE xferSize = serBuffer.getSize();
328
329 NATIVE_INT_TYPE stat = ::write(this->m_fd, data, xferSize);
330
331 if (-1 == stat || stat != xferSize) {
332 Fw::LogStringArg _arg = this->m_device;
333 this->log_WARNING_HI_WriteError(_arg, stat);
335 }
336 }
337 // Deallocate when necessary
338 if (isConnected_deallocate_OutputPort(0)) {
339 deallocate_out(0, serBuffer);
340 }
341 return status;
342}
343
344void LinuxUartDriver ::serialReadTaskEntry(void* ptr) {
345 FW_ASSERT(ptr != nullptr);
346 Drv::RecvStatus status = RecvStatus::RECV_ERROR; // added by m.chase 03.06.2017
347 LinuxUartDriver* comp = reinterpret_cast<LinuxUartDriver*>(ptr);
348 while (!comp->m_quitReadThread) {
349 Fw::Buffer buff = comp->allocate_out(0, comp->m_allocationSize);
350
351 // On failed allocation, error and deallocate
352 if (buff.getData() == nullptr) {
353 Fw::LogStringArg _arg = comp->m_device;
354 comp->log_WARNING_HI_NoBuffers(_arg);
355 status = RecvStatus::RECV_ERROR;
356 comp->recv_out(0, buff, status);
357 // to avoid spinning, wait 50 ms
358 Os::Task::delay(50);
359 continue;
360 }
361
362 // timespec stime;
363 // (void)clock_gettime(CLOCK_REALTIME,&stime);
364 // DEBUG_PRINT("<<< Calling dsp_relay_uart_relay_read() at %d %d\n", stime.tv_sec, stime.tv_nsec);
365
366 int stat = 0;
367
368 // Read until something is received or an error occurs. Only loop when
369 // stat == 0 as this is the timeout condition and the read should spin
370 while ((stat == 0) && !comp->m_quitReadThread) {
371 stat = ::read(comp->m_fd, buff.getData(), buff.getSize());
372 }
373 buff.setSize(0);
374
375 // On error stat (-1) must mark the read as error
376 // On normal stat (>0) pass a recv ok
377 // On timeout stat (0) and m_quitReadThread, error to return the buffer
378 if (stat == -1) {
379 Fw::LogStringArg _arg = comp->m_device;
380 comp->log_WARNING_HI_ReadError(_arg, stat);
381 status = RecvStatus::RECV_ERROR;
382 } else if (stat > 0) {
383 buff.setSize(stat);
384 status = RecvStatus::RECV_OK; // added by m.chase 03.06.2017
385 } else {
386 status = RecvStatus::RECV_ERROR; // Simply to return the buffer
387 }
388 comp->recv_out(0, buff, status); // added by m.chase 03.06.2017
389 }
390}
391
392void LinuxUartDriver ::startReadThread(NATIVE_UINT_TYPE priority,
393 NATIVE_UINT_TYPE stackSize,
394 NATIVE_UINT_TYPE cpuAffinity) {
395 Os::TaskString task("SerReader");
397 this->m_readTask.start(task, serialReadTaskEntry, this, priority, stackSize, cpuAffinity);
398 FW_ASSERT(stat == Os::Task::TASK_OK, stat);
399}
400
401void LinuxUartDriver ::quitReadThread() {
402 this->m_quitReadThread = true;
403}
404
405Os::Task::TaskStatus LinuxUartDriver ::join(void** value_ptr) {
406 return m_readTask.join(value_ptr);
407}
408
409} // end namespace Drv
#define FW_ASSERT(...)
Definition Assert.hpp:14
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(...)
Auto-generated base for LinuxUartDriver component.
void log_ACTIVITY_HI_PortOpened(const Fw::LogStringArg &device)
void log_WARNING_HI_OpenError(const Fw::LogStringArg &device, I32 error, const Fw::LogStringArg &name)
void ready_out(NATIVE_INT_TYPE portNum)
Invoke output port ready.
bool isConnected_ready_OutputPort(NATIVE_INT_TYPE portNum)
bool open(const char *const device, UartBaudRate baud, UartFlowControl fc, UartParity parity, NATIVE_INT_TYPE allocationSize)
UartBaudRate
Configure UART parameters.
Status associated with the received data.
@ RECV_OK
Receive worked as expected.
@ RECV_ERROR
Receive error occurred retrying may succeed.
Status returned by the send call.
@ SEND_ERROR
Send error occurred retrying may succeed.
@ SEND_OK
Send worked as expected.
U8 * getData() const
Definition Buffer.cpp:68
U32 getSize() const
Definition Buffer.cpp:72
void setSize(U32 size)
Definition Buffer.cpp:87
void init()
Object initializer.
Definition ObjBase.cpp:27
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