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