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