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, this->m_fd, _err);
63  return false;
64  }
65 
66  this->m_fd = fd;
67 
68  // Configure blocking reads
69  struct termios cfg;
70 
71  stat = tcgetattr(fd, &cfg);
72  if (-1 == stat) {
73  (void)close(fd);
74  Fw::LogStringArg _arg = device;
75  Fw::LogStringArg _err = strerror(errno);
76  this->log_WARNING_HI_OpenError(_arg, fd, _err);
77  return false;
78  }
79 
80  /*
81  If MIN > 0 and TIME = 0, MIN sets the number of characters to receive before the read is satisfied. As TIME is
82  zero, the timer is not used.
83 
84  If MIN = 0 and TIME > 0, TIME serves as a timeout value. The read will be satisfied if a single character is read,
85  or TIME is exceeded (t = TIME *0.1 s). If TIME is exceeded, no character will be returned.
86 
87  If MIN > 0 and TIME > 0, TIME serves as an inter-character timer. The read will be satisfied if MIN characters are
88  received, or the time between two characters exceeds TIME. The timer is restarted every time a character is
89  received and only becomes active after the first character has been received.
90 
91  If MIN = 0 and TIME = 0, read will be satisfied immediately. The number of characters currently available, or the
92  number of characters requested will be returned. According to Antonino (see contributions), you could issue a
93  fcntl(fd, F_SETFL, FNDELAY); before reading to get the same result.
94  */
95  cfg.c_cc[VMIN] = 0;
96  cfg.c_cc[VTIME] = 10; // 1 sec timeout on no-data
97 
98  stat = tcsetattr(fd, TCSANOW, &cfg);
99  if (-1 == stat) {
100  (void)close(fd);
101  Fw::LogStringArg _arg = device;
102  Fw::LogStringArg _err = strerror(errno);
103  this->log_WARNING_HI_OpenError(_arg, fd, _err);
104  return false;
105  }
106 
107  // Set flow control
108  if (fc == HW_FLOW) {
109  struct termios t;
110 
111  stat = tcgetattr(fd, &t);
112  if (-1 == stat) {
113  (void)close(fd);
114  Fw::LogStringArg _arg = device;
115  Fw::LogStringArg _err = strerror(errno);
116  this->log_WARNING_HI_OpenError(_arg, fd, _err);
117  return false;
118  }
119 
120  // modify flow control flags
121  t.c_cflag |= CRTSCTS;
122 
123  stat = tcsetattr(fd, TCSANOW, &t);
124  if (-1 == stat) {
125  (void)close(fd);
126  Fw::LogStringArg _arg = device;
127  Fw::LogStringArg _err = strerror(errno);
128  this->log_WARNING_HI_OpenError(_arg, fd, _err);
129  return false;
130  }
131  }
132 
133  int relayRate = B0;
134  switch (baud) {
135  case BAUD_9600:
136  relayRate = B9600;
137  break;
138  case BAUD_19200:
139  relayRate = B19200;
140  break;
141  case BAUD_38400:
142  relayRate = B38400;
143  break;
144  case BAUD_57600:
145  relayRate = B57600;
146  break;
147  case BAUD_115K:
148  relayRate = B115200;
149  break;
150  case BAUD_230K:
151  relayRate = B230400;
152  break;
153 #ifdef B460800
154  case BAUD_460K:
155  relayRate = B460800;
156  break;
157 #endif
158 #ifdef B921600
159  case BAUD_921K:
160  relayRate = B921600;
161  break;
162 #endif
163 #ifdef B1000000
164  case BAUD_1000K:
165  relayRate = B1000000;
166  break;
167 #endif
168 #ifdef B1152000
169  case BAUD_1152K:
170  relayRate = B1152000;
171  break;
172 #endif
173 #ifdef B1500000
174  case BAUD_1500K:
175  relayRate = B1500000;
176  break;
177 #endif
178 #ifdef B2000000
179  case BAUD_2000K:
180  relayRate = B2000000;
181  break;
182 #endif
183 #ifdef B2500000
184  case BAUD_2500K:
185  relayRate = B2500000;
186  break;
187 #endif
188 #ifdef B3000000
189  case BAUD_3000K:
190  relayRate = B3000000;
191  break;
192 #endif
193 #ifdef B3500000
194  case BAUD_3500K:
195  relayRate = B3500000;
196  break;
197 #endif
198 #ifdef B4000000
199  case BAUD_4000K:
200  relayRate = B4000000;
201  break;
202 #endif
203  default:
204  FW_ASSERT(false, static_cast<FwAssertArgType>(baud));
205  break;
206  }
207 
208  struct termios newtio;
209 
210  stat = tcgetattr(fd, &newtio);
211  if (-1 == stat) {
212  (void)close(fd);
213  Fw::LogStringArg _arg = device;
214  Fw::LogStringArg _err = strerror(errno);
215  this->log_WARNING_HI_OpenError(_arg, fd, _err);
216  return false;
217  }
218 
219  // CS8 = 8 data bits, CLOCAL = Local line, CREAD = Enable Receiver
220  /*
221  Even parity (7E1):
222  options.c_cflag |= PARENB
223  options.c_cflag &= ~PARODD
224  options.c_cflag &= ~CSTOPB
225  options.c_cflag &= ~CSIZE;
226  options.c_cflag |= CS7;
227  Odd parity (7O1):
228  options.c_cflag |= PARENB
229  options.c_cflag |= PARODD
230  options.c_cflag &= ~CSTOPB
231  options.c_cflag &= ~CSIZE;
232  options.c_cflag |= CS7;
233  */
234  newtio.c_cflag |= CS8 | CLOCAL | CREAD;
235 
236  switch (parity) {
237  case PARITY_ODD:
238  newtio.c_cflag |= (PARENB | PARODD);
239  break;
240  case PARITY_EVEN:
241  newtio.c_cflag |= PARENB;
242  break;
243  case PARITY_NONE:
244  newtio.c_cflag &= static_cast<unsigned int>(~PARENB);
245  break;
246  default:
247  FW_ASSERT(false, parity);
248  break;
249  }
250 
251  // Set baud rate:
252  stat = cfsetispeed(&newtio, static_cast<speed_t>(relayRate));
253  if (stat) {
254  (void)close(fd);
255  Fw::LogStringArg _arg = device;
256  Fw::LogStringArg _err = strerror(errno);
257  this->log_WARNING_HI_OpenError(_arg, fd, _err);
258  return false;
259  }
260  stat = cfsetospeed(&newtio, static_cast<speed_t>(relayRate));
261  if (stat) {
262  (void)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 
269  // Raw output:
270  newtio.c_oflag = 0;
271 
272  // set input mode (non-canonical, no echo,...)
273  newtio.c_lflag = 0;
274 
275  newtio.c_iflag = INPCK;
276 
277  // Flush old data:
278  (void)tcflush(fd, TCIFLUSH);
279 
280  // Set attributes:
281  stat = tcsetattr(fd, TCSANOW, &newtio);
282  if (-1 == stat) {
283  (void)close(fd);
284  Fw::LogStringArg _arg = device;
285  Fw::LogStringArg _err = strerror(errno);
286  this->log_WARNING_HI_OpenError(_arg, fd, _err);
287  return false;
288  }
289 
290  // All done!
291  Fw::LogStringArg _arg = device;
292  this->log_ACTIVITY_HI_PortOpened(_arg);
293  if (this->isConnected_ready_OutputPort(0)) {
294  this->ready_out(0); // Indicate the driver is connected
295  }
296  return true;
297 }
298 
300  if (this->m_fd != -1) {
301  (void)close(this->m_fd);
302  }
303 }
304 
305 // ----------------------------------------------------------------------
306 // Handler implementations for user-defined typed input ports
307 // ----------------------------------------------------------------------
308 
309 void LinuxUartDriver ::run_handler(FwIndexType portNum, U32 context) {
310  this->tlmWrite_BytesSent(this->m_bytesSent);
311  this->tlmWrite_BytesRecv(this->m_bytesReceived);
312 }
313 
314 Drv::ByteStreamStatus LinuxUartDriver ::send_handler(const FwIndexType portNum, Fw::Buffer& serBuffer) {
316  if (this->m_fd == -1 || serBuffer.getData() == nullptr || serBuffer.getSize() == 0) {
318  } else {
319  unsigned char* data = serBuffer.getData();
320  FW_ASSERT_NO_OVERFLOW(serBuffer.getSize(), size_t);
321  size_t xferSize = static_cast<size_t>(serBuffer.getSize());
322 
323  ssize_t stat = ::write(this->m_fd, data, xferSize);
324 
325  if (-1 == stat || static_cast<size_t>(stat) != xferSize) {
326  Fw::LogStringArg _arg = this->m_device;
327  this->log_WARNING_HI_WriteError(_arg, static_cast<I32>(stat));
329  } else {
330  this->m_bytesSent += static_cast<FwSizeType>(stat);
331  }
332  }
333  return status;
334 }
335 
336 void LinuxUartDriver::recvReturnIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer) {
337  this->deallocate_out(0, fwBuffer);
338 }
339 
340 void LinuxUartDriver ::serialReadTaskEntry(void* ptr) {
341  FW_ASSERT(ptr != nullptr);
342  Drv::ByteStreamStatus status = ByteStreamStatus::OTHER_ERROR; // added by m.chase 03.06.2017
343  LinuxUartDriver* comp = reinterpret_cast<LinuxUartDriver*>(ptr);
344  // @non-terminating@: read thread runs until quit is requested
345  while (!comp->m_quitReadThread) {
346  Fw::Buffer buff = comp->allocate_out(0, comp->m_allocationSize);
347 
348  // On failed allocation, error
349  if (buff.getData() == nullptr) {
350  Fw::LogStringArg _arg = comp->m_device;
351  comp->log_WARNING_HI_NoBuffers(_arg);
353  comp->recv_out(0, buff, status);
354  // to avoid spinning, wait 50 ms
355  (void)Os::Task::delay(Fw::TimeInterval(0, 50000)); // best-effort delay
356  continue;
357  }
358 
359  int stat = 0;
360 
361  // Read until something is received or an error occurs. Only loop when
362  // stat == 0 as this is the timeout condition and the read should spin
363  FW_ASSERT_NO_OVERFLOW(buff.getSize(), size_t);
364  // @non-terminating@: retry read until data arrives or quit is requested
365  while ((stat == 0) && !comp->m_quitReadThread) {
366  stat = static_cast<int>(::read(comp->m_fd, buff.getData(), static_cast<size_t>(buff.getSize())));
367  }
368  buff.setSize(0);
369 
370  // On error stat (-1) must mark the read as error
371  // On normal stat (>0) pass a recv ok
372  // On timeout stat (0) and m_quitReadThread, error to return the buffer
373  if (stat == -1) {
374  Fw::LogStringArg _arg = comp->m_device;
375  comp->log_WARNING_HI_ReadError(_arg, stat);
377  } else if (stat > 0) {
378  buff.setSize(static_cast<U32>(stat));
379  status = ByteStreamStatus::OP_OK; // added by m.chase 03.06.2017
380  comp->m_bytesReceived += static_cast<FwSizeType>(stat);
381  } else {
382  status = ByteStreamStatus::OTHER_ERROR; // Simply to return the buffer
383  }
384 
385  comp->recv_out(0, buff, status); // added by m.chase 03.06.2017
386  }
387 }
388 
390  Os::Task::ParamType stackSize,
391  Os::Task::ParamType cpuAffinity) {
392  Os::TaskString task("SerReader");
393  Os::Task::Arguments arguments(task, serialReadTaskEntry, this, priority, stackSize, cpuAffinity);
394  Os::Task::Status stat = this->m_readTask.start(arguments);
395  FW_ASSERT(stat == Os::Task::OP_OK, stat);
396 }
397 
399  this->m_quitReadThread = true;
400 }
401 
403  return m_readTask.join();
404 }
405 
406 } // end namespace Drv
void ready_out(FwIndexType portNum) const
Invoke output port ready.
PlatformSizeType FwSizeType
void setSize(FwSizeType size)
Definition: Buffer.cpp:75
void tlmWrite_BytesRecv(FwSizeType arg, Fw::Time _tlmTime=Fw::Time()) const
bool isConnected_ready_OutputPort(FwIndexType portNum) const
U8 * getData() const
Definition: Buffer.cpp:56
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:60
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