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 
107  U32 flags = 0;
108  switch (configuration) {
110  flags = GPIOEVENT_REQUEST_RISING_EDGE;
111  break;
113  flags = GPIOEVENT_REQUEST_FALLING_EDGE;
114  break;
116  flags = GPIOEVENT_REQUEST_RISING_EDGE | GPIOEVENT_REQUEST_FALLING_EDGE;
117  break;
118  default:
119  FW_ASSERT(false, static_cast<FwAssertArgType>(configuration));
120  break;
121  }
122  return flags;
123 }
124 
126  if (this->m_fd >= 0) {
127  (void)::close(this->m_fd);
128  }
129 }
130 
131 // ----------------------------------------------------------------------
132 // Handler implementations for user-defined typed input ports
133 // ----------------------------------------------------------------------
134 
135 Os::File::Status LinuxGpioDriver ::setupLineHandle(const int chip_descriptor,
136  const U32 gpio,
137  const GpioConfiguration& configuration,
138  const Fw::Logic& default_state,
139  int& fd) {
141  // Set up the GPIO request
142  struct gpiohandle_request request;
143  (void)::memset(&request, 0, sizeof request);
144  request.lineoffsets[0] = gpio;
145  (void)Fw::StringUtils::string_copy(request.consumer_label, FW_OPTIONAL_NAME(this->getObjName()),
146  static_cast<FwSizeType>(sizeof request.consumer_label));
147  request.default_values[0] = (default_state == Fw::Logic::HIGH) ? 1 : 0;
148  request.fd = -1;
149  request.lines = 1;
150  request.flags = configuration_to_handler_flags(configuration);
151 
152  errno = 0;
153  int return_value = ioctl(chip_descriptor, GPIO_GET_LINEHANDLE_IOCTL, &request);
154  fd = request.fd;
155  if (return_value != 0) {
156  status = errno_to_file_status(errno);
157  fd = -1;
158  }
159  return status;
160 }
161 
162 Os::File::Status LinuxGpioDriver ::setupLineEvent(const int chip_descriptor,
163  const U32 gpio,
164  const GpioConfiguration& configuration,
165  int& fd) {
167  // Set up the GPIO request
168  struct gpioevent_request event;
169  (void)::memset(&event, 0, sizeof event);
170  event.lineoffset = gpio;
171  (void)Fw::StringUtils::string_copy(event.consumer_label, FW_OPTIONAL_NAME(this->getObjName()),
172  static_cast<FwSizeType>(sizeof event.consumer_label));
173  event.fd = -1;
174  event.handleflags = configuration_to_handler_flags(configuration);
175  event.eventflags = configuration_to_event_flags(configuration);
176  errno = 0;
177  int return_value = ioctl(chip_descriptor, GPIO_GET_LINEEVENT_IOCTL, &event);
178  fd = event.fd;
179  if (return_value != 0) {
180  status = errno_to_file_status(errno);
181  fd = -1;
182  }
183  return status;
184 }
185 
187  const U32 gpio,
188  const GpioConfiguration& configuration,
189  const Fw::Logic& default_state) {
191  Os::File chip_file;
192  FW_ASSERT(device != nullptr);
193  FW_ASSERT(configuration < MAX_GPIO_CONFIGURATION and configuration >= 0,
194  static_cast<FwAssertArgType>(configuration));
195 
196  // Open chip file and check for success
197  status = chip_file.open(device, Os::File::Mode::OPEN_WRITE);
198  if (status != Os::File::OP_OK) {
199  this->log_WARNING_HI_OpenChipError(Fw::String(device), Os::FileStatus(static_cast<Os::FileStatus::T>(status)));
200  return status;
201  }
202  // Read chip information and check for correctness
203  int chip_descriptor = reinterpret_cast<Os::Posix::File::PosixFileHandle*>(chip_file.getHandle())->m_file_descriptor;
204  struct gpiochip_info chip_info;
205  (void)::memset(&chip_info, 0, sizeof chip_info);
206  int return_value = ioctl(chip_descriptor, GPIO_GET_CHIPINFO_IOCTL, &chip_info);
207  if (return_value != 0) {
208  status = errno_to_file_status(errno);
209  this->log_WARNING_HI_OpenChipError(Fw::String(device), Os::FileStatus(static_cast<Os::FileStatus::T>(status)));
210  return status;
211  }
212  // Check if the GPIO line exists
213  if (gpio >= chip_info.lines) {
214  status = Os::File::Status::DOESNT_EXIST;
215  this->log_WARNING_HI_OpenPinError(Fw::String(device), gpio, Fw::String("Does Not Exist"),
216  Os::FileStatus(static_cast<Os::FileStatus::T>(status)));
217  return status;
218  }
219  Fw::String pin_message("Unknown");
220  struct gpioline_info pin_info;
221  (void)::memset(&pin_info, 0, sizeof pin_info);
222  pin_info.line_offset = gpio;
223  return_value = ioctl(chip_descriptor, GPIO_GET_LINEINFO_IOCTL, &pin_info);
224  if (return_value == 0) {
225  const bool has_consumer = pin_info.consumer[0] != '\0';
226  (void)pin_message.format("%s%s%s", pin_info.name, has_consumer ? " with current consumer " : "",
227  has_consumer ? pin_info.consumer : "");
228  }
229 
230  // Set up pin and set file descriptor for it
231  int pin_fd = -1;
232  switch (configuration) {
233  // Cascade intended
234  case GPIO_OUTPUT:
235  case GPIO_INPUT:
236  status = this->setupLineHandle(chip_descriptor, gpio, configuration, default_state, pin_fd);
237  break;
238  // Cascade intended
242  status = this->setupLineEvent(chip_descriptor, gpio, configuration, pin_fd);
243  break;
244  default:
245  FW_ASSERT(false);
246  break;
247  }
248  // Final status check
249  if (status != Os::File::Status::OP_OK) {
250  this->log_WARNING_HI_OpenPinError(Fw::String(device), gpio, pin_message,
251  Os::FileStatus(static_cast<Os::FileStatus::T>(status)));
252  } else {
253  this->log_DIAGNOSTIC_OpenChip(Fw::String(chip_info.name), Fw::String(chip_info.label), gpio, pin_message);
254  this->m_fd = pin_fd;
255  this->m_configuration = configuration;
256  }
257  return status;
258 }
259 
260 Drv::GpioStatus LinuxGpioDriver ::gpioRead_handler(const FwIndexType portNum, Fw::Logic& state) {
262  if (this->m_configuration == GpioConfiguration::GPIO_INPUT) {
263  struct gpiohandle_data values;
264  (void)::memset(&values, 0, sizeof values);
265  int return_value = ioctl(this->m_fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &values);
266  if (return_value != 0) {
267  status = errno_to_gpio_status(errno);
268  } else {
269  state = values.values[0] ? Fw::Logic::HIGH : Fw::Logic::LOW;
270  status = Drv::GpioStatus::OP_OK;
271  }
272  }
273  return status;
274 }
275 
276 Drv::GpioStatus LinuxGpioDriver ::gpioWrite_handler(const FwIndexType portNum, const Fw::Logic& state) {
278  if (this->m_configuration == GpioConfiguration::GPIO_OUTPUT) {
279  struct gpiohandle_data values;
280  (void)::memset(&values, 0, sizeof values);
281  values.values[0] = (state == Fw::Logic::HIGH) ? 1 : 0;
282  int return_value = ioctl(this->m_fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &values);
283  if (return_value != 0) {
284  status = errno_to_gpio_status(errno);
285  } else {
286  status = Drv::GpioStatus::OP_OK;
287  }
288  }
289  return status;
290 }
291 
292 void LinuxGpioDriver ::pollLoop() {
293  // Ensure size of FwSizeType is large enough to fit the necessary ranges
294  // NOTE: casts to unsigned types for int and ssize_t are made to avoid sign-compare warning;
295  // in both cases the cast is safe because max() returns nonnegative value.
296  static_assert(GPIO_POLL_TIMEOUT < static_cast<unsigned int>(std::numeric_limits<int>::max()),
297  "Poll timeout would overflow");
298  static_assert(sizeof(struct gpioevent_data) < std::numeric_limits<FwSizeType>::max(), "FwSizeType too small");
299  using unsigned_ssize_t = std::make_unsigned<ssize_t>::type;
300  static_assert(
301  static_cast<unsigned_ssize_t>(std::numeric_limits<ssize_t>::max()) <= std::numeric_limits<FwSizeType>::max(),
302  "FwSizeType too small");
303  // Setup poll information
304  pollfd file_descriptors[1];
305  // Loop forever
306  // @non-terminating@: polling thread runs until stopped
307  while (this->getRunning()) {
308  // Setup polling
309  (void)::memset(file_descriptors, 0, sizeof file_descriptors);
310  file_descriptors[0].fd = this->m_fd;
311  file_descriptors[0].events = POLLIN; // Ask for read data available
312  // Poll for fd bing ready
313  int status = ::poll(file_descriptors, 1, static_cast<int>(GPIO_POLL_TIMEOUT));
314  // Check for some file descriptor to be ready
315  if (status > 0) {
316  struct gpioevent_data event_data;
317  FwSizeType read_bytes = static_cast<FwSizeType>(::read(this->m_fd, &event_data, sizeof event_data));
318  if (read_bytes == sizeof event_data) {
319  Os::RawTime timestamp;
320  Os::RawTime::Status timeStatus = timestamp.now();
321  if (timeStatus != Os::RawTime::Status::OP_OK) {
323  Os::RawTimeStatus(static_cast<Os::RawTimeStatus::T>(timeStatus)));
324  }
325  this->gpioInterrupt_out(0, timestamp);
326  }
327  // A read error occurred
328  else {
329  this->log_WARNING_HI_InterruptReadError(static_cast<U32>(sizeof event_data),
330  static_cast<U32>(read_bytes));
331  }
332  }
333  // An error of some kind occurred
334  else if (status < 0) {
335  this->log_WARNING_HI_PollingError(static_cast<I32>(errno));
336  }
337  }
338 }
339 
340 } // 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.
void gpioInterrupt_out(FwIndexType portNum, Os::RawTime &cycleStart) const
Invoke output port gpioInterrupt.
Status now() override
Get the current time.
Definition: RawTime.cpp:38
#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:43
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:225
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.
Os::File::Status errno_to_file_status(int errno_input)
static constexpr FwSizeType GPIO_POLL_TIMEOUT
#define FW_ASSERT(...)
Definition: Assert.hpp:14