123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- #include "GPIO_Sysfs.h"
- #include <AP_HAL/AP_HAL.h>
- #include <fcntl.h>
- #include <stdio.h>
- #include <sys/stat.h>
- #include <unistd.h>
- #include "Util.h"
- #define LOW 0
- #define HIGH 1
- #define assert_vpin(v_, max_, ...) do { \
- if (v_ >= max_) { \
- hal.console->printf("warning (%s): vpin %u out of range [0, %u)\n",\
- __PRETTY_FUNCTION__, v_, max_); \
- return __VA_ARGS__; \
- } \
- } while (0)
- using namespace Linux;
- static const AP_HAL::HAL& hal = AP_HAL::get_HAL();
- #define UINT32_MAX_STR "4294967295"
- /* Trick to use minimum stack space for each of the params */
- union gpio_params {
- char export_gpio[sizeof("export")];
- char direction[sizeof("gpio" UINT32_MAX_STR "/direction")];
- char value[sizeof("gpio" UINT32_MAX_STR "/value")];
- };
- #define GPIO_BASE_PATH "/sys/class/gpio/"
- #define GPIO_PATH_MAX (sizeof(GPIO_BASE_PATH) + sizeof(gpio_params) - 1)
- DigitalSource_Sysfs::DigitalSource_Sysfs(unsigned pin, int value_fd)
- : _pin(pin)
- , _value_fd(value_fd)
- {
- }
- DigitalSource_Sysfs::~DigitalSource_Sysfs()
- {
- if (_value_fd >= 0) {
- ::close(_value_fd);
- }
- }
- uint8_t DigitalSource_Sysfs::read()
- {
- char char_value;
- if (::pread(_value_fd, &char_value, 1, 0) < 0) {
- hal.console->printf("DigitalSource_Sysfs: Unable to read pin %u value.\n",
- _pin);
- return LOW;
- }
- return char_value - '0';
- }
- void DigitalSource_Sysfs::write(uint8_t value)
- {
- if (::pwrite(_value_fd, value == HIGH ? "1" : "0", 1, 0) < 0) {
- hal.console->printf("DigitalSource_Sysfs: Unable to write pin %u value.\n",
- _pin);
- }
- }
- void DigitalSource_Sysfs::mode(uint8_t output)
- {
- auto gpio_sysfs = GPIO_Sysfs::from(hal.gpio);
- gpio_sysfs->_pinMode(_pin, output);
- }
- void DigitalSource_Sysfs::toggle()
- {
- write(!read());
- }
- void GPIO_Sysfs::init()
- {
- #ifdef HAL_GPIO_SCRIPT
- if (!_script.thread_created) {
- _script.thread_created = true;
- if (!hal.scheduler->thread_create(FUNCTOR_BIND_MEMBER(&GPIO_Sysfs::_gpio_script_thread, void),
- "GPIO_Script", 4096, AP_HAL::Scheduler::PRIORITY_IO, -1)) {
- AP_HAL::panic("Unable to create GPIO_Script thread");
- }
- }
- #endif
- }
- void GPIO_Sysfs::pinMode(uint8_t vpin, uint8_t output)
- {
- #ifdef HAL_GPIO_SCRIPT
- if (vpin >= n_pins && output) {
- return;
- }
- #endif
- assert_vpin(vpin, n_pins);
- _export_pin(vpin);
- _pinMode(pin_table[vpin], output);
- }
- void GPIO_Sysfs::_pinMode(unsigned int pin, uint8_t output)
- {
- const char *dir = output ? "out" : "in";
- char path[GPIO_PATH_MAX];
- int r = snprintf(path, GPIO_PATH_MAX, GPIO_BASE_PATH "gpio%u/direction",
- pin);
- if (r < 0 || r >= (int)GPIO_PATH_MAX
- || Util::from(hal.util)->write_file(path, "%s", dir) < 0) {
- hal.console->printf("GPIO_Sysfs: Unable to set pin %u mode.\n", pin);
- }
- }
- int GPIO_Sysfs::_open_pin_value(unsigned int pin, int flags)
- {
- char path[GPIO_PATH_MAX];
- int fd;
- int r = snprintf(path, GPIO_PATH_MAX, GPIO_BASE_PATH "gpio%u/value", pin);
- if (r < 0 || r >= (int)GPIO_PATH_MAX
- || (fd = open(path, flags | O_CLOEXEC)) < 0) {
- hal.console->printf("GPIO_Sysfs: Unable to get value file descriptor for pin %u.\n",
- pin);
- return -1;
- }
- return fd;
- }
- uint8_t GPIO_Sysfs::read(uint8_t vpin)
- {
- assert_vpin(vpin, n_pins, LOW);
- const unsigned pin = pin_table[vpin];
- int fd = _open_pin_value(pin, O_RDONLY);
- if (fd < 0) {
- goto error;
- }
- char char_value;
- if (::pread(fd, &char_value, 1, 0) < 0) {
- goto error;
- }
- close(fd);
- return char_value - '0';
- error:
- hal.console->printf("GPIO_Sysfs: Unable to read pin %u value.\n", vpin);
- return LOW;
- }
- void GPIO_Sysfs::write(uint8_t vpin, uint8_t value)
- {
- #ifdef HAL_GPIO_SCRIPT
- if (vpin >= n_pins) {
- _gpio_script_write(vpin, value);
- return;
- }
- #endif
- assert_vpin(vpin, n_pins);
- const unsigned pin = pin_table[vpin];
- int fd = _open_pin_value(pin, O_WRONLY);
- if (fd < 0) {
- goto error;
- }
- if (::pwrite(fd, value == HIGH ? "1" : "0", 1, 0) < 0) {
- goto error;
- }
- close(fd);
- return;
- error:
- hal.console->printf("GPIO_Sysfs: Unable to write pin %u value.\n", vpin);
- }
- void GPIO_Sysfs::toggle(uint8_t vpin)
- {
- write(vpin, !read(vpin));
- }
- AP_HAL::DigitalSource* GPIO_Sysfs::channel(uint16_t vpin)
- {
- assert_vpin(vpin, n_pins, nullptr);
- const unsigned pin = pin_table[vpin];
- int value_fd = -1;
- if (_export_pin(vpin)) {
- value_fd = _open_pin_value(pin, O_RDWR);
- }
- /* Even if we couldn't open the fd, return a new DigitalSource and let
- * reads and writes fail later due to invalid. Otherwise we
- * could crash in undesired places */
- return new DigitalSource_Sysfs(pin, value_fd);
- }
- bool GPIO_Sysfs::usb_connected(void)
- {
- return false;
- }
- bool GPIO_Sysfs::_export_pin(uint8_t vpin)
- {
- #ifdef HAL_GPIO_SCRIPT
- if (vpin >= n_pins) {
- return false;
- }
- #endif
- assert_vpin(vpin, n_pins, false);
- const unsigned int pin = pin_table[vpin];
- char gpio_path[GPIO_PATH_MAX];
- int fd;
- int r = snprintf(gpio_path, GPIO_PATH_MAX, GPIO_BASE_PATH "gpio%u", pin);
- if (r < 0 || r >= (int) GPIO_PATH_MAX) {
- goto fail_snprintf;
- }
- if (access(gpio_path, F_OK) == 0) {
- // Already exported
- return true;
- }
- fd = open(GPIO_BASE_PATH "export", O_WRONLY | O_CLOEXEC);
- if (fd < 0) {
- goto fail_open;
- }
- // Try to export
- if (dprintf(fd, "%u", pin) < 0) {
- goto fail_export;
- }
- close(fd);
- return true;
- fail_export:
- close(fd);
- fail_open:
- fail_snprintf:
- hal.console->printf("GPIO_Sysfs: Unable to export pin %u.\n", pin);
- return false;
- }
- #ifdef HAL_GPIO_SCRIPT
- /*
- support using an external script triggered by a write to a GPIO
- value. This is called whenever a GPIO request is made that is for an
- unknown pin value. The script is called by a separate thread, and
- only one script can be run at a time. This prevents the scripts
- using too many resources
- */
- void GPIO_Sysfs::_gpio_script_write(uint8_t vpin, uint8_t value)
- {
- pin_value_t pv;
- pv.pin = vpin;
- pv.value = value;
- _script.pending.push(pv);
- }
- /*
- thread for running GPIO scripts
- */
- void GPIO_Sysfs::_gpio_script_thread(void)
- {
- while (true) {
- // don't run more than 20/sec
- hal.scheduler->delay(50);
- pin_value_t pv;
- if (_script.pending.pop(pv)) {
- char cmd[100];
- snprintf(cmd, sizeof(cmd)-1, "/bin/sh %s %u %u", HAL_GPIO_SCRIPT, pv.pin, pv.value);
- hal.console->printf("Running: %s\n", cmd);
- const int system_ret = system(cmd);
- if (system_ret != 0) {
- hal.console->printf("Unexpected return value (%d)\n", system_ret);
- }
- }
- }
- }
- #endif // HAL_GPIO_SCRIPT
|