F´ Flight Software - C/C++ Documentation  devel
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 <FpConfig.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 
24 namespace Drv {
25 
26 
29  switch (errno_input) {
30  case 0:
31  status = Os::File::Status::OP_OK;
32  break;
33  case EBADF:
34  status = Os::File::Status::NOT_OPENED;
35  break;
36  case EINVAL:
37  status = Os::File::Status::INVALID_ARGUMENT;
38  break;
39  case ENODEV:
40  status = Os::File::Status::DOESNT_EXIST;
41  break;
42  case ENOMEM:
44  break;
45  case EPERM:
46  status = Os::File::Status::NO_PERMISSION;
47  break;
48  case ENXIO:
49  status = Os::File::Status::INVALID_MODE;
50  break;
51  // Cascades intended
52  case EFAULT:
53  case EWOULDBLOCK:
54  case EBUSY:
55  case EIO:
56  default:
58  break;
59  }
60  return status;
61 }
62 
64  Drv::GpioStatus status = Drv::GpioStatus::T::UNKNOWN_ERROR;
65  switch (errno_input) {
66  case EBADF:
67  status = Drv::GpioStatus::T::NOT_OPENED;
68  break;
69  case ENXIO:
71  break;
72  // Cascades intended
73  case EFAULT:
74  case EINVAL:
75  case EWOULDBLOCK:
76  case EBUSY:
77  case EIO:
78  default:
79  status = Drv::GpioStatus::T::UNKNOWN_ERROR;
80  break;
81  }
82  return status;
83 }
84 
86  U32 flags = 0;
87  switch (configuration) {
89  flags = GPIOHANDLE_REQUEST_OUTPUT;
90  break;
91  // Cascade intended
96  flags = GPIOHANDLE_REQUEST_INPUT;
97  break;
98  default:
99  FW_ASSERT(0, static_cast<FwAssertArgType>(configuration));
100  break;
101  }
102  return flags;
103 }
104 
106  U32 flags = 0;
107  switch (configuration) {
109  flags = GPIOEVENT_REQUEST_RISING_EDGE;
110  break;
112  flags = GPIOEVENT_REQUEST_FALLING_EDGE;
113  break;
115  flags = GPIOEVENT_REQUEST_RISING_EDGE | GPIOEVENT_REQUEST_FALLING_EDGE;
116  break;
117  default:
118  FW_ASSERT(0, static_cast<FwAssertArgType>(configuration));
119  break;
120  }
121  return flags;
122 }
123 
125  (void) ::close(this->m_fd);
126 }
127 
128 // ----------------------------------------------------------------------
129 // Handler implementations for user-defined typed input ports
130 // ----------------------------------------------------------------------
131 
132 Os::File::Status LinuxGpioDriver ::setupLineHandle(const PlatformIntType chip_descriptor,
133  const U32 gpio,
134  const GpioConfiguration& configuration,
135  const Fw::Logic& default_state,
136  PlatformIntType& fd) {
138  // Set up the GPIO request
139  struct gpiohandle_request request;
140  (void) ::memset(&request, 0, sizeof request);
141  request.lineoffsets[0] = gpio;
142  Fw::StringUtils::string_copy(request.consumer_label, this->getObjName(),
143  static_cast<FwSizeType>(sizeof request.consumer_label));
144  request.default_values[0] = (default_state == Fw::Logic::HIGH) ? 1 : 0;
145  request.fd = -1;
146  request.lines = 1;
147  request.flags = configuration_to_handler_flags(configuration);
148 
149  errno = 0;
150  PlatformIntType return_value = ioctl(chip_descriptor, GPIO_GET_LINEHANDLE_IOCTL, &request);
151  fd = request.fd;
152  if (return_value != 0) {
153  status = errno_to_file_status(errno);
154  fd = -1;
155  }
156  return status;
157 }
158 
159 Os::File::Status LinuxGpioDriver ::setupLineEvent(const PlatformIntType chip_descriptor,
160  const U32 gpio,
161  const GpioConfiguration& configuration,
162  PlatformIntType& fd) {
164  // Set up the GPIO request
165  struct gpioevent_request event;
166  (void) ::memset(&event, 0, sizeof event);
167  event.lineoffset = gpio;
168  Fw::StringUtils::string_copy(event.consumer_label, this->getObjName(),
169  static_cast<FwSizeType>(sizeof event.consumer_label));
170  event.fd = -1;
171  event.handleflags = configuration_to_handler_flags(configuration);
172  event.eventflags = configuration_to_event_flags(configuration);
173  errno = 0;
174  PlatformIntType return_value = ioctl(chip_descriptor, GPIO_GET_LINEEVENT_IOCTL, &event);
175  fd = event.fd;
176  if (return_value != 0) {
177  status = errno_to_file_status(errno);
178  fd = -1;
179  }
180  return status;
181 }
182 
184  const U32 gpio,
185  const GpioConfiguration& configuration,
186  const Fw::Logic& default_state) {
188  Os::File chip_file;
189  FW_ASSERT(device != nullptr);
190  FW_ASSERT(configuration < MAX_GPIO_CONFIGURATION and configuration >= 0, static_cast<FwAssertArgType>(configuration));
191 
192  // Open chip file and check for success
193  status = chip_file.open(device, Os::File::Mode::OPEN_WRITE);
194  if (status != Os::File::OP_OK) {
195  this->log_WARNING_HI_OpenChipError(Fw::String(device), Os::FileStatus(static_cast<Os::FileStatus::T>(status)));
196  return status;
197  }
198  // Read chip information and check for correctness
199  PlatformIntType chip_descriptor =
200  reinterpret_cast<Os::Posix::File::PosixFileHandle*>(chip_file.getHandle())->m_file_descriptor;
201  struct gpiochip_info chip_info;
202  (void) ::memset(&chip_info, 0, sizeof chip_info);
203  PlatformIntType return_value = ioctl(chip_descriptor, GPIO_GET_CHIPINFO_IOCTL, &chip_info);
204  if (return_value != 0) {
205  status = errno_to_file_status(errno);
206  this->log_WARNING_HI_OpenChipError(Fw::String(device), Os::FileStatus(static_cast<Os::FileStatus::T>(status)));
207  return status;
208  }
209  // Check if the GPIO line exists
210  if (gpio >= chip_info.lines) {
211  this->log_WARNING_HI_OpenPinError(Fw::String(device), gpio, Fw::String("Does Not Exist"),
212  Os::FileStatus(static_cast<Os::FileStatus::T>(status)));
213  return status;
214  }
215  Fw::String pin_message("Unknown");
216  struct gpioline_info pin_info;
217  (void) ::memset(&pin_info, 0, sizeof pin_info);
218  pin_info.line_offset = gpio;
219  return_value = ioctl(chip_descriptor, GPIO_GET_LINEINFO_IOCTL, &pin_info);
220  if (return_value == 0) {
221  const bool has_consumer = pin_info.consumer[0] != '\0';
222  pin_message.format("%s%s%s", pin_info.name, has_consumer ? " with current consumer " : "",
223  has_consumer ? pin_info.consumer : "");
224  }
225 
226  // Set up pin and set file descriptor for it
227  PlatformIntType pin_fd = -1;
228  switch (configuration) {
229  // Cascade intended
230  case GPIO_OUTPUT:
231  case GPIO_INPUT:
232  status = this->setupLineHandle(chip_descriptor, gpio, configuration, default_state, pin_fd);
233  break;
234  // Cascade intended
238  status = this->setupLineEvent(chip_descriptor, gpio, configuration, pin_fd);
239  break;
240  default:
241  FW_ASSERT(0);
242  break;
243  }
244  // Final status check
245  if (status != Os::File::Status::OP_OK) {
246  this->log_WARNING_HI_OpenPinError(Fw::String(device), gpio, pin_message,
247  Os::FileStatus(static_cast<Os::FileStatus::T>(status)));
248  } else {
249  this->log_DIAGNOSTIC_OpenChip(Fw::String(chip_info.name), Fw::String(chip_info.label),
250  gpio, pin_message);
251  this->m_fd = pin_fd;
252  this->m_configuration = configuration;
253  }
254  return status;
255 }
256 
257 Drv::GpioStatus LinuxGpioDriver ::gpioRead_handler(const NATIVE_INT_TYPE portNum, Fw::Logic& state) {
259  if (this->m_configuration == GpioConfiguration::GPIO_INPUT) {
260  struct gpiohandle_data values;
261  (void) ::memset(&values, 0, sizeof values);
262  PlatformIntType return_value = ioctl(this->m_fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &values);
263  if (return_value != 0) {
264  status = errno_to_gpio_status(errno);
265  } else {
266  state = values.values[0] ? Fw::Logic::HIGH : Fw::Logic::LOW;
267  status = Drv::GpioStatus::OP_OK;
268  }
269  }
270  return status;
271 }
272 
273 Drv::GpioStatus LinuxGpioDriver ::gpioWrite_handler(const NATIVE_INT_TYPE portNum, const Fw::Logic& state) {
275  if (this->m_configuration == GpioConfiguration::GPIO_OUTPUT) {
276  struct gpiohandle_data values;
277  (void) ::memset(&values, 0, sizeof values);
278  values.values[0] = (state == Fw::Logic::HIGH) ? 1 : 0;
279  PlatformIntType return_value = ioctl(this->m_fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &values);
280  if (return_value != 0) {
281  status = errno_to_gpio_status(errno);
282  } else {
283  status = Drv::GpioStatus::OP_OK;
284  }
285  }
286  return status;
287 }
288 
289 void LinuxGpioDriver ::pollLoop() {
290  static_assert(GPIO_POLL_TIMEOUT < std::numeric_limits<int>::max(), "Poll timeout would overflow");
291  static_assert(sizeof(struct gpioevent_data) < std::numeric_limits<FwSizeType>::max(), "FwSizeType too small");
292  static_assert(std::numeric_limits<ssize_t>::max() <= std::numeric_limits<FwSizeType>::max(), "FwSizeType too small");
293  // Setup poll information
294  pollfd file_descriptors[1];
295  // Loop forever
296  while (this->getRunning()) {
297  // Setup polling
298  (void) ::memset(file_descriptors, 0, sizeof file_descriptors);
299  file_descriptors[0].fd = this->m_fd;
300  file_descriptors[0].events = POLLIN; // Ask for read data available
301  // Poll for fd bing ready
302  PlatformIntType status = ::poll(file_descriptors, 1, static_cast<int>(GPIO_POLL_TIMEOUT));
303  // Check for some file descriptor to be ready
304  if (status > 0) {
305  struct gpioevent_data event_data;
306  FwSizeType read_bytes = static_cast<FwSizeType>(::read(this->m_fd, &event_data, sizeof event_data));
307  if (read_bytes == sizeof event_data) {
308  Os::RawTime timestamp;
309  timestamp.now();
310  this->gpioInterrupt_out(0, timestamp);
311  }
312  // A read error occurred
313  else {
314  this->log_WARNING_HI_InterruptReadError(static_cast<U32>(sizeof event_data),
315  static_cast<U32>(read_bytes));
316  }
317  }
318  // An error of some kind occurred
319  else if (status < 0) {
320  this->log_WARNING_HI_PollingError(static_cast<I32>(errno));
321  }
322  }
323 }
324 
325 } // end namespace Drv
#define FW_ASSERT(...)
Definition: Assert.hpp:14
PlatformIntType NATIVE_INT_TYPE
Definition: BasicTypes.h:55
int PlatformIntType
DefaultTypes.hpp provides fallback defaults for the platform types.
PlatformAssertArgType FwAssertArgType
Definition: FpConfig.h:39
PlatformSizeType FwSizeType
Definition: FpConfig.h:35
C++-compatible configuration header for fprime configuration.
@ INVALID_MODE
Operation not permitted with current configuration.
@ OP_OK
Operation succeeded.
void log_DIAGNOSTIC_OpenChip(const Fw::StringBase &chip, const Fw::StringBase &chipLabel, U32 pin, const Fw::StringBase &pinMessage)
Log event OpenChip.
void log_WARNING_HI_OpenPinError(const Fw::StringBase &chip, U32 pin, const Fw::StringBase &pinMessage, Os::FileStatus status)
Log event OpenPinError.
void log_WARNING_HI_OpenChipError(const Fw::StringBase &chip, Os::FileStatus status)
Log event OpenChipError.
void log_WARNING_HI_InterruptReadError(U32 expected, U32 got)
Log event InterruptReadError.
void log_WARNING_HI_PollingError(I32 error_number)
Log event PollingError.
void gpioInterrupt_out(FwIndexType portNum, Os::RawTime &cycleStart)
Invoke output port gpioInterrupt.
static constexpr FwSizeType GPIO_POLL_TIMEOUT
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
GpioConfiguration
configure the GPIO pin
@ GPIO_INPUT
Input GPIO pin for direct reading.
@ GPIO_INTERRUPT_FALLING_EDGE
Input GPIO pin triggers interrupt port on falling edge.
@ GPIO_INTERRUPT_BOTH_RISING_AND_FALLING_EDGES
Input GPIO pin triggers interrupt port on both edges.
@ GPIO_INTERRUPT_RISING_EDGE
Input GPIO pin triggers interrupt port on rising edge.
@ GPIO_OUTPUT
Output GPIO pin for direct writing.
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
FileHandle * getHandle() override
returns the raw file handle
Definition: File.cpp:184
Os::FileInterface::Status open(const char *path, Mode mode)
open file with supplied path and mode
Definition: File.cpp:45
@ OP_OK
Operation was successful.
Definition: File.hpp:30
FPP shadow-enum representing Os::File::Status.
T
The raw enum type.
Status now() override
Get the current time.
Definition: RawTime.cpp:36
U32 configuration_to_event_flags(Drv::LinuxGpioDriver::GpioConfiguration configuration)
Drv::GpioStatus errno_to_gpio_status(PlatformIntType errno_input)
U32 configuration_to_handler_flags(Drv::LinuxGpioDriver::GpioConfiguration configuration)
Os::File::Status errno_to_file_status(PlatformIntType errno_input)
char * string_copy(char *destination, const char *source, FwSizeType num)
copy string with null-termination guaranteed
Definition: StringUtils.cpp:6
@ OP_OK
Operation succeeded.
Definition: Os.hpp:26
@ OTHER_ERROR
A catch-all for other errors. Have to look in implementation-specific code.
@ NO_SPACE
No space left on the device for writing.