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