| Line | Branch | Exec | Source |
|---|---|---|---|
| 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 | #include <Drv/LinuxGpioDriver/LinuxGpioDriver.hpp> | ||
| 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 | |||
| 28 | ✗ | Os::File::Status errno_to_file_status(int errno_input) { | |
| 29 | ✗ | Os::File::Status status = Os::File::Status::OTHER_ERROR; | |
| 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: | |
| 44 | ✗ | status = Os::File::Status::NO_SPACE; | |
| 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: | ||
| 58 | ✗ | status = Os::File::Status::OTHER_ERROR; | |
| 59 | ✗ | break; | |
| 60 | } | ||
| 61 | ✗ | return status; | |
| 62 | } | ||
| 63 | |||
| 64 | ✗ | Drv::GpioStatus errno_to_gpio_status(int errno_input) { | |
| 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: | |
| 71 | ✗ | status = Drv::GpioStatus::INVALID_MODE; | |
| 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 | |||
| 86 | ✗ | U32 configuration_to_handler_flags(Drv::LinuxGpioDriver::GpioConfiguration configuration) { | |
| 87 | ✗ | U32 flags = 0; | |
| 88 | ✗ | switch (configuration) { | |
| 89 | ✗ | case LinuxGpioDriver::GPIO_OUTPUT: | |
| 90 | ✗ | flags = GPIOHANDLE_REQUEST_OUTPUT; | |
| 91 | ✗ | break; | |
| 92 | // Cascade intended | ||
| 93 | ✗ | case LinuxGpioDriver::GPIO_INPUT: | |
| 94 | case LinuxGpioDriver::GPIO_INTERRUPT_RISING_EDGE: | ||
| 95 | case LinuxGpioDriver::GPIO_INTERRUPT_FALLING_EDGE: | ||
| 96 | case LinuxGpioDriver::GPIO_INTERRUPT_BOTH_RISING_AND_FALLING_EDGES: | ||
| 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) { | |
| 110 | ✗ | case LinuxGpioDriver::GPIO_OUTPUT: | |
| 111 | ✗ | flags = GPIO_V2_LINE_FLAG_OUTPUT; | |
| 112 | ✗ | break; | |
| 113 | ✗ | case LinuxGpioDriver::GPIO_INPUT: | |
| 114 | ✗ | flags = GPIO_V2_LINE_FLAG_INPUT; | |
| 115 | ✗ | break; | |
| 116 | ✗ | case LinuxGpioDriver::GPIO_INTERRUPT_RISING_EDGE: | |
| 117 | ✗ | flags = GPIO_V2_LINE_FLAG_INPUT | GPIO_V2_LINE_FLAG_EDGE_RISING; | |
| 118 | ✗ | break; | |
| 119 | ✗ | case LinuxGpioDriver::GPIO_INTERRUPT_FALLING_EDGE: | |
| 120 | ✗ | flags = GPIO_V2_LINE_FLAG_INPUT | GPIO_V2_LINE_FLAG_EDGE_FALLING; | |
| 121 | ✗ | break; | |
| 122 | ✗ | case LinuxGpioDriver::GPIO_INTERRUPT_BOTH_RISING_AND_FALLING_EDGES: | |
| 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 | |||
| 133 | ✗ | U32 configuration_to_event_flags(Drv::LinuxGpioDriver::GpioConfiguration configuration) { | |
| 134 | ✗ | U32 flags = 0; | |
| 135 | ✗ | switch (configuration) { | |
| 136 | ✗ | case LinuxGpioDriver::GPIO_INTERRUPT_RISING_EDGE: | |
| 137 | ✗ | flags = GPIOEVENT_REQUEST_RISING_EDGE; | |
| 138 | ✗ | break; | |
| 139 | ✗ | case LinuxGpioDriver::GPIO_INTERRUPT_FALLING_EDGE: | |
| 140 | ✗ | flags = GPIOEVENT_REQUEST_FALLING_EDGE; | |
| 141 | ✗ | break; | |
| 142 | ✗ | case LinuxGpioDriver::GPIO_INTERRUPT_BOTH_RISING_AND_FALLING_EDGES: | |
| 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 | |||
| 152 | ✗ | LinuxGpioDriver ::~LinuxGpioDriver() { | |
| 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 | ||
| 169 | ✗ | Os::File::Status status = Os::File::OP_OK; | |
| 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) { | ||
| 206 | ✗ | Os::File::Status status = Os::File::OP_OK; | |
| 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) { | ||
| 232 | ✗ | Os::File::Status status = Os::File::OP_OK; | |
| 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 | |||
| 252 | ✗ | Os::File::Status LinuxGpioDriver ::open(const char* device, | |
| 253 | const U32 gpio, | ||
| 254 | const GpioConfiguration& configuration, | ||
| 255 | const Fw::Logic& default_state) { | ||
| 256 | ✗ | Os::File::Status status = Os::File::OP_OK; | |
| 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 | ||
| 328 | ✗ | case GPIO_INTERRUPT_RISING_EDGE: | |
| 329 | case GPIO_INTERRUPT_FALLING_EDGE: | ||
| 330 | case GPIO_INTERRUPT_BOTH_RISING_AND_FALLING_EDGES: | ||
| 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) { | |
| 352 | ✗ | Drv::GpioStatus status = Drv::GpioStatus::INVALID_MODE; | |
| 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) { | |
| 383 | ✗ | Drv::GpioStatus status = Drv::GpioStatus::INVALID_MODE; | |
| 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) { | |
| 457 | ✗ | this->log_WARNING_HI_InterruptTimeError( | |
| 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 | ||
| 475 |