F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
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 
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 
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) {
315  if (this->m_fd == -1 || serBuffer.getData() == nullptr || serBuffer.getSize() == 0) {
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));
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);
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);
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 
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 
398  this->m_quitReadThread = true;
399 }
400 
402  return m_readTask.join();
403 }
404 
405 } // end namespace Drv
void ready_out(FwIndexType portNum) const
Invoke output port ready.
PlatformSizeType FwSizeType
void setSize(FwSizeType size)
Definition: Buffer.cpp:131
void tlmWrite_BytesRecv(FwSizeType arg, Fw::Time _tlmTime=Fw::Time()) const
bool isConnected_ready_OutputPort(FwIndexType portNum) const
U8 * getData() const
Definition: Buffer.cpp:82
Status start(const Arguments &arguments) override
start the task
Definition: Task.cpp:84
Error occurred, retrying may succeed.
Auto-generated base for LinuxUartDriver component.
void log_WARNING_HI_WriteError(const Fw::StringBase &device, I32 error)
void start(FwTaskPriorityType priority=Os::Task::TASK_PRIORITY_DEFAULT, Os::Task::ParamType stackSize=Os::Task::TASK_DEFAULT, Os::Task::ParamType cpuAffinity=Os::Task::TASK_DEFAULT)
bool open(const char *const device, UartBaudRate baud, UartFlowControl fc, UartParity parity, FwSizeType allocationSize)
void deallocate_out(FwIndexType portNum, Fw::Buffer &fwBuffer) const
Invoke output port deallocate.
Status returned by the send call.
message sent/received okay
Definition: Task.hpp:50
Status join() override
block until the task has ended
Definition: Task.cpp:141
Operation worked as expected.
void quitReadThread()
Quit thread.
void log_WARNING_HI_OpenError(const Fw::StringBase &device, I32 error, const Fw::StringBase &name) const
void log_ACTIVITY_HI_PortOpened(const Fw::StringBase &device) const
PlatformTaskPriorityType FwTaskPriorityType
The type of task priorities used.
FwSizeType ParamType
backwards-compatible parameter type
Definition: Task.hpp:233
LinuxUartDriver(const char *const compName)
FwSizeType getSize() const
Definition: Buffer.cpp:90
UartBaudRate
Configure UART parameters.
PlatformIndexType FwIndexType
#define FW_ASSERT_NO_OVERFLOW(value, T)
Definition: Assert.hpp:48
C++ header for working with basic fprime types.
static Status delay(const Fw::TimeInterval &interval)
delay the current task
Definition: Task.cpp:204
void tlmWrite_BytesSent(FwSizeType arg, Fw::Time _tlmTime=Fw::Time()) const
Os::Task::Status join()
Join thread.
#define FW_ASSERT(...)
Definition: Assert.hpp:14