| #include "tlbmc/hal/gpio/gpio.h" |
| |
| #include <gpiod.h> |
| |
| #include <memory> |
| #include <string> |
| |
| #include "absl/memory/memory.h" |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/str_cat.h" |
| #include "g3/macros.h" |
| #include "gpio_config.pb.h" |
| |
| namespace milotic_tlbmc { |
| |
| namespace internal { |
| absl::StatusOr<int> ParseGpioRequestType(const GpioConfig& config) { |
| switch (config.request_type_case()) { |
| case GpioConfig::kGpioDirection: |
| return config.gpio_direction() == GPIO_DIRECTION_INPUT |
| ? GPIOD_LINE_REQUEST_DIRECTION_INPUT |
| : GPIOD_LINE_REQUEST_DIRECTION_OUTPUT; |
| case GpioConfig::kGpioEventConfig: |
| switch (config.gpio_event_config().event_type()) { |
| case GPIO_EVENT_RISING: |
| return GPIOD_LINE_REQUEST_EVENT_RISING_EDGE; |
| case GPIO_EVENT_FALLING: |
| return GPIOD_LINE_REQUEST_EVENT_FALLING_EDGE; |
| case GPIO_EVENT_BOTH: |
| return GPIOD_LINE_REQUEST_EVENT_BOTH_EDGES; |
| default: |
| return absl::InvalidArgumentError("Unsupported GPIO event type"); |
| } |
| default: |
| return absl::InvalidArgumentError("Unsupported GPIO request type"); |
| } |
| } |
| } // namespace internal |
| |
| absl::StatusOr<bool> Gpio::GetValue() const { |
| if (config_.gpio_direction() == GPIO_DIRECTION_OUTPUT) { |
| return absl::FailedPreconditionError( |
| "GPIO line in OUTPUT direction cannot be read"); |
| } |
| |
| int value = gpiod_line_get_value(gpio_line_.get()); |
| if (value < 0) { |
| return absl::InternalError("Failed to get GPIO line value"); |
| } |
| if (value == 1) { |
| return true; |
| } |
| if (value == 0) { |
| return false; |
| } |
| |
| // This should not happen. |
| return absl::InternalError("Invalid GPIO line value"); |
| } |
| |
| absl::Status Gpio::SetValue(bool value) { |
| if (config_.gpio_direction() != GPIO_DIRECTION_OUTPUT) { |
| return absl::FailedPreconditionError( |
| "GPIO line is not in OUTPUT direction"); |
| } |
| |
| int write_value = value ? 1 : 0; |
| if (gpiod_line_set_value(gpio_line_.get(), write_value) < 0) { |
| return absl::InternalError("Failed to set GPIO line value"); |
| } |
| return absl::OkStatus(); |
| } |
| |
| std::string Gpio::GetGpioName() const { |
| if (config_.identifier_case() == GpioConfig::kGpioIdentifier) { |
| return absl::StrCat(config_.gpio_identifier().chip_id(), ":", |
| config_.gpio_identifier().gpio_num()); |
| } |
| if (config_.identifier_case() == GpioConfig::kLineName) { |
| return config_.line_name(); |
| } |
| // This should not happen. |
| return "Unknown GPIO name"; |
| } |
| |
| absl::StatusOr<std::unique_ptr<Gpio>> Gpio::Create(const GpioConfig& config) { |
| gpiod_line* line = nullptr; |
| |
| // If chip_id and gpio_num are provided, we can use the (chip_id, gpio_num) to |
| // get the GPIO line. Otherwise, we use the line_name to get the GPIO line. |
| if (config.identifier_case() == GpioConfig::kGpioIdentifier) { |
| line = gpiod_line_get(config.gpio_identifier().chip_id().c_str(), |
| config.gpio_identifier().gpio_num()); |
| } else if (config.identifier_case() == GpioConfig::kLineName) { |
| line = gpiod_line_find(config.line_name().c_str()); |
| } else { |
| return absl::InvalidArgumentError(absl::StrCat( |
| "Unsupported GPIO identifier: ", config.identifier_case())); |
| } |
| |
| if (line == nullptr) { |
| return absl::InternalError( |
| absl::StrCat("Failed to find GPIO line: ", config.line_name())); |
| } |
| |
| gpiod_line_request_config request_config{ |
| .consumer = "tlbmc-gpio", |
| .flags = 0, |
| }; |
| |
| ECCLESIA_ASSIGN_OR_RETURN(auto request_type, |
| internal::ParseGpioRequestType(config)); |
| request_config.request_type = request_type; |
| |
| if (config.request_type_case() == GpioConfig::kGpioDirection && |
| config.gpio_direction() == GPIO_DIRECTION_OUTPUT) { |
| if (config.gpio_polarity() == GPIO_POLARITY_UNKNOWN) { |
| return absl::InvalidArgumentError(absl::StrCat( |
| "Output GPIO line ", config.line_name(), " has unknown polarity")); |
| } |
| // The default value of the output GPIO line is the opposite of the |
| // polarity. |
| int default_val = |
| (config.gpio_polarity() == GPIO_POLARITY_ACTIVE_LOW) ? 1 : 0; |
| if (gpiod_line_request(line, &request_config, default_val) < 0) { |
| return absl::InternalError("Failed to request GPIO line"); |
| } |
| } else { |
| if (gpiod_line_request(line, &request_config, /*default_val=*/0) < 0) { |
| return absl::InternalError("Failed to request GPIO line"); |
| } |
| } |
| |
| int fd = -1; |
| if (config.request_type_case() == GpioConfig::kGpioEventConfig) { |
| fd = gpiod_line_event_get_fd(line); |
| if (fd < 0) { |
| return absl::InternalError("Failed to get GPIO line event fd"); |
| } |
| } |
| |
| return absl::WrapUnique(new Gpio(config, GpioLineUniquePtr(line), fd)); |
| } |
| |
| } // namespace milotic_tlbmc |