F´ Flight Software - C/C++ Documentation
A framework for building embedded system applications to NASA flight quality standards.
LinuxGpioDriver.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 // ======================================================================
13 #include <Fw/FPrimeBasicTypes.hpp>
14 #include <Fw/Types/String.hpp>
15 #include <Fw/Types/StringUtils.hpp>
16 #include <Os/Posix/File.hpp>
17 
18 #include <linux/gpio.h>
19 #include <poll.h>
20 #include <sys/ioctl.h>
21 #include <unistd.h>
22 #include <cerrno>
23 #include <cstring>
24 #include <type_traits>
25 
26 namespace Drv {
27 
30  switch (errno_input) {
31  case 0:
32  status = Os::File::Status::OP_OK;
33  break;
34  case EBADF:
35  status = Os::File::Status::NOT_OPENED;
36  break;
37  case EINVAL:
38  status = Os::File::Status::INVALID_ARGUMENT;
39  break;
40  case ENODEV:
41  status = Os::File::Status::DOESNT_EXIST;
42  break;
43  case ENOMEM:
45  break;
46  case EPERM:
47  status = Os::File::Status::NO_PERMISSION;
48  break;
49  case ENXIO:
50  status = Os::File::Status::INVALID_MODE;
51  break;
52  // Cascades intended
53  case EFAULT:
54  case EWOULDBLOCK:
55  case EBUSY:
56  case EIO:
57  default:
59  break;
60  }
61  return status;
62 }
63 
65  Drv::GpioStatus status = Drv::GpioStatus::T::UNKNOWN_ERROR;
66  switch (errno_input) {
67  case EBADF:
68  status = Drv::GpioStatus::T::NOT_OPENED;
69  break;
70  case ENXIO:
72  break;
73  // Cascades intended
74  case EFAULT:
75  case EINVAL:
76  case EWOULDBLOCK:
77  case EBUSY:
78  case EIO:
79  default:
80  status = Drv::GpioStatus::T::UNKNOWN_ERROR;
81  break;
82  }
83  return status;
84 }
85 
87  U32 flags = 0;
88  switch (configuration) {
90  flags = GPIOHANDLE_REQUEST_OUTPUT;
91  break;
92  // Cascade intended
97  flags = GPIOHANDLE_REQUEST_INPUT;
98  break;
99  default:
100  FW_ASSERT(false, static_cast<FwAssertArgType>(configuration));
101  break;
102  }
103  return flags;
104 }
105 
106 #ifdef GPIO_V2_GET_LINE_IOCTL
107 U64 configuration_to_line_flags_v2(Drv::LinuxGpioDriver::GpioConfiguration configuration) {
108  U64 flags = 0;
109  switch (configuration) {
111  flags = GPIO_V2_LINE_FLAG_OUTPUT;
112  break;
114  flags = GPIO_V2_LINE_FLAG_INPUT;
115  break;
117  flags = GPIO_V2_LINE_FLAG_INPUT | GPIO_V2_LINE_FLAG_EDGE_RISING;
118  break;
120  flags = GPIO_V2_LINE_FLAG_INPUT | GPIO_V2_LINE_FLAG_EDGE_FALLING;
121  break;
123  flags = GPIO_V2_LINE_FLAG_INPUT | GPIO_V2_LINE_FLAG_EDGE_RISING | GPIO_V2_LINE_FLAG_EDGE_FALLING;
124  break;
125  default:
126  FW_ASSERT(false, static_cast<FwAssertArgType>(configuration));
127  break;
128  }
129  return flags;
130 }
131 #endif
132 
134  U32 flags = 0;
135  switch (configuration) {
137  flags = GPIOEVENT_REQUEST_RISING_EDGE;
138  break;
140  flags = GPIOEVENT_REQUEST_FALLING_EDGE;
141  break;
143  flags = GPIOEVENT_REQUEST_RISING_EDGE | GPIOEVENT_REQUEST_FALLING_EDGE;
144  break;
145  default:
146  FW_ASSERT(false, static_cast<FwAssertArgType>(configuration));
147  break;
148  }
149  return flags;
150 }
151 
153  if (this->m_fd >= 0) {
154  (void)::close(this->m_fd);
155  }
156 }
157 
158 // ----------------------------------------------------------------------
159 // Handler implementations for user-defined typed input ports
160 // ----------------------------------------------------------------------
161 
162 Os::File::Status LinuxGpioDriver ::setupLineRequestV2(const int chip_descriptor,
163  const U32 gpio,
164  const GpioConfiguration& configuration,
165  const Fw::Logic& default_state,
166  int& fd) {
167  fd = -1;
168 #ifdef GPIO_V2_GET_LINE_IOCTL
170  // Set up the GPIO v2 line request
171  struct gpio_v2_line_request request;
172  (void)::memset(&request, 0, sizeof request);
173  request.offsets[0] = gpio;
174  (void)Fw::StringUtils::string_copy(request.consumer, FW_OPTIONAL_NAME(this->getObjName()),
175  static_cast<FwSizeType>(sizeof request.consumer));
176  request.num_lines = 1;
177  request.fd = -1;
178  request.config.flags = configuration_to_line_flags_v2(configuration);
179  // For outputs, set the default state via an output-values config attribute
180  if (configuration == GPIO_OUTPUT) {
181  request.config.num_attrs = 1;
182  request.config.attrs[0].attr.id = GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES;
183  request.config.attrs[0].attr.values = (default_state == Fw::Logic::HIGH) ? 1 : 0;
184  request.config.attrs[0].mask = 1;
185  }
186 
187  errno = 0;
188  int return_value = ioctl(chip_descriptor, GPIO_V2_GET_LINE_IOCTL, &request);
189  fd = request.fd;
190  if (return_value != 0) {
191  status = errno_to_file_status(errno);
192  fd = -1;
193  }
194  return status;
195 #else
196  // Headers do not provide the v2 uAPI: report unsupported (v2 is never selected in this build)
197  return Os::File::Status::NOT_SUPPORTED;
198 #endif
199 }
200 
201 Os::File::Status LinuxGpioDriver ::setupLineHandle(const int chip_descriptor,
202  const U32 gpio,
203  const GpioConfiguration& configuration,
204  const Fw::Logic& default_state,
205  int& fd) {
207  // Set up the GPIO request
208  struct gpiohandle_request request;
209  (void)::memset(&request, 0, sizeof request);
210  request.lineoffsets[0] = gpio;
211  (void)Fw::StringUtils::string_copy(request.consumer_label, FW_OPTIONAL_NAME(this->getObjName()),
212  static_cast<FwSizeType>(sizeof request.consumer_label));
213  request.default_values[0] = (default_state == Fw::Logic::HIGH) ? 1 : 0;
214  request.fd = -1;
215  request.lines = 1;
216  request.flags = configuration_to_handler_flags(configuration);
217 
218  errno = 0;
219  int return_value = ioctl(chip_descriptor, GPIO_GET_LINEHANDLE_IOCTL, &request);
220  fd = request.fd;
221  if (return_value != 0) {
222  status = errno_to_file_status(errno);
223  fd = -1;
224  }
225  return status;
226 }
227 
228 Os::File::Status LinuxGpioDriver ::setupLineEvent(const int chip_descriptor,
229  const U32 gpio,
230  const GpioConfiguration& configuration,
231  int& fd) {
233  // Set up the GPIO request
234  struct gpioevent_request event;
235  (void)::memset(&event, 0, sizeof event);
236  event.lineoffset = gpio;
237  (void)Fw::StringUtils::string_copy(event.consumer_label, FW_OPTIONAL_NAME(this->getObjName()),
238  static_cast<FwSizeType>(sizeof event.consumer_label));
239  event.fd = -1;
240  event.handleflags = configuration_to_handler_flags(configuration);
241  event.eventflags = configuration_to_event_flags(configuration);
242  errno = 0;
243  int return_value = ioctl(chip_descriptor, GPIO_GET_LINEEVENT_IOCTL, &event);
244  fd = event.fd;
245  if (return_value != 0) {
246  status = errno_to_file_status(errno);
247  fd = -1;
248  }
249  return status;
250 }
251 
253  const U32 gpio,
254  const GpioConfiguration& configuration,
255  const Fw::Logic& default_state) {
257  Os::File chip_file;
258  FW_ASSERT(device != nullptr);
259  FW_ASSERT(configuration < MAX_GPIO_CONFIGURATION and configuration >= 0,
260  static_cast<FwAssertArgType>(configuration));
261 
262  // Open chip file and check for success
263  status = chip_file.open(device, Os::File::Mode::OPEN_WRITE);
264  if (status != Os::File::OP_OK) {
265  this->log_WARNING_HI_OpenChipError(Fw::String(device), Os::FileStatus(static_cast<Os::FileStatus::T>(status)));
266  return status;
267  }
268  // Read chip information and check for correctness
269  int chip_descriptor = reinterpret_cast<Os::Posix::File::PosixFileHandle*>(chip_file.getHandle())->m_file_descriptor;
270  struct gpiochip_info chip_info;
271  (void)::memset(&chip_info, 0, sizeof chip_info);
272  int return_value = ioctl(chip_descriptor, GPIO_GET_CHIPINFO_IOCTL, &chip_info);
273  if (return_value != 0) {
274  status = errno_to_file_status(errno);
275  this->log_WARNING_HI_OpenChipError(Fw::String(device), Os::FileStatus(static_cast<Os::FileStatus::T>(status)));
276  return status;
277  }
278  // Check if the GPIO line exists
279  if (gpio >= chip_info.lines) {
280  status = Os::File::Status::DOESNT_EXIST;
281  this->log_WARNING_HI_OpenPinError(Fw::String(device), gpio, Fw::String("Does Not Exist"),
282  Os::FileStatus(static_cast<Os::FileStatus::T>(status)));
283  return status;
284  }
285  Fw::String pin_message("Unknown");
286  // The v2 line-info ioctl doubles as a probe for v2 uAPI support: it only succeeds when the kernel
287  // (and this chip) support the v2 uAPI, so its result selects which uAPI version to request below.
288  bool supports_v2 = false;
289 #ifdef GPIO_V2_GET_LINEINFO_IOCTL
290  struct gpio_v2_line_info pin_info_v2;
291  (void)::memset(&pin_info_v2, 0, sizeof pin_info_v2);
292  pin_info_v2.offset = gpio;
293  return_value = ioctl(chip_descriptor, GPIO_V2_GET_LINEINFO_IOCTL, &pin_info_v2);
294  if (return_value == 0) {
295  const bool has_consumer = pin_info_v2.consumer[0] != '\0';
296  (void)pin_message.format("%s%s%s", pin_info_v2.name, has_consumer ? " with current consumer " : "",
297  has_consumer ? pin_info_v2.consumer : "");
298  supports_v2 = true;
299  }
300 #endif
301  if (not supports_v2) {
302  struct gpioline_info pin_info;
303  (void)::memset(&pin_info, 0, sizeof pin_info);
304  pin_info.line_offset = gpio;
305  return_value = ioctl(chip_descriptor, GPIO_GET_LINEINFO_IOCTL, &pin_info);
306  if (return_value == 0) {
307  const bool has_consumer = pin_info.consumer[0] != '\0';
308  (void)pin_message.format("%s%s%s", pin_info.name, has_consumer ? " with current consumer " : "",
309  has_consumer ? pin_info.consumer : "");
310  }
311  }
312 
313  // Set up pin and set file descriptor for it. The v2 uAPI is used when the probe above detected support;
314  // otherwise the deprecated v1 uAPI is used. Real v2 request errors are reported, not retried on v1.
315  int pin_fd = -1;
316  ApiVersion api_version = ApiVersion::API_V1;
317  if (supports_v2) {
318  api_version = ApiVersion::API_V2;
319  status = this->setupLineRequestV2(chip_descriptor, gpio, configuration, default_state, pin_fd);
320  } else {
321  switch (configuration) {
322  // Cascade intended
323  case GPIO_OUTPUT:
324  case GPIO_INPUT:
325  status = this->setupLineHandle(chip_descriptor, gpio, configuration, default_state, pin_fd);
326  break;
327  // Cascade intended
331  status = this->setupLineEvent(chip_descriptor, gpio, configuration, pin_fd);
332  break;
333  default:
334  FW_ASSERT(false);
335  break;
336  }
337  }
338  // Final status check
339  if (status != Os::File::Status::OP_OK) {
340  this->log_WARNING_HI_OpenPinError(Fw::String(device), gpio, pin_message,
341  Os::FileStatus(static_cast<Os::FileStatus::T>(status)));
342  } else {
343  this->log_DIAGNOSTIC_OpenChip(Fw::String(chip_info.name), Fw::String(chip_info.label), gpio, pin_message);
344  this->m_fd = pin_fd;
345  this->m_configuration = configuration;
346  this->m_apiVersion = api_version;
347  }
348  return status;
349 }
350 
351 Drv::GpioStatus LinuxGpioDriver ::gpioRead_handler(const FwIndexType portNum, Fw::Logic& state) {
353  if (this->m_configuration == GpioConfiguration::GPIO_INPUT) {
354 #ifdef GPIO_V2_GET_LINE_IOCTL
355  if (this->m_apiVersion == ApiVersion::API_V2) {
356  struct gpio_v2_line_values values;
357  (void)::memset(&values, 0, sizeof values);
358  values.mask = 1;
359  int return_value = ioctl(this->m_fd, GPIO_V2_LINE_GET_VALUES_IOCTL, &values);
360  if (return_value != 0) {
361  status = errno_to_gpio_status(errno);
362  } else {
363  state = (values.bits & 1) ? Fw::Logic::HIGH : Fw::Logic::LOW;
364  status = Drv::GpioStatus::OP_OK;
365  }
366  return status;
367  }
368 #endif
369  struct gpiohandle_data values;
370  (void)::memset(&values, 0, sizeof values);
371  int return_value = ioctl(this->m_fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &values);
372  if (return_value != 0) {
373  status = errno_to_gpio_status(errno);
374  } else {
375  state = values.values[0] ? Fw::Logic::HIGH : Fw::Logic::LOW;
376  status = Drv::GpioStatus::OP_OK;
377  }
378  }
379  return status;
380 }
381 
382 Drv::GpioStatus LinuxGpioDriver ::gpioWrite_handler(const FwIndexType portNum, const Fw::Logic& state) {
384  if (this->m_configuration == GpioConfiguration::GPIO_OUTPUT) {
385 #ifdef GPIO_V2_GET_LINE_IOCTL
386  if (this->m_apiVersion == ApiVersion::API_V2) {
387  struct gpio_v2_line_values values;
388  (void)::memset(&values, 0, sizeof values);
389  values.mask = 1;
390  values.bits = (state == Fw::Logic::HIGH) ? 1 : 0;
391  int return_value = ioctl(this->m_fd, GPIO_V2_LINE_SET_VALUES_IOCTL, &values);
392  if (return_value != 0) {
393  status = errno_to_gpio_status(errno);
394  } else {
395  status = Drv::GpioStatus::OP_OK;
396  }
397  return status;
398  }
399 #endif
400  struct gpiohandle_data values;
401  (void)::memset(&values, 0, sizeof values);
402  values.values[0] = (state == Fw::Logic::HIGH) ? 1 : 0;
403  int return_value = ioctl(this->m_fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &values);
404  if (return_value != 0) {
405  status = errno_to_gpio_status(errno);
406  } else {
407  status = Drv::GpioStatus::OP_OK;
408  }
409  }
410  return status;
411 }
412 
413 void LinuxGpioDriver ::pollLoop() {
414  // Ensure size of FwSizeType is large enough to fit the necessary ranges
415  // NOTE: casts to unsigned types for int and ssize_t are made to avoid sign-compare warning;
416  // in both cases the cast is safe because max() returns nonnegative value.
417  static_assert(GPIO_POLL_TIMEOUT < static_cast<unsigned int>(std::numeric_limits<int>::max()),
418  "Poll timeout would overflow");
419  static_assert(sizeof(struct gpioevent_data) < std::numeric_limits<FwSizeType>::max(), "FwSizeType too small");
420 #ifdef GPIO_V2_GET_LINE_IOCTL
421  static_assert(sizeof(struct gpio_v2_line_event) < std::numeric_limits<FwSizeType>::max(), "FwSizeType too small");
422 #endif
423  using unsigned_ssize_t = std::make_unsigned<ssize_t>::type;
424  static_assert(
425  static_cast<unsigned_ssize_t>(std::numeric_limits<ssize_t>::max()) <= std::numeric_limits<FwSizeType>::max(),
426  "FwSizeType too small");
427  // Setup poll information
428  pollfd file_descriptors[1];
429  // Loop forever
430  // @non-terminating@: polling thread runs until stopped
431  while (this->getRunning()) {
432  // Setup polling
433  (void)::memset(file_descriptors, 0, sizeof file_descriptors);
434  file_descriptors[0].fd = this->m_fd;
435  file_descriptors[0].events = POLLIN; // Ask for read data available
436  // Poll for fd bing ready
437  int status = ::poll(file_descriptors, 1, static_cast<int>(GPIO_POLL_TIMEOUT));
438  // Check for some file descriptor to be ready
439  if (status > 0) {
440  union {
441  struct gpioevent_data v1;
442 #ifdef GPIO_V2_GET_LINE_IOCTL
443  struct gpio_v2_line_event v2;
444 #endif
445  } event_data;
446  FwSizeType expected_bytes = sizeof event_data.v1;
447 #ifdef GPIO_V2_GET_LINE_IOCTL
448  if (this->m_apiVersion == ApiVersion::API_V2) {
449  expected_bytes = sizeof event_data.v2;
450  }
451 #endif
452  FwSizeType read_bytes = static_cast<FwSizeType>(::read(this->m_fd, &event_data, expected_bytes));
453  if (read_bytes == expected_bytes) {
454  Os::RawTime timestamp;
455  Os::RawTime::Status timeStatus = timestamp.now();
456  if (timeStatus != Os::RawTime::Status::OP_OK) {
458  Os::RawTimeStatus(static_cast<Os::RawTimeStatus::T>(timeStatus)));
459  }
460  this->gpioInterrupt_out(0, timestamp);
461  }
462  // A read error occurred
463  else {
464  this->log_WARNING_HI_InterruptReadError(static_cast<U32>(expected_bytes), static_cast<U32>(read_bytes));
465  }
466  }
467  // An error of some kind occurred
468  else if (status < 0) {
469  this->log_WARNING_HI_PollingError(static_cast<I32>(errno));
470  }
471  }
472 }
473 
474 } // end namespace Drv
Operation succeeded.
Definition: Os.hpp:27
void log_WARNING_HI_OpenChipError(const Fw::StringBase &chip, const Os::FileStatus &status) const
Log event OpenChipError.
A catch-all for other errors. Have to look in implementation-specific code.
FPP shadow-enum representing Os::RawTime::Status.
PlatformSizeType FwSizeType
Input GPIO pin triggers interrupt port on falling edge.
void log_WARNING_HI_InterruptReadError(U32 expected, U32 got) const
Log event InterruptReadError.
Status now() override
Get the current time.
void gpioInterrupt_out(FwIndexType portNum, Os::RawTime &cycleStart) const
Invoke output port gpioInterrupt.
#define FW_OPTIONAL_NAME(name)
Definition: FpConfig.h:47
Operation succeeded.
Os::FileInterface::Status open(const char *path, Mode mode)
open file with supplied path and mode
Definition: File.cpp:50
Operation not permitted with current configuration.
char * string_copy(char *destination, const char *source, FwSizeType num)
copy string with null-termination guaranteed
Definition: StringUtils.cpp:7
void log_WARNING_HI_OpenPinError(const Fw::StringBase &chip, U32 pin, const Fw::StringBase &pinMessage, const Os::FileStatus &status) const
Log event OpenPinError.
void log_DIAGNOSTIC_OpenChip(const Fw::StringBase &chip, const Fw::StringBase &chipLabel, U32 pin, const Fw::StringBase &pinMessage) const
Log event OpenChip.
FileHandle * getHandle() override
returns the raw file handle
Definition: File.cpp:232
GpioConfiguration
configure the GPIO pin
Input GPIO pin triggers interrupt port on both edges.
FormatStatus format(const CHAR *formatString,...)
write formatted string to buffer
Definition: StringBase.cpp:58
Logic low state.
Definition: LogicEnumAc.hpp:34
Drv::GpioStatus errno_to_gpio_status(int errno_input)
No space left on the device for writing.
Os::File::Status open(const char *device, const U32 gpio, const GpioConfiguration &configuration, const Fw::Logic &default_state=Fw::Logic::LOW)
open a GPIO pin for use in the system
Operation was successful.
Definition: File.hpp:42
PlatformIndexType FwIndexType
void log_WARNING_HI_PollingError(I32 error_number) const
Log event PollingError.
Output GPIO pin for direct writing.
Logic states.
Definition: LogicEnumAc.hpp:18
Logic high state.
Definition: LogicEnumAc.hpp:36
void log_WARNING_HI_InterruptTimeError(const Os::RawTimeStatus &status) const
Log event InterruptTimeError.
U32 configuration_to_event_flags(Drv::LinuxGpioDriver::GpioConfiguration configuration)
Input GPIO pin for direct reading.
Input GPIO pin triggers interrupt port on rising edge.
U32 configuration_to_handler_flags(Drv::LinuxGpioDriver::GpioConfiguration configuration)
FPP shadow-enum representing Os::File::Status.
ApiVersion
Linux GPIO character device uAPI version used for the opened pin.
Os::File::Status errno_to_file_status(int errno_input)
static constexpr FwSizeType GPIO_POLL_TIMEOUT
#define FW_ASSERT(...)
Definition: Assert.hpp:14