blob: 67c88d56833e87d5b1ae5cd35c70d62b59cb286e [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "gpioutil.h"
#include <dirent.h>
#include <lib/component/incoming/cpp/service.h>
#include <lib/fdio/directory.h>
#include <lib/fit/defer.h>
#include <stdio.h>
#include <filesystem>
// Directory path to the GPIO class
#define GPIO_DEV_CLASS_PATH "/dev/class/gpio/"
constexpr char kGpioDevClassDir[] = GPIO_DEV_CLASS_PATH;
constexpr char kGpioDevClassNsDir[] = "/ns" GPIO_DEV_CLASS_PATH;
int ParseArgs(int argc, char** argv, GpioFunc* func, uint8_t* write_value,
fuchsia_hardware_gpio::wire::GpioFlags* in_flag, uint8_t* out_value,
uint64_t* ds_ua) {
if (argc < 2) {
return -1;
}
/* Following functions allow no args */
switch (argv[1][0]) {
case 'l':
*func = List;
return 0;
}
if (argc < 3) {
return -1;
}
*write_value = 0;
*in_flag = fuchsia_hardware_gpio::wire::GpioFlags::kNoPull;
*out_value = 0;
*ds_ua = 0;
unsigned long flag = 0;
switch (argv[1][0]) {
case 'n':
*func = GetName;
break;
case 'r':
*func = Read;
break;
case 'w':
*func = Write;
if (argc < 4) {
return -1;
}
*write_value = static_cast<uint8_t>(std::stoul(argv[3]));
break;
case 'i':
*func = ConfigIn;
if (argc < 4) {
return -1;
}
flag = std::stoul(argv[3]);
if (flag > 3) {
fprintf(stderr, "Invalid flag\n\n");
return -1;
}
*in_flag = static_cast<fuchsia_hardware_gpio::wire::GpioFlags>(flag);
break;
case 'o':
*func = ConfigOut;
if (argc < 4) {
return -1;
}
*out_value = static_cast<uint8_t>(std::stoul(argv[3]));
break;
case 'd':
if (argc >= 4) {
*func = SetDriveStrength;
*ds_ua = static_cast<uint64_t>(std::stoull(argv[3]));
} else if (argc == 3) {
*func = GetDriveStrength;
} else {
return -1;
}
break;
default:
*func = Invalid;
return -1;
}
return 0;
}
zx::result<> ListGpios() {
const char* dev_class_dir = kGpioDevClassNsDir;
if (!std::filesystem::is_directory(dev_class_dir)) {
dev_class_dir = kGpioDevClassDir;
}
for (auto const& dir_entry : std::filesystem::directory_iterator(dev_class_dir)) {
const char* gpio_path = dir_entry.path().c_str();
zx::result client_end = component::Connect<fuchsia_hardware_gpio::Gpio>(gpio_path);
if (client_end.is_error()) {
fprintf(stderr, "Could not connect to client from %s: %s\n", gpio_path,
client_end.status_string());
return client_end.take_error();
}
fidl::WireSyncClient<fuchsia_hardware_gpio::Gpio> client(std::move(client_end.value()));
const fidl::WireResult result_pin = client->GetPin();
if (!result_pin.ok()) {
fprintf(stderr, "Could not get pin from %s: %s\n", gpio_path, result_pin.status_string());
return zx::error(result_pin.status());
}
const fit::result response_pin = result_pin.value();
if (response_pin.is_error()) {
fprintf(stderr, "Could not get pin from %s: %s\n", gpio_path,
zx_status_get_string(response_pin.error_value()));
return zx::error(result_pin.status());
}
const fidl::WireResult result_name = client->GetName();
if (!result_name.ok()) {
fprintf(stderr, "Could not get name from %s: %s\n", gpio_path, result_name.status_string());
return zx::error(result_name.status());
}
const fit::result response_name = result_name.value();
if (response_name.is_error()) {
fprintf(stderr, "Could not get name from %s: %s\n", gpio_path,
zx_status_get_string(response_name.error_value()));
return zx::error(result_name.status());
}
uint32_t pin = response_pin.value()->pin;
std::string_view name = response_name.value()->name.get();
printf("[gpio-%d] %.*s\n", pin, static_cast<int>(name.length()), name.data());
}
return zx::ok();
}
zx::result<fidl::WireSyncClient<fuchsia_hardware_gpio::Gpio>> FindGpioClientByName(
std::string_view name) {
const char* dev_class_dir = kGpioDevClassNsDir;
if (!std::filesystem::is_directory(dev_class_dir)) {
dev_class_dir = kGpioDevClassDir;
}
for (auto const& dir_entry : std::filesystem::directory_iterator(dev_class_dir)) {
const char* gpio_path = dir_entry.path().c_str();
zx::result client_end = component::Connect<fuchsia_hardware_gpio::Gpio>(gpio_path);
if (client_end.is_error()) {
fprintf(stderr, "Could not connect to client from %s: %s\n", gpio_path,
client_end.status_string());
return client_end.take_error();
}
fidl::WireSyncClient<fuchsia_hardware_gpio::Gpio> client(std::move(client_end.value()));
const fidl::WireResult result_name = client->GetName();
if (!result_name.ok()) {
fprintf(stderr, "Could not get name from %s: %s\n", gpio_path, result_name.status_string());
return zx::error(result_name.status());
}
const fit::result response_name = result_name.value();
if (response_name.is_error()) {
fprintf(stderr, "Could not get name from %s: %s\n", gpio_path,
zx_status_get_string(response_name.error_value()));
return zx::error(result_name.status());
}
std::string_view gpio_name = response_name.value()->name.get();
if (name == gpio_name) {
return zx::ok(std::move(client));
}
}
return zx::error(ZX_ERR_NOT_FOUND);
}
int ClientCall(fidl::WireSyncClient<fuchsia_hardware_gpio::Gpio> client, GpioFunc func,
uint8_t write_value, fuchsia_hardware_gpio::wire::GpioFlags in_flag,
uint8_t out_value, uint64_t ds_ua) {
switch (func) {
case GetName: {
auto result_pin = client->GetPin();
if (!result_pin.ok()) {
fprintf(stderr, "Could not get Pin\n");
return -2;
}
auto result_name = client->GetName();
if (!result_name.ok()) {
fprintf(stderr, "Could not get Name\n");
return -2;
}
auto pin = result_pin->value()->pin;
auto name = result_name->value()->name.get();
printf("GPIO Name: [gpio-%d] %.*s\n", pin, static_cast<int>(name.length()), name.data());
break;
}
case Read: {
auto result = client->Read();
if ((result.status() != ZX_OK) || result->is_error()) {
fprintf(stderr, "Could not read GPIO\n");
return -2;
}
printf("GPIO Value: %u\n", result->value()->value);
break;
}
case Write: {
auto result = client->Write(write_value);
if ((result.status() != ZX_OK) || result->is_error()) {
fprintf(stderr, "Could not write to GPIO\n");
return -2;
}
break;
}
case ConfigIn: {
auto result = client->ConfigIn(in_flag);
if ((result.status() != ZX_OK) || result->is_error()) {
fprintf(stderr, "Could not configure GPIO as input\n");
return -2;
}
break;
}
case ConfigOut: {
auto result = client->ConfigOut(out_value);
if ((result.status() != ZX_OK) || result->is_error()) {
fprintf(stderr, "Could not configure GPIO as output\n");
return -2;
}
break;
}
case SetDriveStrength: {
auto result = client->SetDriveStrength(ds_ua);
if ((result.status() != ZX_OK) || result->is_error()) {
fprintf(stderr, "Could not set GPIO drive strength\n");
return -2;
}
printf("Set drive strength to %lu\n", result->value()->actual_ds_ua);
break;
}
case GetDriveStrength: {
auto result = client->GetDriveStrength();
if ((result.status() != ZX_OK) || result->is_error()) {
fprintf(stderr, "Could not get drive strength\n");
return -2;
}
printf("Drive Strength: %lu ua\n", result->value()->result_ua);
break;
}
default:
fprintf(stderr, "Invalid function\n\n");
return -1;
}
return 0;
}