From 8fd330c54e702719301072357c3db591eb16d489 Mon Sep 17 00:00:00 2001 From: andryblack Date: Fri, 24 Apr 2020 00:16:31 +0300 Subject: [PATCH] linux: userspace GPIO control Allow use host GPIO pins for non-realtime purposes. Signed-off-by: Andrey Kunitsyn --- .travis.yml | 1 + src/linux/Kconfig | 2 + src/linux/Makefile | 2 +- src/linux/analog.c | 8 ++- src/linux/console.c | 2 +- src/linux/gpio.c | 163 +++++++++++++++++++++++++++++++++++++++++++ src/linux/gpio.h | 16 +++-- src/linux/hard_pwm.c | 2 +- src/linux/internal.h | 1 + src/linux/spidev.c | 12 ---- 10 files changed, 187 insertions(+), 22 deletions(-) create mode 100644 src/linux/gpio.c diff --git a/.travis.yml b/.travis.yml index 49aee964..fa751640 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ # This is a travis-ci.org continuous integration configuration file. language: c +dist: bionic addons: apt: diff --git a/src/linux/Kconfig b/src/linux/Kconfig index f4b51ffa..edf575b0 100644 --- a/src/linux/Kconfig +++ b/src/linux/Kconfig @@ -10,6 +10,8 @@ config LINUX_SELECT select HAVE_GPIO_SPI select HAVE_GPIO_HARD_PWM select HAVE_GPIO_I2C + select HAVE_GPIO_BITBANGING + select HAVE_GPIO config BOARD_DIRECTORY string diff --git a/src/linux/Makefile b/src/linux/Makefile index 5cc9c69a..db3c913a 100644 --- a/src/linux/Makefile +++ b/src/linux/Makefile @@ -4,7 +4,7 @@ dirs-y += src/linux src/generic src-y += linux/main.c linux/timer.c linux/console.c linux/watchdog.c src-y += linux/pca9685.c linux/spidev.c linux/analog.c linux/hard_pwm.c -src-y += linux/i2c.c generic/crc16_ccitt.c generic/alloc.c +src-y += linux/i2c.c linux/gpio.c generic/crc16_ccitt.c generic/alloc.c CFLAGS_klipper.elf += -lutil diff --git a/src/linux/analog.c b/src/linux/analog.c index 2ce085a8..aa8fb701 100644 --- a/src/linux/analog.c +++ b/src/linux/analog.c @@ -15,15 +15,17 @@ DECL_CONSTANT("ADC_MAX", 4095); // Assume 12bit adc -DECL_ENUMERATION_RANGE("pin", "analog0", 0, 8); +#define ANALOG_START (1<<8) + +DECL_ENUMERATION_RANGE("pin", "analog0", ANALOG_START, 8); #define IIO_PATH "/sys/bus/iio/devices/iio:device0/in_voltage%d_raw" struct gpio_adc -gpio_adc_setup(uint8_t pin) +gpio_adc_setup(uint32_t pin) { char fname[256]; - snprintf(fname, sizeof(fname), IIO_PATH, pin); + snprintf(fname, sizeof(fname), IIO_PATH, pin-ANALOG_START); int fd = open(fname, O_RDONLY|O_CLOEXEC); if (fd < 0) { diff --git a/src/linux/console.c b/src/linux/console.c index 4afa6151..1c02b7a1 100644 --- a/src/linux/console.c +++ b/src/linux/console.c @@ -53,7 +53,7 @@ set_non_blocking(int fd) return 0; } -static int +int set_close_on_exec(int fd) { int ret = fcntl(fd, F_SETFD, FD_CLOEXEC); diff --git a/src/linux/gpio.c b/src/linux/gpio.c new file mode 100644 index 00000000..d8990581 --- /dev/null +++ b/src/linux/gpio.c @@ -0,0 +1,163 @@ +// Very basic support via a Linux gpiod device +// +// Copyright (C) 2017-2018 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. +#include "autoconf.h" +#include "gpio.h" +#include "command.h" // shutdown +#include "sched.h" // shutdown + +#include // open +#include // snprintf +#include // memset +#include // atexit + +#include "internal.h" // report_errno + +#include +#include +#include + +#define CHIP_FILE_NAME "/dev/gpiochip0" +#define GPIO_CONSUMER "klipper" +#define NUM_LINES 40 + +DECL_ENUMERATION_RANGE("pin", "P0", 0, NUM_LINES); + +struct gpio_line { + int offset; + int fd; + int state; +}; + +static struct gpio_line lines[NUM_LINES]; + +static int gpio_chip_fd = -1; + +static int +get_chip_fd(void) { + int i = 0; + if (gpio_chip_fd <= 0) { + gpio_chip_fd = open(CHIP_FILE_NAME,O_RDWR | O_CLOEXEC); + if (gpio_chip_fd < 0) { + report_errno("open " CHIP_FILE_NAME,-1); + shutdown("Unable to open GPIO chip device"); + } + for (i=0; ioffset = pin; + struct gpio_out g = { .line = line }; + gpio_out_reset(g,val); + return g; +} + +static void +gpio_release_line(struct gpio_line* line) +{ + if (line->fd > 0) { + close(line->fd); + line->fd = -1; + } +} + +void +gpio_out_reset(struct gpio_out g, uint8_t val) +{ + int rv; + struct gpiohandle_request req; + gpio_release_line(g.line); + memset(&req, 0, sizeof(req)); + req.lines = 1; + req.flags = GPIOHANDLE_REQUEST_OUTPUT; + req.lineoffsets[0] = g.line->offset; + req.default_values[0] = !!val; + strncpy(req.consumer_label,GPIO_CONSUMER,sizeof(req.consumer_label) - 1); + rv = ioctl(get_chip_fd(), GPIO_GET_LINEHANDLE_IOCTL, &req); + if (rv < 0) { + report_errno("gpio_out_reset get line",rv); + shutdown("Unable to open out GPIO chip line"); + } + set_close_on_exec(req.fd); + g.line->fd = req.fd; + g.line->state = !!val; +} + +void +gpio_out_write(struct gpio_out g, uint8_t val) +{ + struct gpiohandle_data data; + memset(&data, 0, sizeof(data)); + data.values[0] = !!val; + ioctl(g.line->fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data); + g.line->state = !!val; +} + +void +gpio_out_toggle(struct gpio_out g) +{ + gpio_out_write(g,!g.line->state); +} + +void +gpio_out_toggle_noirq(struct gpio_out g) +{ + gpio_out_toggle(g); +} + +struct gpio_in +gpio_in_setup(uint32_t pin, int8_t pull_up) +{ + struct gpio_line* line = &lines[pin]; + line->offset = pin; + struct gpio_in g = { .line = line }; + gpio_in_reset(g,pull_up); + return g; +} + +void +gpio_in_reset(struct gpio_in g, int8_t pull_up) +{ + int rv; + struct gpiohandle_request req; + gpio_release_line(g.line); + memset(&req, 0, sizeof(req)); + req.lines = 1; + req.flags = GPIOHANDLE_REQUEST_INPUT; +#if defined(GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP) + if (pull_up > 0) { + req.flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP; + } else if (pull_up < 0) { + req.flags |= GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_DOWN; + } +#endif + req.lineoffsets[0] = g.line->offset; + strncpy(req.consumer_label,GPIO_CONSUMER,sizeof(req.consumer_label) - 1); + rv = ioctl(get_chip_fd(), GPIO_GET_LINEHANDLE_IOCTL, &req); + if (rv < 0) { + report_errno("gpio_in_reset get line",rv); + shutdown("Unable to open in GPIO chip line"); + } + set_close_on_exec(req.fd); + g.line->fd = req.fd; +} + +uint8_t +gpio_in_read(struct gpio_in g) +{ + struct gpiohandle_data data; + memset(&data, 0, sizeof(data)); + ioctl(g.line->fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data); + return data.values[0]; +} diff --git a/src/linux/gpio.h b/src/linux/gpio.h index 32e76bf5..e6ef86bd 100644 --- a/src/linux/gpio.h +++ b/src/linux/gpio.h @@ -4,17 +4,25 @@ #include // uint8_t struct gpio_out { - uint32_t pin; + struct gpio_line* line; }; -struct gpio_out gpio_out_setup(uint8_t pin, uint8_t val); +struct gpio_out gpio_out_setup(uint32_t pin, uint8_t val); +void gpio_out_reset(struct gpio_out g, uint8_t val); void gpio_out_toggle_noirq(struct gpio_out g); void gpio_out_toggle(struct gpio_out g); void gpio_out_write(struct gpio_out g, uint8_t val); +struct gpio_in { + struct gpio_line* line; +}; +struct gpio_in gpio_in_setup(uint32_t pin, int8_t pull_up); +void gpio_in_reset(struct gpio_in g, int8_t pull_up); +uint8_t gpio_in_read(struct gpio_in g); + struct gpio_adc { int fd; }; -struct gpio_adc gpio_adc_setup(uint8_t pin); +struct gpio_adc gpio_adc_setup(uint32_t pin); uint32_t gpio_adc_sample(struct gpio_adc g); uint16_t gpio_adc_read(struct gpio_adc g); void gpio_adc_cancel_sample(struct gpio_adc g); @@ -32,7 +40,7 @@ struct gpio_pwm { int fd; uint32_t period; }; -struct gpio_pwm gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint16_t val); +struct gpio_pwm gpio_pwm_setup(uint32_t pin, uint32_t cycle_time, uint16_t val); void gpio_pwm_write(struct gpio_pwm g, uint16_t val); struct i2c_config { diff --git a/src/linux/hard_pwm.c b/src/linux/hard_pwm.c index f14b844b..cfe8b436 100644 --- a/src/linux/hard_pwm.c +++ b/src/linux/hard_pwm.c @@ -37,7 +37,7 @@ DECL_ENUMERATION_RANGE("pin", "pwmchip7/pwm0", HARD_PWM(7, 0), 16); #define PWM_PATH "/sys/class/pwm/pwmchip%u/pwm%u/%s" #define PWM_PATH_BB "/sys/class/pwm/pwm-%u:%u/%s" -struct gpio_pwm gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint16_t val) +struct gpio_pwm gpio_pwm_setup(uint32_t pin, uint32_t cycle_time, uint16_t val) { char filename[256]; char scratch[16]; diff --git a/src/linux/internal.h b/src/linux/internal.h index b47426b3..592c4588 100644 --- a/src/linux/internal.h +++ b/src/linux/internal.h @@ -11,6 +11,7 @@ // console.c void report_errno(char *where, int rc); int set_non_blocking(int fd); +int set_close_on_exec(int fd); int console_setup(char *name); void console_sleep(struct timespec ts); diff --git a/src/linux/spidev.c b/src/linux/spidev.c index 764541e3..166db395 100644 --- a/src/linux/spidev.c +++ b/src/linux/spidev.c @@ -107,15 +107,3 @@ spi_transfer(struct spi_config config, uint8_t receive_data } } } - -// Dummy versions of gpio_out functions -struct gpio_out -gpio_out_setup(uint8_t pin, uint8_t val) -{ - shutdown("gpio_out_setup not supported"); -} - -void -gpio_out_write(struct gpio_out g, uint8_t val) -{ -}