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  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  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  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  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 #if defined TGT_OS_TYPE_LINUX
154  case BAUD_460K:
155  relayRate = B460800;
156  break;
157  case BAUD_921K:
158  relayRate = B921600;
159  break;
160  case BAUD_1000K:
161  relayRate = B1000000;
162  break;
163  case BAUD_1152K:
164  relayRate = B1152000;
165  break;
166  case BAUD_1500K:
167  relayRate = B1500000;
168  break;
169  case BAUD_2000K:
170  relayRate = B2000000;
171  break;
172 #ifdef B2500000
173  case BAUD_2500K:
174  relayRate = B2500000;
175  break;
176 #endif
177 #ifdef B3000000
178  case BAUD_3000K:
179  relayRate = B3000000;
180  break;
181 #endif
182 #ifdef B3500000
183  case BAUD_3500K:
184  relayRate = B3500000;
185  break;
186 #endif
187 #ifdef B4000000
188  case BAUD_4000K:
189  relayRate = B4000000;
190  break;
191 #endif
192 #endif
193  default:
194  FW_ASSERT(0, static_cast<FwAssertArgType>(baud));
195  break;
196  }
197 
198  struct termios newtio;
199 
200  stat = tcgetattr(fd, &newtio);
201  if (-1 == stat) {
202  close(fd);
203  Fw::LogStringArg _arg = device;
204  Fw::LogStringArg _err = strerror(errno);
205  this->log_WARNING_HI_OpenError(_arg, fd, _err);
206  return false;
207  }
208 
209  // CS8 = 8 data bits, CLOCAL = Local line, CREAD = Enable Receiver
210  /*
211  Even parity (7E1):
212  options.c_cflag |= PARENB
213  options.c_cflag &= ~PARODD
214  options.c_cflag &= ~CSTOPB
215  options.c_cflag &= ~CSIZE;
216  options.c_cflag |= CS7;
217  Odd parity (7O1):
218  options.c_cflag |= PARENB
219  options.c_cflag |= PARODD
220  options.c_cflag &= ~CSTOPB
221  options.c_cflag &= ~CSIZE;
222  options.c_cflag |= CS7;
223  */
224  newtio.c_cflag |= CS8 | CLOCAL | CREAD;
225 
226  switch (parity) {
227  case PARITY_ODD:
228  newtio.c_cflag |= (PARENB | PARODD);
229  break;
230  case PARITY_EVEN:
231  newtio.c_cflag |= PARENB;
232  break;
233  case PARITY_NONE:
234  newtio.c_cflag &= static_cast<unsigned int>(~PARENB);
235  break;
236  default:
237  FW_ASSERT(0, parity);
238  break;
239  }
240 
241  // Set baud rate:
242  stat = cfsetispeed(&newtio, static_cast<speed_t>(relayRate));
243  if (stat) {
244  close(fd);
245  Fw::LogStringArg _arg = device;
246  Fw::LogStringArg _err = strerror(errno);
247  this->log_WARNING_HI_OpenError(_arg, fd, _err);
248  return false;
249  }
250  stat = cfsetospeed(&newtio, static_cast<speed_t>(relayRate));
251  if (stat) {
252  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 
259  // Raw output:
260  newtio.c_oflag = 0;
261 
262  // set input mode (non-canonical, no echo,...)
263  newtio.c_lflag = 0;
264 
265  newtio.c_iflag = INPCK;
266 
267  // Flush old data:
268  (void)tcflush(fd, TCIFLUSH);
269 
270  // Set attributes:
271  stat = tcsetattr(fd, TCSANOW, &newtio);
272  if (-1 == stat) {
273  close(fd);
274  Fw::LogStringArg _arg = device;
275  Fw::LogStringArg _err = strerror(errno);
276  this->log_WARNING_HI_OpenError(_arg, fd, _err);
277  return false;
278  }
279 
280  // All done!
281  Fw::LogStringArg _arg = device;
282  this->log_ACTIVITY_HI_PortOpened(_arg);
283  if (this->isConnected_ready_OutputPort(0)) {
284  this->ready_out(0); // Indicate the driver is connected
285  }
286  return true;
287 }
288 
290  if (this->m_fd != -1) {
291  (void)close(this->m_fd);
292  }
293 }
294 
295 // ----------------------------------------------------------------------
296 // Handler implementations for user-defined typed input ports
297 // ----------------------------------------------------------------------
298 
299 void LinuxUartDriver ::run_handler(FwIndexType portNum, U32 context) {
300  this->tlmWrite_BytesSent(this->m_bytesSent);
301  this->tlmWrite_BytesRecv(this->m_bytesReceived);
302 }
303 
304 Drv::ByteStreamStatus LinuxUartDriver ::send_handler(const FwIndexType portNum, Fw::Buffer& serBuffer) {
306  if (this->m_fd == -1 || serBuffer.getData() == nullptr || serBuffer.getSize() == 0) {
308  } else {
309  unsigned char* data = serBuffer.getData();
310  FW_ASSERT_NO_OVERFLOW(serBuffer.getSize(), size_t);
311  size_t xferSize = static_cast<size_t>(serBuffer.getSize());
312 
313  ssize_t stat = ::write(this->m_fd, data, xferSize);
314 
315  if (-1 == stat || static_cast<size_t>(stat) != xferSize) {
316  Fw::LogStringArg _arg = this->m_device;
317  this->log_WARNING_HI_WriteError(_arg, static_cast<I32>(stat));
319  } else {
320  this->m_bytesSent += static_cast<FwSizeType>(stat);
321  }
322  }
323  return status;
324 }
325 
326 void LinuxUartDriver::recvReturnIn_handler(FwIndexType portNum, Fw::Buffer& fwBuffer) {
327  this->deallocate_out(0, fwBuffer);
328 }
329 
330 void LinuxUartDriver ::serialReadTaskEntry(void* ptr) {
331  FW_ASSERT(ptr != nullptr);
332  Drv::ByteStreamStatus status = ByteStreamStatus::OTHER_ERROR; // added by m.chase 03.06.2017
333  LinuxUartDriver* comp = reinterpret_cast<LinuxUartDriver*>(ptr);
334  while (!comp->m_quitReadThread) {
335  Fw::Buffer buff = comp->allocate_out(0, comp->m_allocationSize);
336 
337  // On failed allocation, error
338  if (buff.getData() == nullptr) {
339  Fw::LogStringArg _arg = comp->m_device;
340  comp->log_WARNING_HI_NoBuffers(_arg);
342  comp->recv_out(0, buff, status);
343  // to avoid spinning, wait 50 ms
345  continue;
346  }
347 
348  int stat = 0;
349 
350  // Read until something is received or an error occurs. Only loop when
351  // stat == 0 as this is the timeout condition and the read should spin
352  FW_ASSERT_NO_OVERFLOW(buff.getSize(), size_t);
353  while ((stat == 0) && !comp->m_quitReadThread) {
354  stat = static_cast<int>(::read(comp->m_fd, buff.getData(), static_cast<size_t>(buff.getSize())));
355  }
356  buff.setSize(0);
357 
358  // On error stat (-1) must mark the read as error
359  // On normal stat (>0) pass a recv ok
360  // On timeout stat (0) and m_quitReadThread, error to return the buffer
361  if (stat == -1) {
362  Fw::LogStringArg _arg = comp->m_device;
363  comp->log_WARNING_HI_ReadError(_arg, stat);
365  } else if (stat > 0) {
366  buff.setSize(static_cast<U32>(stat));
367  status = ByteStreamStatus::OP_OK; // added by m.chase 03.06.2017
368  comp->m_bytesReceived += static_cast<FwSizeType>(stat);
369  } else {
370  status = ByteStreamStatus::OTHER_ERROR; // Simply to return the buffer
371  }
372 
373  comp->recv_out(0, buff, status); // added by m.chase 03.06.2017
374  }
375 }
376 
378  Os::Task::ParamType stackSize,
379  Os::Task::ParamType cpuAffinity) {
380  Os::TaskString task("SerReader");
381  Os::Task::Arguments arguments(task, serialReadTaskEntry, this, priority, stackSize, cpuAffinity);
382  Os::Task::Status stat = this->m_readTask.start(arguments);
383  FW_ASSERT(stat == Os::Task::OP_OK, stat);
384 }
385 
387  this->m_quitReadThread = true;
388 }
389 
391  return m_readTask.join();
392 }
393 
394 } // end namespace Drv
static Status delay(Fw::TimeInterval interval)
delay the current task
Definition: Task.cpp:204
PlatformSizeType FwSizeType
void setSize(FwSizeType size)
Definition: Buffer.cpp:75
void tlmWrite_BytesRecv(FwSizeType arg, Fw::Time _tlmTime=Fw::Time()) 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)
void deallocate_out(FwIndexType portNum, Fw::Buffer &fwBuffer)
Invoke output port deallocate.
bool open(const char *const device, UartBaudRate baud, UartFlowControl fc, UartParity parity, FwSizeType allocationSize)
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:232
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:49
C++ header for working with basic fprime types.
void ready_out(FwIndexType portNum)
Invoke output port ready.
bool isConnected_ready_OutputPort(FwIndexType portNum)
void tlmWrite_BytesSent(FwSizeType arg, Fw::Time _tlmTime=Fw::Time()) const
Os::Task::Status join()
Join thread.
#define FW_ASSERT(...)
Definition: Assert.hpp:14