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  NATIVE_INT_TYPE fd = -1;
41  NATIVE_INT_TYPE 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  NATIVE_INT_TYPE 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 Drv::SendStatus LinuxUartDriver ::send_handler(const NATIVE_INT_TYPE 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  NATIVE_INT_TYPE xferSize = static_cast<NATIVE_INT_TYPE>(serBuffer.getSize());
302 
303  NATIVE_INT_TYPE stat = static_cast<NATIVE_INT_TYPE>(::write(this->m_fd, data, static_cast<size_t>(xferSize)));
304 
305  if (-1 == stat || stat != xferSize) {
306  Fw::LogStringArg _arg = this->m_device;
307  this->log_WARNING_HI_WriteError(_arg, stat);
309  }
310  }
311  // Deallocate when necessary
313  deallocate_out(0, serBuffer);
314  }
315  return status;
316 }
317 
318 void LinuxUartDriver ::serialReadTaskEntry(void* ptr) {
319  FW_ASSERT(ptr != nullptr);
320  Drv::RecvStatus status = RecvStatus::RECV_ERROR; // added by m.chase 03.06.2017
321  LinuxUartDriver* comp = reinterpret_cast<LinuxUartDriver*>(ptr);
322  while (!comp->m_quitReadThread) {
323  Fw::Buffer buff = comp->allocate_out(0,comp->m_allocationSize);
324 
325  // On failed allocation, error and deallocate
326  if (buff.getData() == nullptr) {
327  Fw::LogStringArg _arg = comp->m_device;
328  comp->log_WARNING_HI_NoBuffers(_arg);
329  status = RecvStatus::RECV_ERROR;
330  comp->recv_out(0, buff, status);
331  // to avoid spinning, wait 50 ms
333  continue;
334  }
335 
336  int stat = 0;
337 
338  // Read until something is received or an error occurs. Only loop when
339  // stat == 0 as this is the timeout condition and the read should spin
340  while ((stat == 0) && !comp->m_quitReadThread) {
341  stat = static_cast<int>(::read(comp->m_fd, buff.getData(), buff.getSize()));
342  }
343  buff.setSize(0);
344 
345  // On error stat (-1) must mark the read as error
346  // On normal stat (>0) pass a recv ok
347  // On timeout stat (0) and m_quitReadThread, error to return the buffer
348  if (stat == -1) {
349  Fw::LogStringArg _arg = comp->m_device;
350  comp->log_WARNING_HI_ReadError(_arg, stat);
351  status = RecvStatus::RECV_ERROR;
352  } else if (stat > 0) {
353  buff.setSize(static_cast<U32>(stat));
354  status = RecvStatus::RECV_OK; // added by m.chase 03.06.2017
355  } else {
356  status = RecvStatus::RECV_ERROR; // Simply to return the buffer
357  }
358  comp->recv_out(0, buff, status); // added by m.chase 03.06.2017
359  }
360 }
361 
363  Os::TaskString task("SerReader");
364  Os::Task::Arguments arguments(task, serialReadTaskEntry, this, priority, stackSize, cpuAffinity);
365  Os::Task::Status stat = this->m_readTask.start(arguments);
366  FW_ASSERT(stat == Os::Task::OP_OK, stat);
367 }
368 
370  this->m_quitReadThread = true;
371 }
372 
374  return m_readTask.join();
375 }
376 
377 } // end namespace Drv
static Status delay(Fw::TimeInterval interval)
delay the current task
Definition: Task.cpp:191
bool isConnected_deallocate_OutputPort(FwIndexType portNum)
Status associated with the received data.
PlatformIntType NATIVE_INT_TYPE
Definition: BasicTypes.h:55
U8 * getData() const
Definition: Buffer.cpp:68
Status start(const Arguments &arguments) override
start the task
Definition: Task.cpp:82
Auto-generated base for LinuxUartDriver component.
U32 getSize() const
Definition: Buffer.cpp:72
Receive worked as expected.
void log_WARNING_HI_WriteError(const Fw::StringBase &device, I32 error)
Receive error occurred retrying may succeed.
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)
Send worked as expected.
void deallocate_out(FwIndexType portNum, Fw::Buffer &fwBuffer)
Invoke output port deallocate.
message sent/received okay
Definition: Task.hpp:30
void setSize(U32 size)
Definition: Buffer.cpp:87
Status join() override
block until the task has ended
Definition: Task.cpp:134
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)
Status returned by the send call.
UartBaudRate
Configure UART parameters.
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)
Os::Task::Status join()
Join thread.
#define FW_ASSERT(...)
Definition: Assert.hpp:14
Send error occurred retrying may succeed.