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