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