F´ Flight Software - C/C++ Documentation  devel
A framework for building embedded system applications to NASA flight quality standards.
LinuxGpioDriverComponentImpl.cpp
Go to the documentation of this file.
1 // ======================================================================
2 // \title LinuxGpioDriverImpl.cpp
3 // \author tcanham
4 // \brief cpp file for LinuxGpioDriver 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 
15 #include <FpConfig.hpp>
16 #include <Os/TaskString.hpp>
17 
18 // TODO make proper static constants for these
19 #define SYSFS_GPIO_DIR "/sys/class/gpio"
20 #define MAX_BUF 64
21 
22 #include <cstdio>
23 #include <cstdlib>
24 #include <cstring>
25 #include <cerrno>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <poll.h>
29 
30 //#define DEBUG_PRINT(...) printf(##__VA_ARGS__); fflush(stdout)
31 #define DEBUG_PRINT(...)
32 
33 namespace Drv {
34 
35 
36 // Code modified from https://developer.ridgerun.com/wiki/index.php?title=Gpio-int-test.c
37  /****************************************************************
38  * gpio_export
39  ****************************************************************/
40  int gpio_export(unsigned int gpio)
41  {
42  int fd, len;
43  char buf[MAX_BUF];
44 
45  fd = open(SYSFS_GPIO_DIR "/export", O_WRONLY);
46  if (fd < 0) {
47  DEBUG_PRINT("gpio/export error!\n");
48  return -1;
49  }
50 
51  // TODO check value of len
52  len = snprintf(buf, sizeof(buf), "%u", gpio);
53  if(write(fd, buf, static_cast<size_t>(len)) != len) {
54  (void) close(fd);
55  DEBUG_PRINT("gpio/export error!\n");
56  return -1;
57  }
58  (void) close(fd);
59 
60  /* NOTE(mereweth) - this is to allow systemd udev to make
61  * necessary filesystem changes after exporting
62  */
63  usleep(100 * 1000);
64 
65  return 0;
66  }
67 
68  /****************************************************************
69  * gpio_unexport
70  ****************************************************************/
71  int gpio_unexport(unsigned int gpio)
72  {
73  int fd, len;
74  char buf[MAX_BUF];
75 
76  fd = open(SYSFS_GPIO_DIR "/unexport", O_WRONLY);
77  if (fd < 0) {
78  DEBUG_PRINT("gpio/unexport error!\n");
79  return -1;
80  }
81 
82  // TODO check value of len
83  len = snprintf(buf, sizeof(buf), "%u", gpio);
84  if(write(fd, buf, static_cast<size_t>(len)) != len) {
85  (void) close(fd);
86  DEBUG_PRINT("gpio/unexport error!\n");
87  return -1;
88  }
89  (void) close(fd);
90 
91  /* NOTE(mereweth) - this is to allow systemd udev to make
92  * necessary filesystem changes after unexporting
93  */
94  usleep(100 * 1000);
95 
96  return 0;
97  }
98 
99  /****************************************************************
100  * gpio_set_dir
101  ****************************************************************/
102  int gpio_set_dir(unsigned int gpio, unsigned int out_flag)
103  {
104  int fd, len;
105  char buf[MAX_BUF];
106 
107  len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%u/direction", gpio);
108  FW_ASSERT(len > 0, len);
109 
110  fd = open(buf, O_WRONLY);
111  if (fd < 0) {
112  DEBUG_PRINT("gpio/direction error!\n");
113  return -1;
114  }
115 
116  const char *dir = out_flag ? "out" : "in";
117  len = static_cast<int>(strlen(dir));
118 
119  if (write(fd, dir, static_cast<size_t>(len)) != len) {
120  (void) close(fd);
121  DEBUG_PRINT("gpio/direction error!\n");
122  return -1;
123  }
124 
125  (void) close(fd);
126  return 0;
127  }
128 
129  /****************************************************************
130  * gpio_set_value
131  ****************************************************************/
132  int gpio_set_value(int fd, unsigned int value)
133  {
134 
135  FW_ASSERT(fd != -1);
136 
137  // TODO make value an enum or check its value
138 
139  const char *val = value ? "1" : "0";
140  const int len = 1;
141 
142  if(write(fd, val, len) != len) {
143  DEBUG_PRINT("gpio/set value error!\n");
144  return -1;
145  }
146 
147  DEBUG_PRINT("GPIO fd %d value %d written\n",fd,value);
148  return 0;
149  }
150 
151  /****************************************************************
152  * gpio_get_value
153  ****************************************************************/
154  int gpio_get_value(int fd, unsigned int *value)
155  {
156  char ch = '0';
157 
158  FW_ASSERT(fd != -1);
159 
160  NATIVE_INT_TYPE stat1 = static_cast<NATIVE_INT_TYPE>(lseek(fd, 0, SEEK_SET)); // Must seek back to the starting
161  NATIVE_INT_TYPE stat2 = static_cast<NATIVE_INT_TYPE>(read(fd, &ch, 1));
162 
163  if (stat1 == -1 || stat2 != 1) {
164  DEBUG_PRINT("GPIO read failure: %d %d!\n",stat1,stat2);
165  return -1;
166  }
167 
168  // TODO could use atoi instead to get the value
169  if (ch != '0') {
170  *value = 1;
171  } else {
172  *value = 0;
173  }
174 
175  DEBUG_PRINT("GPIO fd %d value %c read\n",fd,ch);
176 
177  return 0;
178  }
179 
180 
181  /****************************************************************
182  * gpio_set_edge
183  ****************************************************************/
184 
185  int gpio_set_edge(unsigned int gpio, const char *edge)
186  {
187  int fd, len;
188  char buf[MAX_BUF];
189 
190  FW_ASSERT(edge != nullptr);
191  // TODO check that edge has correct values of "none", "rising", or "falling"
192 
193  len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%u/edge", gpio);
194  FW_ASSERT(len > 0, len);
195 
196  fd = open(buf, O_WRONLY);
197  if (fd < 0) {
198  DEBUG_PRINT("gpio/set-edge error!\n");
199  return -1;
200  }
201 
202  len = static_cast<int>(strlen(edge) + 1);
203  if(write(fd, edge, static_cast<size_t>(len)) != len) {
204  (void) close(fd);
205  DEBUG_PRINT("gpio/set-edge error!\n");
206  return -1;
207  }
208 
209  (void) close(fd);
210  return 0;
211  }
212 
213  /****************************************************************
214  * gpio_fd_open
215  ****************************************************************/
216 
217  int gpio_fd_open(unsigned int gpio)
218  {
219  int fd, len;
220  char buf[MAX_BUF];
221 
222  len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%u/value", gpio);
223  FW_ASSERT(len > 0, len);
224 
225  fd = open(buf, O_RDWR | O_NONBLOCK );
226  if (fd < 0) {
227  DEBUG_PRINT("gpio/fd_open error!\n");
228  return -1;
229  }
230  return fd;
231  }
232 
233  /****************************************************************
234  * gpio_fd_close
235  ****************************************************************/
236 
237  int gpio_fd_close(int fd, unsigned int gpio)
238  {
239  // TODO is this needed? w/o this the edge file and others can retain the state from
240  // previous settings.
241  (void) gpio_unexport(gpio); // TODO check return value
242 
243  return close(fd);
244  }
245 
246 
247  // ----------------------------------------------------------------------
248  // Handler implementations for user-defined typed input ports
249  // ----------------------------------------------------------------------
250 
251  void LinuxGpioDriverComponentImpl ::
252  gpioRead_handler(
253  const NATIVE_INT_TYPE portNum,
254  Fw::Logic &state
255  )
256  {
257  FW_ASSERT(this->m_fd != -1);
258 
259  NATIVE_UINT_TYPE val;
260  NATIVE_INT_TYPE stat = gpio_get_value(this->m_fd, &val);
261  if (-1 == stat) {
262  this->log_WARNING_HI_GP_ReadError(this->m_gpio,stat);
263  return;
264  } else {
265  state = val ? Fw::Logic::HIGH : Fw::Logic::LOW;
266  }
267 
268  }
269 
270  void LinuxGpioDriverComponentImpl ::
271  gpioWrite_handler(
272  const NATIVE_INT_TYPE portNum,
273  const Fw::Logic& state
274  )
275  {
276  FW_ASSERT(this->m_fd != -1);
277 
278  NATIVE_INT_TYPE stat;
279 
280  stat = gpio_set_value(this->m_fd,(state == Fw::Logic::HIGH) ? 1 : 0);
281 
282  if (0 != stat) {
283  this->log_WARNING_HI_GP_WriteError(this->m_gpio,stat);
284  return;
285  }
286  }
287 
289  open(NATIVE_INT_TYPE gpio, GpioDirection direction) {
290 
291  // TODO check for invalid gpio?
292  NATIVE_INT_TYPE stat;
293 
294  // Configure:
295  stat = gpio_export(static_cast<unsigned int>(gpio));
296  if (-1 == stat) {
297  Fw::LogStringArg arg = strerror(errno);
298  this->log_WARNING_HI_GP_OpenError(gpio,stat,arg);
299  return false;
300  }
301  stat = gpio_set_dir(static_cast<unsigned int>(gpio), direction == GPIO_OUT ? 1 : 0);
302  if (-1 == stat) {
303  Fw::LogStringArg arg = strerror(errno);
304  this->log_WARNING_HI_GP_OpenError(gpio,stat,arg);
305  return false;
306  }
307 
308  // If needed, set edge to rising in intTaskEntry()
309 
310  // Open:
311  this->m_fd = gpio_fd_open(static_cast<unsigned int>(gpio));
312  if (-1 == this->m_fd) {
313  Fw::LogStringArg arg = strerror(errno);
314  this->log_WARNING_HI_GP_OpenError(gpio,errno,arg);
315  } else {
316  this->m_gpio = gpio;
317  }
318 
319 
320  return true;
321  }
322 
324  void LinuxGpioDriverComponentImpl ::
325  intTaskEntry(void * ptr) {
326 
327  FW_ASSERT(ptr);
328  LinuxGpioDriverComponentImpl* compPtr = static_cast<LinuxGpioDriverComponentImpl*>(ptr);
329  FW_ASSERT(compPtr->m_fd != -1);
330 
331  // start GPIO interrupt
332  NATIVE_INT_TYPE stat;
333  stat = gpio_set_edge(static_cast<unsigned int>(compPtr->m_gpio), "rising");
334  if (-1 == stat) {
335  compPtr->log_WARNING_HI_GP_IntStartError(compPtr->m_gpio);
336  return;
337  }
338 
339  // spin waiting for interrupt
340  while(not compPtr->m_quitThread) {
341  pollfd fdset[1];
342  NATIVE_INT_TYPE nfds = 1;
343  NATIVE_INT_TYPE timeout = 10000; // Timeout of 10 seconds
344 
345  memset(fdset, 0, sizeof(fdset));
346 
347  fdset[0].fd = compPtr->m_fd;
348  fdset[0].events = POLLPRI;
349  stat = poll(fdset, static_cast<nfds_t>(nfds), timeout);
350 
351  /*
352  * According to this link, poll will always have POLLERR set for the sys/class/gpio subsystem
353  * so can't check for it to look for error:
354  * http://stackoverflow.com/questions/27411013/poll-returns-both-pollpri-pollerr
355  */
356  if (stat < 0) {
357  DEBUG_PRINT("stat: %d, revents: 0x%x, POLLERR: 0x%x, POLLIN: 0x%x, POLLPRI: 0x%x\n",
358  stat, fdset[0].revents, POLLERR, POLLIN, POLLPRI); // TODO remove
359  compPtr->log_WARNING_HI_GP_IntWaitError(compPtr->m_gpio);
360  return;
361  }
362 
363  if (stat == 0) {
364  // continue to poll
365  DEBUG_PRINT("Krait timed out waiting for GPIO interrupt\n");
366  continue;
367  }
368 
369  // Asserting that number of fds w/ revents is 1:
370  FW_ASSERT(stat == 1, stat); // TODO should i bother w/ this assert?
371 
372  // TODO what to do if POLLPRI not set?
373 
374  // TODO: if I take out the read then the poll just continually interrupts
375  // Read is only taking 22 usecs each time, so it is not blocking for long
376  if (fdset[0].revents & POLLPRI) {
377 
378  char buf[MAX_BUF];
379  (void) lseek(fdset[0].fd, 0, SEEK_SET); // Must seek back to the starting
380  if(read(fdset[0].fd, buf, MAX_BUF) > 0) {
381  DEBUG_PRINT("\npoll() GPIO interrupt occurred w/ value: %c\n", buf[0]);
382  }
383  }
384 
385  // call interrupt ports
386  Svc::TimerVal timerVal;
387  timerVal.take();
388 
389  for (NATIVE_INT_TYPE port = 0; port < compPtr->getNum_intOut_OutputPorts(); port++) {
390  if (compPtr->isConnected_intOut_OutputPort(port)) {
391  compPtr->intOut_out(port,timerVal);
392  }
393  }
394 
395  }
396 
397  }
398 
401  Os::TaskString name;
402  name.format("GPINT_%s",this->getObjName()); // The task name can only be 16 chars including null
403  Os::Task::Arguments arguments(name, LinuxGpioDriverComponentImpl::intTaskEntry, this, priority, stackSize, cpuAffinity);
404  Os::Task::Status stat = this->m_intTask.start(arguments);
405 
406  if (stat != Os::Task::OP_OK) {
407  DEBUG_PRINT("Task start error: %d\n",stat);
408  }
409 
410  return stat;
411 
412  }
413 
415  exitThread() {
416  this->m_quitThread = true;
417  }
418 
419 
420 
423  {
424  if (this->m_fd != -1) {
425  DEBUG_PRINT("Closing GPIO %d fd %d\n",this->m_gpio, this->m_fd);
426  (void) gpio_fd_close(this->m_fd, static_cast<unsigned int>(this->m_gpio));
427  }
428 
429  }
430 
431 
432 } // end namespace Drv
#define FW_ASSERT(...)
Definition: Assert.hpp:14
PlatformIntType NATIVE_INT_TYPE
Definition: BasicTypes.h:51
PlatformUIntType NATIVE_UINT_TYPE
Definition: BasicTypes.h:52
C++-compatible configuration header for fprime configuration.
#define SYSFS_GPIO_DIR
#define DEBUG_PRINT(...)
void intOut_out(FwIndexType portNum, Svc::TimerVal &cycleStart)
Invoke output port intOut.
bool isConnected_intOut_OutputPort(FwIndexType portNum)
void log_WARNING_HI_GP_ReadError(I32 gpio, I32 error)
void log_WARNING_HI_GP_OpenError(I32 gpio, I32 error, const Fw::StringBase &msg)
void log_WARNING_HI_GP_WriteError(I32 gpio, I32 error)
bool open(NATIVE_INT_TYPE gpio, GpioDirection direction)
open GPIO
Os::Task::Status startIntTask(Os::Task::ParamType priority=Os::Task::TASK_DEFAULT, Os::Task::ParamType stackSize=Os::Task::TASK_DEFAULT, Os::Task::ParamType cpuAffinity=Os::Task::TASK_DEFAULT)
Start interrupt task.
Logic states.
Definition: LogicEnumAc.hpp:19
@ LOW
Logic low state.
Definition: LogicEnumAc.hpp:33
@ HIGH
Logic high state.
Definition: LogicEnumAc.hpp:35
void format(const CHAR *formatString,...)
write formatted string to buffer
Definition: StringBase.cpp:56
FwSizeType ParamType
backwards-compatible parameter type
Definition: Task.hpp:218
Status start(const Arguments &arguments) override
start the task
Definition: Task.cpp:82
@ OP_OK
message sent/received okay
Definition: Task.hpp:30
Serializable class for carrying timer values.
Definition: TimerVal.hpp:22
void take()
Function to store a timer value.
Definition: TimerVal.cpp:38
int gpio_fd_open(unsigned int gpio)
int gpio_set_edge(unsigned int gpio, const char *edge)
int gpio_fd_close(int fd, unsigned int gpio)
int gpio_unexport(unsigned int gpio)
int gpio_set_dir(unsigned int gpio, unsigned int out_flag)
int gpio_get_value(int fd, unsigned int *value)
int gpio_export(unsigned int gpio)
int gpio_set_value(int fd, unsigned int value)