From 2b98fc78695284928689eaa2462f3f35971aa865 Mon Sep 17 00:00:00 2001 From: mihais Date: Sat, 23 Sep 2017 17:13:06 +0300 Subject: [PATCH] gpio: chardev interface / multiple gpio support for sysfs Signed-off-by: Mihai Stefanescu Signed-off-by: Brendan Le Foll --- api/mraa/gpio.h | 76 +- examples/gpio_multiple_rw.c | 103 +++ include/gpio/gpio_chardev.h | 12 +- include/mraa_internal_types.h | 12 +- src/gpio/gpio.c | 1299 ++++++++++++++++----------------- src/gpio/gpio_chardev.c | 110 ++- src/mraa.c | 39 +- 7 files changed, 887 insertions(+), 764 deletions(-) create mode 100644 examples/gpio_multiple_rw.c diff --git a/api/mraa/gpio.h b/api/mraa/gpio.h index c60dae0..f382d1f 100644 --- a/api/mraa/gpio.h +++ b/api/mraa/gpio.h @@ -104,6 +104,15 @@ typedef enum { MRAA_GPIO_PUSH_PULL = 1, /**< Push Pull Configuration */ } mraa_gpio_out_driver_mode_t; +typedef long long unsigned int mraa_timestamp_t; + +typedef struct { + int id; + mraa_timestamp_t timestamp; +} mraa_gpio_event; + +typedef mraa_gpio_event* mraa_gpio_events_t; + /** * Initialise gpio_context, based on board number * @@ -112,7 +121,15 @@ typedef enum { */ mraa_gpio_context mraa_gpio_init(int pin); -mraa_gpio_context mraa_gpio_init_multiple(int pins[], int num_pins); +/** + * Initialise gpio_context, based on board number, for multiple pins (can be one). + * + * @param pins Pin array read from the board + * @param num_pins Number of pins - must be the same as the pins array length provided + * as the first argument. + * @returns gpio context or NULL + */ +mraa_gpio_context mraa_gpio_init_multi(int pins[], int num_pins); /** * Initialise gpio context without any mapping to a pin @@ -131,13 +148,11 @@ mraa_gpio_context mraa_gpio_init_raw(int gpiopin); */ mraa_result_t mraa_gpio_edge_mode(mraa_gpio_context dev, mraa_gpio_edge_t mode); -mraa_result_t mraa_gpio_edge_mode_multiple(mraa_gpio_context dev, mraa_gpio_edge_t mode); - /** - * Set an interrupt on pin + * Set an interrupt on pin(s). * * @param dev The Gpio context - * @param edge The edge mode to set the gpio into + * @param edge The edge mode to set the gpio(s) into * @param fptr Function pointer to function to be called when interrupt is * triggered * @param args Arguments passed to the interrupt handler (fptr) @@ -145,11 +160,19 @@ mraa_result_t mraa_gpio_edge_mode_multiple(mraa_gpio_context dev, mraa_gpio_edge */ mraa_result_t mraa_gpio_isr(mraa_gpio_context dev, mraa_gpio_edge_t edge, void (*fptr)(void*), void* args); -mraa_result_t mraa_gpio_isr_multiple(mraa_gpio_context dev, mraa_gpio_edge_t edge, void (*fptr)(void*), void* args); +/** + * Get an array of structures describing triggered events. + * + * @param dev The Gpio context + * @return Array of structures containing pairs of pin id's and the associated timestamp. + * An event with negative id value indicates that no event was triggered for the respective pin. + * The array length is that of the number of pins provided in mraa_gpio_init_multi(). + */ +mraa_gpio_events_t mraa_gpio_get_events(mraa_gpio_context dev); /** * Stop the current interrupt watcher on this Gpio, and set the Gpio edge mode - * to MRAA_GPIO_EDGE_NONE + * to MRAA_GPIO_EDGE_NONE(only for sysfs interface). * * @param dev The Gpio context * @return Result of operation @@ -157,37 +180,36 @@ mraa_result_t mraa_gpio_isr_multiple(mraa_gpio_context dev, mraa_gpio_edge_t edg mraa_result_t mraa_gpio_isr_exit(mraa_gpio_context dev); /** - * Set Gpio Output Mode, + * Set Gpio(s) Output Mode, * * @param dev The Gpio context - * @param mode The Gpio Output Mode + * @param mode The Gpio(s) Output Mode * @return Result of operation */ mraa_result_t mraa_gpio_mode(mraa_gpio_context dev, mraa_gpio_mode_t mode); /** - * Set Gpio direction + * Set Gpio(s) direction * * @param dev The Gpio context - * @param dir The direction of the Gpio + * @param dir The direction of the Gpio(s) * @return Result of operation */ mraa_result_t mraa_gpio_dir(mraa_gpio_context dev, mraa_gpio_dir_t dir); -mraa_result_t mraa_gpio_dir_multiple(mraa_gpio_context dev, mraa_gpio_dir_t dir); - /** - * Read Gpio direction + * Read Gpio(s) direction * * @param dev The Gpio context - * @param dir The address where to store the Gpio direction + * @param dir The address where to store the Gpio(s) direction * @return Result of operation */ mraa_result_t mraa_gpio_read_dir(mraa_gpio_context dev, mraa_gpio_dir_t *dir); /** * Close the Gpio context - * - Will free the memory for the context and unexport the Gpio + * - Will free the memory for the context and unexport the Gpio - sysfs interface. + * - Will free up the memory used by context and close related file descriptors - chardev interface. * * @param dev The Gpio context * @return Result of operation @@ -203,10 +225,15 @@ mraa_result_t mraa_gpio_close(mraa_gpio_context dev); */ int mraa_gpio_read(mraa_gpio_context dev); -/* Values array is provided by user. Must be the same size as the array passed in init. - * It will be overwritten with the read results. +/** + * Read the Gpio(s) value. The user must provide an integer array with a length equal to the + * number of pins provided to mraa_gpio_init_multi() function. + * + * @param dev The Gpio context + * @param output_values The array provided by the user. Existing values will be overwritten with the newly read ones. + * @return Result of operation */ -mraa_result_t mraa_gpio_read_multiple(mraa_gpio_context dev, int output_values[]); +mraa_result_t mraa_gpio_read_multi(mraa_gpio_context dev, int output_values[]); /** * Write to the Gpio Value. @@ -217,7 +244,16 @@ mraa_result_t mraa_gpio_read_multiple(mraa_gpio_context dev, int output_values[] */ mraa_result_t mraa_gpio_write(mraa_gpio_context dev, int value); -mraa_result_t mraa_gpio_write_multiple(mraa_gpio_context dev, int input_values[]); +/** + * Write to the Gpio(s) Value. The user must provide an integer array with a length equal to the + * number of pins provided to mraa_gpio_init_multi() function. + * + * @param dev The Gpio context + * @param output_values The array provided by the user. It must contain the values intended to be written + * to the gpio pins, in the same order as the init function. + * @return Result of operation + */ +mraa_result_t mraa_gpio_write_multi(mraa_gpio_context dev, int input_values[]); /** * Change ownership of the context. diff --git a/examples/gpio_multiple_rw.c b/examples/gpio_multiple_rw.c new file mode 100644 index 0000000..3432ee2 --- /dev/null +++ b/examples/gpio_multiple_rw.c @@ -0,0 +1,103 @@ +/* This currently works only on Intel Joule Platform. */ + +#include +#include +#include +#include +#include + +#include "mraa/gpio.h" + +int running = 0; + +void +sig_handler(int signo) +{ + if (signo == SIGINT) { + printf("Closing down nicely\n"); + running = -1; + } +} + +int main(int argc, char** argv) +{ + mraa_result_t r = MRAA_SUCCESS; + /* These are Joule's onboard LED's. */ + int default_joule_leds[] = {100, 101, 102, 103}; + int *gpio_pins; + int num_pins; + int *input_values, *output_values; + + printf("Provide int arg(s) if you want to toggle gpio pins other than Joule's onboard LED's\n"); + + if (argc < 2) { + gpio_pins = default_joule_leds; + num_pins = 4; + } else { + num_pins = argc - 1; + gpio_pins = malloc(num_pins * sizeof(int)); + + for (int i = 0; i < num_pins; ++i) { + gpio_pins[i] = strtol(argv[i+1], NULL, 10); + } + } + + /* Allocate input and output values arrays. */ + input_values = malloc(num_pins * sizeof(int)); + output_values = malloc(num_pins * sizeof(int)); + + mraa_init(); + fprintf(stdout, "MRAA Version: %s\nStarting program...\n", mraa_get_version()); + + mraa_gpio_context gpio = mraa_gpio_init_multi(gpio_pins, num_pins); + if (gpio == NULL) { + fprintf(stderr, "Error during gpio initialization\n"); + exit(1); + } + + r = mraa_gpio_dir(gpio, MRAA_GPIO_OUT); + if (r != MRAA_SUCCESS) { + mraa_result_print(r); + } + + signal(SIGINT, sig_handler); + + /* Set input values first. */ + memset(input_values, 0, num_pins * sizeof(int)); + + while (running == 0) { + r = mraa_gpio_write_multi(gpio, input_values); + + sleep(1); + + if (r != MRAA_SUCCESS) { + mraa_result_print(r); + } else { + r = mraa_gpio_read_multi(gpio, output_values); + if (r != MRAA_SUCCESS) { + mraa_result_print(r); + } + } + + for (int i = 0; i < num_pins; ++i) { + input_values[i] = (input_values[i] + 1) % 2; + } + } + + memset(input_values, 0, num_pins * sizeof(int)); + mraa_gpio_write_multi(gpio, input_values); + + r = mraa_gpio_close(gpio); + if (r != MRAA_SUCCESS) { + mraa_result_print(r); + } + + /* Cleanup. */ + if (argc >= 2) { + free(gpio_pins); + } + free(input_values); + free(output_values); + + return r; +} \ No newline at end of file diff --git a/include/gpio/gpio_chardev.h b/include/gpio/gpio_chardev.h index befca25..ebcffb0 100644 --- a/include/gpio/gpio_chardev.h +++ b/include/gpio/gpio_chardev.h @@ -29,6 +29,7 @@ extern "C" { #endif #include "mraa_internal.h" +#include "linux/gpio.h" typedef struct { int chip_fd; @@ -38,6 +39,8 @@ typedef struct { typedef struct gpioline_info mraa_gpiod_line_info; void _mraa_free_gpio_groups(mraa_gpio_context dev); +void _mraa_close_gpio_event_handles(mraa_gpio_context dev); +void _mraa_close_gpio_desc(mraa_gpio_context dev); int _mraa_gpiod_ioctl(int fd, unsigned long gpio_request, void* data); mraa_gpiod_chip_info* mraa_get_chip_info_by_path(const char* path); @@ -45,6 +48,7 @@ mraa_gpiod_chip_info* mraa_get_chip_info_by_name(const char* name); mraa_gpiod_chip_info* mraa_get_chip_info_by_label(const char* label); mraa_gpiod_chip_info* mraa_get_chip_info_by_number(unsigned number); +mraa_gpiod_line_info* mraa_get_line_info_from_descriptor(int chip_fd, unsigned line_number); mraa_gpiod_line_info* mraa_get_line_info_by_chip_number(unsigned chip_number, unsigned line_number); mraa_gpiod_line_info* mraa_get_line_info_by_chip_name(const char* chip_name, unsigned line_number); mraa_gpiod_line_info* mraa_get_line_info_by_chip_label(const char* chip_label, unsigned line_number); @@ -52,8 +56,14 @@ mraa_gpiod_line_info* mraa_get_line_info_by_chip_label(const char* chip_label, u int mraa_get_lines_handle(int chip_fd, unsigned line_offsets[], unsigned num_lines, unsigned flags, unsigned default_value); int mraa_set_line_values(int line_handle, unsigned int num_lines, unsigned char input_values[]); int mraa_get_line_values(int line_handle, unsigned int num_lines, unsigned char output_values[]); -int mraa_get_number_of_gpio_chips(); +mraa_boolean_t mraa_is_gpio_line_kernel_owned(mraa_gpiod_line_info *linfo); +mraa_boolean_t mraa_is_gpio_line_dir_out(mraa_gpiod_line_info *linfo); +mraa_boolean_t mraa_is_gpio_line_active_low(mraa_gpiod_line_info *linfo); +mraa_boolean_t mraa_is_gpio_line_open_drain(mraa_gpiod_line_info *linfo); +mraa_boolean_t mraa_is_gpio_line_open_source(mraa_gpiod_line_info *linfo); + +int mraa_get_number_of_gpio_chips(); /* Multiple gpio support. */ typedef struct _gpio_group* mraa_gpiod_group_t; diff --git a/include/mraa_internal_types.h b/include/mraa_internal_types.h index 63a5fd8..fb34a43 100644 --- a/include/mraa_internal_types.h +++ b/include/mraa_internal_types.h @@ -155,18 +155,12 @@ struct _gpio { AGpio *bgpio; #endif - /* TODO: The below members should be integrated in gpio_group struct. */ - int dev_fd; - int gpiod_handle; - unsigned int gpio_chip; - unsigned int gpio_line; - - /* Multiple gpio support. These members are treated separately for now until multiple gpio support is accepted. */ - unsigned int num_chips; struct _gpio_group *gpio_group; - /* Pin index passed by the user to gpio_group structures. */ + unsigned int num_chips; int *pin_to_gpio_table; unsigned int num_pins; + mraa_gpio_events_t events; + int *provided_pins; struct _gpio *next; }; diff --git a/src/gpio/gpio.c b/src/gpio/gpio.c index 8d69e4f..f46ace8 100644 --- a/src/gpio/gpio.c +++ b/src/gpio/gpio.c @@ -39,6 +39,7 @@ #include #include #include +#include #define SYSFS_CLASS_GPIO "/sys/class/gpio" #define MAX_SIZE 64 @@ -58,6 +59,16 @@ _mraa_gpio_get_valfp(mraa_gpio_context dev) return MRAA_SUCCESS; } +void +mraa_gpio_close_event_handles_sysfs(int fds[], int num_fds) +{ + for (int i = 0; i < num_fds; ++i) { + close(fds[i]); + } + + free(fds); +} + static mraa_gpio_context mraa_gpio_init_internal(mraa_adv_func_t* func_table, int pin) { @@ -111,13 +122,15 @@ mraa_gpio_init_internal(mraa_adv_func_t* func_table, int pin) } else { int export = open(SYSFS_CLASS_GPIO "/export", O_WRONLY); if (export == -1) { - syslog(LOG_ERR, "gpio%i: init: Failed to open 'export' for writing: %s", pin, strerror(errno)); + syslog(LOG_ERR, "gpio%i: init: Failed to open 'export' for writing: %s", + pin, strerror(errno)); status = MRAA_ERROR_INVALID_RESOURCE; goto init_internal_cleanup; } length = snprintf(bu, sizeof(bu), "%d", dev->pin); if (write(export, bu, length * sizeof(char)) == -1) { - syslog(LOG_ERR, "gpio%i: init: Failed to write to 'export': %s", pin, strerror(errno)); + syslog(LOG_ERR, "gpio%i: init: Failed to write to 'export': %s", + pin, strerror(errno)); close(export); status = MRAA_ERROR_INVALID_RESOURCE; goto init_internal_cleanup; @@ -127,12 +140,20 @@ mraa_gpio_init_internal(mraa_adv_func_t* func_table, int pin) } } + /* We only have one pin. No need for multiple pin legacy support. */ + dev->num_pins = 1; + dev->next = NULL; + + /* Initialize events array. */ + dev->events = NULL; + init_internal_cleanup: if (status != MRAA_SUCCESS) { if (dev != NULL) free(dev); return NULL; } + return dev; } @@ -140,11 +161,17 @@ mraa_gpio_context mraa_gpio_init(int pin) { mraa_board_t* board = plat; + if (board == NULL) { syslog(LOG_ERR, "gpio%i: init: platform not initialised", pin); return NULL; } + if (board->chardev_capable) { + int pins[1] = { pin }; + return mraa_gpio_init_multi(pins, 1); + } + if (mraa_is_sub_platform_id(pin)) { syslog(LOG_NOTICE, "gpio%i: init: Using sub platform", pin); board = board->sub_platform; @@ -156,7 +183,8 @@ mraa_gpio_init(int pin) } if (pin < 0 || pin >= board->phy_pin_count) { - syslog(LOG_ERR, "gpio: init: pin %i beyond platform pin count (%i)", pin, board->phy_pin_count); + syslog(LOG_ERR, "gpio: init: pin %i beyond platform pin count (%i)", + pin, board->phy_pin_count); return NULL; } if (board->pins[pin].capabilities.gpio != 1) { @@ -176,20 +204,6 @@ mraa_gpio_init(int pin) return NULL; } - if (board->chardev_capable) { - mraa_gpiod_chip_info* cinfo = mraa_get_chip_info_by_number(board->pins[pin].gpio.gpio_chip); - if (!cinfo) { - syslog(LOG_ERR, "[GPIOD_INTERFACE]: error getting gpio chip info"); - free(r); - return NULL; - } - r->dev_fd = cinfo->chip_fd; - r->gpio_chip = board->pins[pin].gpio.gpio_chip; - r->gpio_line = board->pins[pin].gpio.gpio_line; - r->gpiod_handle = -1; - free(cinfo); - } - if (r->phy_pin == -1) r->phy_pin = pin; @@ -201,157 +215,200 @@ mraa_gpio_init(int pin) } } - /* Just one gpio line. */ - r->num_pins = 1; - return r; } mraa_gpio_context -mraa_gpio_init_multiple(int pins[], int num_pins) +mraa_gpio_chardev_init(int pins[], int num_pins) { int chip_id, line_offset; mraa_gpio_context dev; - mraa_board_t* board = plat; mraa_gpiod_group_t gpio_group; + mraa_board_t* board = plat; + + dev = (mraa_gpio_context) calloc(1, sizeof(struct _gpio)); + if (dev == NULL) { + syslog(LOG_CRIT, "[GPIOD_INTERFACE]: Failed to allocate memory for context"); + return NULL; + } + + dev->pin_to_gpio_table = malloc(num_pins * sizeof(int)); + if (dev->pin_to_gpio_table == NULL) { + syslog(LOG_CRIT, "[GPIOD_INTERFACE]: Failed to allocate memory for internal member"); + return NULL; + } + + dev->num_chips = mraa_get_number_of_gpio_chips(); + if (dev->num_chips <= 0) { + free(dev); + return NULL; + } + + dev->num_pins = num_pins; + + gpio_group = calloc(dev->num_chips, sizeof(struct _gpio_group)); + if (gpio_group == NULL) { + syslog(LOG_CRIT, "[GPIOD_INTERFACE]: Failed to allocate memory for internal member"); + return NULL; + } + + for (int i = 0; i < dev->num_chips; ++i) { + gpio_group[i].gpio_chip = i; + /* Just to be sure realloc has the desired behaviour. */ + gpio_group[i].gpio_lines = NULL; + } + + for (int i = 0; i < num_pins; ++i) { + if (mraa_is_sub_platform_id(pins[i])) { + syslog(LOG_NOTICE, "[GPIOD_INTERFACE]: init: Using sub platform for %d", pins[i]); + board = board->sub_platform; + if (board == NULL) { + syslog(LOG_ERR, "[GPIOD_INTERFACE]: init: Sub platform not initialised for pin %d", pins[i]); + return NULL; + } + pins[i] = mraa_get_sub_platform_index(pins[i]); + } + + if (pins[i] < 0 || pins[i] >= board->phy_pin_count) { + syslog(LOG_ERR, "[GPIOD_INTERFACE]: init: pin %d beyond platform pin count (%d)", + pins[i], board->phy_pin_count); + return NULL; + } + + if (board->pins[pins[i]].capabilities.gpio != 1) { + syslog(LOG_ERR, "[GPIOD_INTERFACE]: init: pin %d not capable of gpio", pins[i]); + return NULL; + } + + if (board->pins[pins[i]].gpio.mux_total > 0) { + if (mraa_setup_mux_mapped(board->pins[pins[i]].gpio) != MRAA_SUCCESS) { + syslog(LOG_ERR, "[GPIOD_INTERFACE]: init: unable to setup muxes for pin %d", pins[i]); + return NULL; + } + } + + chip_id = board->pins[pins[i]].gpio.gpio_chip; + line_offset = board->pins[pins[i]].gpio.gpio_line; + + /* Map pin to _gpio_group structure. */ + dev->pin_to_gpio_table[i] = chip_id; + + if (!gpio_group[chip_id].is_required) { + mraa_gpiod_chip_info* cinfo = mraa_get_chip_info_by_number(chip_id); + if (!cinfo) { + syslog(LOG_ERR, "[GPIOD_INTERFACE]: error getting gpio_chip_info for chip %d", chip_id); + return NULL; + } + + gpio_group[chip_id].dev_fd = cinfo->chip_fd; + gpio_group[chip_id].is_required = 1; + gpio_group[chip_id].gpiod_handle = -1; + + free(cinfo); + } + + int line_in_group; + line_in_group = gpio_group[chip_id].num_gpio_lines; + gpio_group[chip_id].gpio_lines = realloc(gpio_group[chip_id].gpio_lines, + (line_in_group + 1) * sizeof(unsigned int)); + if (gpio_group[chip_id].gpio_lines == NULL) { + syslog(LOG_CRIT, "[GPIOD_INTERFACE]: Failed to allocate memory for internal member"); + return NULL; + } + + gpio_group[chip_id].gpio_lines[line_in_group] = line_offset; + gpio_group[chip_id].num_gpio_lines++; + } + + /* Initialize rw_values for read / write multiple functions. + * Also, allocate memory for inverse map: */ + for (int i = 0; i < dev->num_chips; ++i) { + gpio_group[i].rw_values = calloc(gpio_group[i].num_gpio_lines, sizeof(unsigned char)); + if (gpio_group[i].rw_values == NULL) { + syslog(LOG_CRIT, "[GPIOD_INTERFACE]: Failed to allocate memory for internal member"); + return NULL; + } + + gpio_group[i].gpio_group_to_pins_table = calloc(gpio_group[i].num_gpio_lines, sizeof(int)); + if (gpio_group[i].gpio_group_to_pins_table == NULL) { + syslog(LOG_CRIT, "[GPIOD_INTERFACE]: Failed to allocate memory for internal member"); + return NULL; + } + + /* Set event handle arrays for all lines contained on a chip to NULL. */ + gpio_group[i].event_handles = NULL; + } + + /* Finally map the inverse relation between a gpio group and its original pin numbers + * provided by user. */ + int* counters = calloc(dev->num_chips, sizeof(int)); + if (counters == NULL) { + syslog(LOG_CRIT, "[GPIOD_INTERFACE]: Failed to allocate memory for local variable"); + return NULL; + } + + for (int i = 0; i < num_pins; ++i) { + int chip = dev->pin_to_gpio_table[i]; + gpio_group[chip].gpio_group_to_pins_table[counters[chip]] = i; + counters[chip]++; + } + free(counters); + + dev->gpio_group = gpio_group; + + /* Save the provided array from the user to our internal structure. */ + dev->provided_pins = malloc(dev->num_pins * sizeof(int)); + if (dev->provided_pins == NULL) { + syslog(LOG_CRIT, "[GPIOD_INTERFACE]: Failed to allocate memory for internal member"); + return NULL; + } + + memcpy(dev->provided_pins, pins, dev->num_pins * sizeof(int)); + + /* Initialize events array. */ + dev->events = NULL; + + return dev; +} + +mraa_gpio_context +mraa_gpio_init_multi(int pins[], int num_pins) +{ + mraa_board_t* board = plat; + if (board == NULL) { syslog(LOG_ERR, "[GPIOD_INTERFACE]: init: platform not initialised"); return NULL; } - if (board->chardev_capable) { + if (board->chardev_capable) + return mraa_gpio_chardev_init(pins, num_pins); - dev = (mraa_gpio_context) calloc(1, sizeof(struct _gpio)); - if (dev == NULL) { - syslog(LOG_CRIT, "[GPIOD_INTERFACE]: Failed to allocate memory for context"); - return NULL; + /* Fallback to legacy interface. */ + mraa_gpio_context head = NULL, current, tmp; + + for (int i = 0; i < num_pins; ++i) { + tmp = mraa_gpio_init(pins[i]); + if (!tmp) { + syslog(LOG_ERR, "mraa_gpio_init_multi() error initializing pin %i", pins[i]); + continue; } - dev->pin_to_gpio_table = malloc(num_pins * sizeof(int)); - - dev->num_chips = mraa_get_number_of_gpio_chips(); - if (dev->num_chips <= 0) { - free(dev); - return NULL; + if (head == NULL) { + head = tmp; + current = tmp; + } else { + current->next = tmp; + current = tmp; } - dev->num_pins = num_pins; - - gpio_group = calloc(dev->num_chips, sizeof(struct _gpio_group)); - for (int i = 0; i < dev->num_chips; ++i) { - gpio_group[i].gpio_chip = i; - /* Just to be sure realloc has the desired behaviour. */ - gpio_group[i].gpio_lines = NULL; - } - - for (int i = 0; i < num_pins; ++i) { - if (mraa_is_sub_platform_id(pins[i])) { - syslog(LOG_NOTICE, "[GPIOD_INTERFACE]: init: Using sub platform for %d", pins[i]); - board = board->sub_platform; - if (board == NULL) { - syslog(LOG_ERR, "[GPIOD_INTERFACE]: init: Sub platform not initialised for pin %d", - pins[i]); - return NULL; - } - pins[i] = mraa_get_sub_platform_index(pins[i]); - } - - if (pins[i] < 0 || pins[i] >= board->phy_pin_count) { - syslog(LOG_ERR, "[GPIOD_INTERFACE]: init: pin %d beyond platform pin count (%d)", - pins[i], board->phy_pin_count); - return NULL; - } - - if (board->pins[pins[i]].capabilities.gpio != 1) { - syslog(LOG_ERR, "[GPIOD_INTERFACE]: init: pin %d not capable of gpio", pins[i]); - return NULL; - } - - if (board->pins[pins[i]].gpio.mux_total > 0) { - if (mraa_setup_mux_mapped(board->pins[pins[i]].gpio) != MRAA_SUCCESS) { - syslog(LOG_ERR, "[GPIOD_INTERFACE]: init: unable to setup muxes for pin %d", pins[i]); - return NULL; - } - } - - chip_id = board->pins[pins[i]].gpio.gpio_chip; - line_offset = board->pins[pins[i]].gpio.gpio_line; - - /* Map pin to _gpio_group structure. */ - dev->pin_to_gpio_table[i] = chip_id; - - if (!gpio_group[chip_id].is_required) { - mraa_gpiod_chip_info* cinfo = mraa_get_chip_info_by_number(chip_id); - if (!cinfo) { - syslog(LOG_ERR, "[GPIOD_INTERFACE]: error getting gpio_chip_info for chip %d", chip_id); - _mraa_free_gpio_groups(dev); - free(dev); - return NULL; - } - - gpio_group[chip_id].dev_fd = cinfo->chip_fd; - gpio_group[chip_id].is_required = 1; - gpio_group[chip_id].gpiod_handle = -1; - - free(cinfo); - } - - int line_in_group; - line_in_group = gpio_group[chip_id].num_gpio_lines; - gpio_group[chip_id].gpio_lines = - realloc(gpio_group[chip_id].gpio_lines, (line_in_group + 1) * sizeof(unsigned int)); - - gpio_group[chip_id].gpio_lines[line_in_group] = line_offset; - gpio_group[chip_id].num_gpio_lines++; - } - - /* Initialize rw_values for read / write multiple functions. - * Also, allocate memory for inverse map: */ - for (int i = 0; i < dev->num_chips; ++i) { - gpio_group[i].rw_values = calloc(gpio_group[i].num_gpio_lines, sizeof(unsigned char)); - gpio_group[i].gpio_group_to_pins_table = calloc(gpio_group[i].num_gpio_lines, sizeof(int)); - - /* Set event handle arrays for all lines contained on a chip to NULL. */ - gpio_group[i].event_handles = NULL; - } - - /* Finally map the inverse relation between a gpio group and its original pin numbers - * provided by user. */ - int* counters = calloc(dev->num_chips, sizeof(int)); - for (int i = 0; i < num_pins; ++i) { - int chip = dev->pin_to_gpio_table[i]; - gpio_group[chip].gpio_group_to_pins_table[counters[chip]] = i; - counters[chip]++; - } - free(counters); - - dev->gpio_group = gpio_group; - - return dev; - } else { - mraa_gpio_context head = NULL, current, tmp; - - for (int i = 0; i < num_pins; ++i) { - tmp = mraa_gpio_init(pins[i]); - if (!tmp) { - syslog(LOG_ERR, "mraa_gpio_init_multiple() error initializing pin %i", pins[i]); - continue; - } - - if (head == NULL) { - head = tmp; - current = tmp; - } else { - current->next = tmp; - current = tmp; - } - - current->next = NULL; - } - - return head; + current->next = NULL; } + + head->num_pins = num_pins; + + return head; } mraa_gpio_context @@ -360,72 +417,78 @@ mraa_gpio_init_raw(int pin) return mraa_gpio_init_internal(plat == NULL ? NULL : plat->adv_func, pin); } +mraa_timestamp_t +_mraa_gpio_get_timestamp_sysfs() +{ + struct timeval time; + gettimeofday(&time, NULL); + + return (time.tv_sec * 1e6 + time.tv_usec); +} static mraa_result_t -mraa_gpio_wait_interrupt(int fd +mraa_gpio_wait_interrupt(int fds[], + int num_fds #ifndef HAVE_PTHREAD_CANCEL , int control_fd #endif + , + mraa_gpio_events_t events ) { unsigned char c; #ifdef HAVE_PTHREAD_CANCEL - struct pollfd pfd[1]; + struct pollfd pfd[num_fds]; #else - struct pollfd pfd[2]; + struct pollfd pfd[num_fds + 1]; if (control_fd < 0) { return MRAA_ERROR_INVALID_PARAMETER; } #endif - if (fd < 0) { + if (!fds) { return MRAA_ERROR_INVALID_PARAMETER; } - // setup poll on POLLPRI - pfd[0].fd = fd; - if (plat->chardev_capable) { - pfd[0].events = POLLIN; - } else { - pfd[0].events = POLLPRI; + for (int i = 0; i < num_fds; ++i) { + pfd[i].fd = fds[i]; + // setup poll on POLLPRI + pfd[i].events = POLLPRI; + + // do an initial read to clear interrupt + lseek(fds[i], 0, SEEK_SET); + read(fds[i], &c, 1); } - - // do an initial read to clear interrupt - lseek(fd, 0, SEEK_SET); - read(fd, &c, 1); - #ifdef HAVE_PTHREAD_CANCEL // Wait for it forever or until pthread_cancel // poll is a cancelable point like sleep() - poll(pfd, 1, -1); + poll(pfd, num_fds, -1); #else // setup poll on the controling fd - pfd[1].fd = control_fd; - pfd[1].events = 0; // POLLHUP, POLLERR, and POLLNVAL + pfd[num_fds].fd = control_fd; + pfd[num_fds].events = 0; // POLLHUP, POLLERR, and POLLNVAL // Wait for it forever or until control fd is closed - poll(pfd, 2, -1); + poll(pfd, num_fds + 1, -1); #endif - if (plat->chardev_capable) { - if (pfd[0].revents & POLLIN) { - struct gpioevent_data event_data; - // do a final read to clear interrupt - read(fd, &event_data, sizeof(event_data)); - } - } else { - // do a final read to clear interrupt - read(fd, &c, 1); + for (int i = 0; i < num_fds; ++i) { + if (pfd[i].revents & POLLPRI) { + read(fds[i], &c, 1); + events[i].id = i; + events[i].timestamp = _mraa_gpio_get_timestamp_sysfs(); + } else + events[i].id = -1; } return MRAA_SUCCESS; } static mraa_result_t -mraa_gpio_wait_interrupt_multiple(int fds[], int num_fds) +mraa_gpio_chardev_wait_interrupt(int fds[], int num_fds, mraa_gpio_events_t events) { struct pollfd pfd[num_fds]; struct gpioevent_data event_data; @@ -439,8 +502,6 @@ mraa_gpio_wait_interrupt_multiple(int fds[], int num_fds) pfd[i].events = POLLIN; lseek(fds[i], 0, SEEK_SET); - - read(fds[i], &event_data, sizeof(event_data)); } poll(pfd, num_fds, -1); @@ -448,12 +509,54 @@ mraa_gpio_wait_interrupt_multiple(int fds[], int num_fds) for (int i = 0; i < num_fds; ++i) { if (pfd[i].revents & POLLIN) { read(fds[i], &event_data, sizeof(event_data)); - } + events[i].id = i; + events[i].timestamp = event_data.timestamp; + } else + events[i].id = -1; } return MRAA_SUCCESS; } +mraa_gpio_events_t +mraa_gpio_get_events(mraa_gpio_context dev) +{ + if (dev == NULL) { + syslog(LOG_ERR, "gpio: mraa_gpio_get_events(): context is invalid"); + return NULL; + } + + unsigned int event_idx = 0; + + if (plat->chardev_capable) { + unsigned int pin_idx; + mraa_gpiod_group_t gpio_iter; + + for_each_gpio_group(gpio_iter, dev) { + for (int i = 0; i < gpio_iter->num_gpio_lines; ++i) { + if (dev->events[event_idx].id != -1) { + pin_idx = gpio_iter->gpio_group_to_pins_table[i]; + dev->events[event_idx].id = dev->provided_pins[pin_idx]; + } + event_idx++; + } + } + } else { + mraa_gpio_context it = dev; + + while (it) { + if (dev->events[event_idx].id != -1) { + dev->events[event_idx].id = it->phy_pin; + } + + event_idx++; + it = it->next; + } + } + + return dev->events; +} + static void* mraa_gpio_interrupt_handler(void* arg) { @@ -462,26 +565,45 @@ mraa_gpio_interrupt_handler(void* arg) return NULL; } - mraa_gpio_context dev = (mraa_gpio_context) arg; - int fp; mraa_result_t ret; + mraa_gpio_context dev = (mraa_gpio_context) arg; + int idx = 0; if (IS_FUNC_DEFINED(dev, gpio_interrupt_handler_init_replace)) { if (dev->advance_func->gpio_interrupt_handler_init_replace(dev) != MRAA_SUCCESS) return NULL; + } + + int *fps = malloc(dev->num_pins * sizeof(int)); + if (!fps) { + syslog(LOG_ERR, "mraa_gpio_interrupt_handler_multiple() malloc error"); + return NULL; + } + + if (plat->chardev_capable) { + mraa_gpiod_group_t gpio_group; + + for_each_gpio_group(gpio_group, dev) { + for (int i = 0; i < gpio_group->num_gpio_lines; ++i) { + fps[idx++] = gpio_group->event_handles[i]; + } + } } else { - if (plat->chardev_capable) { - fp = dev->gpiod_handle; - } else { + mraa_gpio_context it = dev; + + while (it) { // open gpio value with open(3) char bu[MAX_SIZE]; - snprintf(bu, MAX_SIZE, SYSFS_CLASS_GPIO "/gpio%d/value", dev->pin); - fp = open(bu, O_RDONLY); - if (fp < 0) { - syslog(LOG_ERR, "gpio%i: interrupt_handler: failed to open 'value' : %s", dev->pin, + snprintf(bu, MAX_SIZE, SYSFS_CLASS_GPIO "/gpio%d/value", it->pin); + fps[idx] = open(bu, O_RDONLY); + if (fps[idx] < 0) { + syslog(LOG_ERR, "gpio%i: interrupt_handler: failed to open 'value' : %s", it->pin, strerror(errno)); return NULL; } + + idx++; + it = it->next; } } @@ -489,18 +611,15 @@ mraa_gpio_interrupt_handler(void* arg) if (pipe(dev->isr_control_pipe)) { syslog(LOG_ERR, "gpio%i: interrupt_handler: failed to create isr control pipe: %s", dev->pin, strerror(errno)); - close(fp); + mraa_gpio_close_event_handles_sysfs(fps, dev->num_pins); return NULL; } #endif - dev->isr_value_fp = fp; - if (lang_func->java_attach_thread != NULL) { if (dev->isr == lang_func->java_isr_callback) { if (lang_func->java_attach_thread() != MRAA_SUCCESS) { - close(dev->isr_value_fp); - dev->isr_value_fp = -1; + mraa_gpio_close_event_handles_sysfs(fps, dev->num_pins); return NULL; } } @@ -510,13 +629,20 @@ mraa_gpio_interrupt_handler(void* arg) if (IS_FUNC_DEFINED(dev, gpio_wait_interrupt_replace)) { ret = dev->advance_func->gpio_wait_interrupt_replace(dev); } else { - ret = mraa_gpio_wait_interrupt(dev->isr_value_fp + if (plat->chardev_capable) { + ret = mraa_gpio_chardev_wait_interrupt(fps, idx, dev->events); + } else { + ret = mraa_gpio_wait_interrupt(fps, idx #ifndef HAVE_PTHREAD_CANCEL - , - dev->isr_control_pipe[0] + , + dev->isr_control_pipe[0] #endif - ); + , + dev->events + ); + } } + if (ret == MRAA_SUCCESS && !dev->isr_thread_terminating) { #ifdef HAVE_PTHREAD_CANCEL pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); @@ -534,9 +660,11 @@ mraa_gpio_interrupt_handler(void* arg) #ifdef HAVE_PTHREAD_CANCEL pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); #endif - if (fp != -1) { - close(dev->isr_value_fp); - dev->isr_value_fp = -1; + if (fps) { + for (int i = 0; i < idx; ++i) { + close(fps[i]); + } + free(fps); } if (lang_func->java_detach_thread != NULL && lang_func->java_delete_global_ref != NULL) { @@ -551,58 +679,68 @@ mraa_gpio_interrupt_handler(void* arg) } } -/* In it's simplest form, for now. */ -static void* -mraa_gpio_interrupt_handler_multiple(void* arg) +mraa_result_t +mraa_gpio_chardev_edge_mode(mraa_gpio_context dev, mraa_gpio_edge_t mode) { - if (arg == NULL) { - syslog(LOG_ERR, "gpio: interrupt_handler: context is invalid"); - return NULL; + if (dev == NULL) { + syslog(LOG_ERR, "gpio: edge_mode: context is invalid"); + return MRAA_ERROR_INVALID_HANDLE; } - mraa_gpio_context dev = (mraa_gpio_context) arg; - mraa_gpiod_group_t gpio_group; - mraa_result_t ret; - int idx = 0; + if (!plat->chardev_capable) { + syslog(LOG_ERR, "mraa_gpio_chardev_edge_mode() not supported for old sysfs interface"); + return MRAA_ERROR_FEATURE_NOT_IMPLEMENTED; + } - int* fps = malloc(dev->num_pins * sizeof(int)); - if (!fps) { - syslog(LOG_ERR, "mraa_gpio_interrupt_handler_multiple() malloc error"); - return NULL; + int status; + mraa_gpiod_group_t gpio_group; + + struct gpioevent_request req; + + switch (mode) { + case MRAA_GPIO_EDGE_BOTH: + req.eventflags = GPIOEVENT_REQUEST_BOTH_EDGES; + break; + case MRAA_GPIO_EDGE_RISING: + req.eventflags = GPIOEVENT_REQUEST_RISING_EDGE; + break; + case MRAA_GPIO_EDGE_FALLING: + req.eventflags = GPIOEVENT_REQUEST_FALLING_EDGE; + break; + /* Chardev interface doesn't handle EDGE_NONE. */ + case MRAA_GPIO_EDGE_NONE: + default: + return MRAA_ERROR_FEATURE_NOT_IMPLEMENTED; } for_each_gpio_group(gpio_group, dev) { - for (int i = 0; i < gpio_group->num_gpio_lines; ++i) { - fps[idx++] = gpio_group->event_handles[i]; + if (gpio_group->gpiod_handle != -1) { + close(gpio_group->gpiod_handle); + gpio_group->gpiod_handle = -1; } - } - for (;;) { - ret = mraa_gpio_wait_interrupt_multiple(fps, idx); + gpio_group->event_handles = malloc(gpio_group->num_gpio_lines * sizeof(int)); + if (!gpio_group->event_handles) { + syslog(LOG_ERR, "mraa_gpio_chardev_edge_mode(): malloc error!"); + return MRAA_ERROR_NO_RESOURCES; + } - if (ret == MRAA_SUCCESS && !dev->isr_thread_terminating) { -#ifdef HAVE_PTHREAD_CANCEL - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); -#endif - dev->isr(dev->isr_args); -#ifdef HAVE_PTHREAD_CANCEL - pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); -#endif - } else { -#ifdef HAVE_PTHREAD_CANCEL - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); -#endif - if (fps) { - for (int i = 0; i < idx; ++i) { - close(fps[i]); - } - free(fps); + for (int i = 0; i < gpio_group->num_gpio_lines; ++i) { + req.lineoffset = gpio_group->gpio_lines[i]; + req.handleflags = GPIOHANDLE_REQUEST_INPUT; + + status = _mraa_gpiod_ioctl(gpio_group->dev_fd, GPIO_GET_LINEEVENT_IOCTL, &req); + if (status < 0) { + syslog(LOG_ERR, "error getting line event handle for line %i", gpio_group->gpio_lines[i]); + return MRAA_ERROR_INVALID_RESOURCE; } - return NULL; + gpio_group->event_handles[i] = req.fd; } } + + return MRAA_SUCCESS; } mraa_result_t @@ -616,53 +754,37 @@ mraa_gpio_edge_mode(mraa_gpio_context dev, mraa_gpio_edge_t mode) if (IS_FUNC_DEFINED(dev, gpio_edge_mode_replace)) return dev->advance_func->gpio_edge_mode_replace(dev, mode); - if (plat->chardev_capable) { - int status; - - if (dev->gpiod_handle != -1) { - close(dev->gpiod_handle); - dev->gpiod_handle = -1; + /* Initialize events array. */ + if (dev->events == NULL && mode != MRAA_GPIO_EDGE_NONE) { + dev->events = malloc(dev->num_pins * sizeof (mraa_gpio_event)); + if (dev->events == NULL) { + syslog(LOG_ERR, "mraa_gpio_edge_mode() malloc error"); + return MRAA_ERROR_NO_RESOURCES; } - struct gpioevent_request req; - - switch (mode) { - case MRAA_GPIO_EDGE_NONE: - return MRAA_SUCCESS; - case MRAA_GPIO_EDGE_BOTH: - req.eventflags = GPIOEVENT_REQUEST_BOTH_EDGES; - break; - case MRAA_GPIO_EDGE_RISING: - req.eventflags = GPIOEVENT_REQUEST_RISING_EDGE; - break; - case MRAA_GPIO_EDGE_FALLING: - req.eventflags = GPIOEVENT_REQUEST_FALLING_EDGE; - break; - default: - return MRAA_ERROR_FEATURE_NOT_IMPLEMENTED; + for (int i = 0; i < dev->num_pins; ++i) { + dev->events[i].id = -1; } + } + if (plat->chardev_capable) + return mraa_gpio_chardev_edge_mode(dev, mode); - req.lineoffset = dev->gpio_line; - req.handleflags = GPIOHANDLE_REQUEST_INPUT; - status = _mraa_gpiod_ioctl(dev->dev_fd, GPIO_GET_LINEEVENT_IOCTL, &req); - if (status < 0) { - return MRAA_ERROR_FEATURE_NOT_IMPLEMENTED; - } + mraa_gpio_context it = dev; - dev->gpiod_handle = req.fd; - } else { - if (dev->value_fp != -1) { - close(dev->value_fp); - dev->value_fp = -1; + while (it) { + + if (it->value_fp != -1) { + close(it->value_fp); + it->value_fp = -1; } char filepath[MAX_SIZE]; - snprintf(filepath, MAX_SIZE, SYSFS_CLASS_GPIO "/gpio%d/edge", dev->pin); + snprintf(filepath, MAX_SIZE, SYSFS_CLASS_GPIO "/gpio%d/edge", it->pin); int edge = open(filepath, O_RDWR); if (edge == -1) { - syslog(LOG_ERR, "gpio%i: edge_mode: Failed to open 'edge' for writing: %s", dev->pin, + syslog(LOG_ERR, "gpio%i: edge_mode: Failed to open 'edge' for writing: %s", it->pin, strerror(errno)); return MRAA_ERROR_INVALID_RESOURCE; } @@ -687,76 +809,14 @@ mraa_gpio_edge_mode(mraa_gpio_context dev, mraa_gpio_edge_t mode) return MRAA_ERROR_FEATURE_NOT_IMPLEMENTED; } if (write(edge, bu, length * sizeof(char)) == -1) { - syslog(LOG_ERR, "gpio%i: edge_mode: Failed to write to 'edge': %s", dev->pin, strerror(errno)); + syslog(LOG_ERR, "gpio%i: edge_mode: Failed to write to 'edge': %s", it->pin, strerror(errno)); close(edge); return MRAA_ERROR_UNSPECIFIED; } close(edge); - } - return MRAA_SUCCESS; -} - -mraa_result_t -mraa_gpio_edge_mode_multiple(mraa_gpio_context dev, mraa_gpio_edge_t mode) -{ - if (dev == NULL) { - syslog(LOG_ERR, "gpio: edge_mode: context is invalid"); - return MRAA_ERROR_INVALID_HANDLE; - } - - if (!plat->chardev_capable) { - syslog(LOG_ERR, "mraa_gpio_isr_multiple() not supported for old sysfs interface"); - return MRAA_ERROR_FEATURE_NOT_IMPLEMENTED; - } - - int status; - mraa_gpiod_group_t gpio_group; - - struct gpioevent_request req; - - switch (mode) { - case MRAA_GPIO_EDGE_NONE: - return MRAA_SUCCESS; - case MRAA_GPIO_EDGE_BOTH: - req.eventflags = GPIOEVENT_REQUEST_BOTH_EDGES; - break; - case MRAA_GPIO_EDGE_RISING: - req.eventflags = GPIOEVENT_REQUEST_RISING_EDGE; - break; - case MRAA_GPIO_EDGE_FALLING: - req.eventflags = GPIOEVENT_REQUEST_FALLING_EDGE; - break; - default: - return MRAA_ERROR_FEATURE_NOT_IMPLEMENTED; - } - - for_each_gpio_group(gpio_group, dev) - { - if (gpio_group->gpiod_handle != -1) { - close(gpio_group->gpiod_handle); - gpio_group->gpiod_handle = -1; - } - - gpio_group->event_handles = malloc(gpio_group->num_gpio_lines * sizeof(int)); - if (!gpio_group->event_handles) { - syslog(LOG_ERR, "mraa_gpio_edge_mode_multiple(): malloc error!"); - return MRAA_ERROR_NO_RESOURCES; - } - - for (int i = 0; i < gpio_group->num_gpio_lines; ++i) { - req.lineoffset = gpio_group->gpio_lines[i]; - req.handleflags = GPIOHANDLE_REQUEST_INPUT; - - status = _mraa_gpiod_ioctl(gpio_group->dev_fd, GPIO_GET_LINEEVENT_IOCTL, &req); - if (status < 0) { - syslog(LOG_ERR, "error getting line event handle for line %i", gpio_group->gpio_lines[i]); - return MRAA_ERROR_INVALID_RESOURCE; - } - - gpio_group->event_handles[i] = req.fd; - } + it = it->next; } return MRAA_SUCCESS; @@ -779,7 +839,11 @@ mraa_gpio_isr(mraa_gpio_context dev, mraa_gpio_edge_t mode, void (*fptr)(void*), return MRAA_ERROR_NO_RESOURCES; } - mraa_result_t ret = mraa_gpio_edge_mode(dev, mode); + mraa_result_t ret; + + + ret = mraa_gpio_edge_mode(dev, mode); + if (ret != MRAA_SUCCESS) { return ret; } @@ -801,44 +865,6 @@ mraa_gpio_isr(mraa_gpio_context dev, mraa_gpio_edge_t mode, void (*fptr)(void*), return MRAA_SUCCESS; } -mraa_result_t -mraa_gpio_isr_multiple(mraa_gpio_context dev, mraa_gpio_edge_t mode, void (*fptr)(void*), void* args) -{ - if (dev == NULL) { - syslog(LOG_ERR, "gpio: isr: context is invalid"); - return MRAA_ERROR_INVALID_HANDLE; - } - - // we only allow one isr per mraa_gpio_context - if (dev->thread_id != 0) { - return MRAA_ERROR_NO_RESOURCES; - } - - // TODO: Check that this function is applied only when multiple lines are given as input in the - // init phase. - - mraa_result_t ret = mraa_gpio_edge_mode_multiple(dev, mode); - if (ret != MRAA_SUCCESS) { - return ret; - } - - dev->isr = fptr; - - /* Most UPM sensors use the C API, the Java global ref must be created here. */ - /* The reason for checking the callback function is internal callbacks. */ - if (lang_func->java_create_global_ref != NULL) { - if (dev->isr == lang_func->java_isr_callback) { - args = lang_func->java_create_global_ref(args); - } - } - - dev->isr_args = args; - - pthread_create(&dev->thread_id, NULL, mraa_gpio_interrupt_handler_multiple, (void*) dev); - - return MRAA_SUCCESS; -} - mraa_result_t mraa_gpio_isr_exit(mraa_gpio_context dev) { @@ -853,14 +879,17 @@ mraa_gpio_isr_exit(mraa_gpio_context dev) } // wasting our time, there is no isr to exit from - if (dev->thread_id == 0 && dev->isr_value_fp == -1) { + if (dev->thread_id == 0) { return ret; } // mark the beginning of the thread termination process for interested parties dev->isr_thread_terminating = 1; // stop isr being useful - ret = mraa_gpio_edge_mode(dev, MRAA_GPIO_EDGE_NONE); + if (plat->chardev_capable) + _mraa_close_gpio_event_handles(dev); + else + ret = mraa_gpio_edge_mode(dev, MRAA_GPIO_EDGE_NONE); if ((dev->thread_id != 0)) { #ifdef HAVE_PTHREAD_CANCEL @@ -877,23 +906,14 @@ mraa_gpio_isr_exit(mraa_gpio_context dev) #endif } - // close the filehandle in case it's still open - if (dev->isr_value_fp != -1) { - if (close(dev->isr_value_fp) != 0) { - ret = MRAA_ERROR_INVALID_RESOURCE; - } - } - // assume our thread will exit either way we just lost it's handle dev->thread_id = 0; dev->isr_value_fp = -1; dev->isr_thread_terminating = 0; - if (plat->chardev_capable) { - if (dev->gpiod_handle != -1) { - close(dev->gpiod_handle); - dev->gpiod_handle = -1; - } + if (dev->events) { + free(dev->events); + dev->events = NULL; } return ret; @@ -909,19 +929,24 @@ mraa_gpio_mode(mraa_gpio_context dev, mraa_gpio_mode_t mode) if (plat->chardev_capable) { unsigned flags = 0; int line_handle; + mraa_gpiod_group_t gpio_iter; - if (dev->gpiod_handle != -1) { - close(dev->gpiod_handle); - dev->gpiod_handle = -1; - } + _mraa_close_gpio_desc(dev); - mraa_gpiod_line_info* linfo = mraa_get_line_info_by_chip_number(dev->gpio_chip, dev->gpio_line); - if (!linfo) { - syslog(LOG_ERR, "[GPIOD_INTERFACE]: error getting line info"); - return MRAA_ERROR_UNSPECIFIED; + + /* We save flag values from the first valid line. */ + for_each_gpio_group(gpio_iter, dev) { + mraa_gpiod_line_info* linfo = mraa_get_line_info_by_chip_number(gpio_iter->gpio_chip, gpio_iter->gpio_lines[0]); + if (!linfo) { + syslog(LOG_ERR, "[GPIOD_INTERFACE]: error getting line info"); + return MRAA_ERROR_UNSPECIFIED; + } + flags = linfo->flags; + free(linfo); + + /* We don't need to iterate any further. */ + break; } - flags = linfo->flags; - free(linfo); /* Without changing the API, for now, we can request only one mode per call. */ switch (mode) { @@ -929,23 +954,24 @@ mraa_gpio_mode(mraa_gpio_context dev, mraa_gpio_mode_t mode) flags |= GPIOHANDLE_REQUEST_ACTIVE_LOW; break; case MRAA_GPIOD_OPEN_DRAIN: - flags = GPIOHANDLE_REQUEST_OPEN_DRAIN; + flags |= GPIOHANDLE_REQUEST_OPEN_DRAIN; break; case MRAA_GPIOD_OPEN_SOURCE: - flags = GPIOHANDLE_REQUEST_OPEN_SOURCE; + flags |= GPIOHANDLE_REQUEST_OPEN_SOURCE; break; default: return MRAA_ERROR_FEATURE_NOT_IMPLEMENTED; } - unsigned int line_offsets[1] = { dev->gpio_line }; - line_handle = mraa_get_lines_handle(dev->dev_fd, line_offsets, 1, flags, 0); - if (line_handle <= 0) { - syslog(LOG_ERR, "[GPIOD_INTERFACE]: error getting line handle"); - return MRAA_ERROR_INVALID_RESOURCE; - } + for_each_gpio_group(gpio_iter, dev) { + line_handle = mraa_get_lines_handle(gpio_iter->dev_fd, gpio_iter->gpio_lines, gpio_iter->num_gpio_lines, flags, 0); + if (line_handle <= 0) { + syslog(LOG_ERR, "[GPIOD_INTERFACE]: error getting line handle"); + return MRAA_ERROR_INVALID_RESOURCE; + } - dev->gpiod_handle = line_handle; + gpio_iter->gpiod_handle = line_handle; + } } else { if (IS_FUNC_DEFINED(dev, gpio_mode_replace)) return dev->advance_func->gpio_mode_replace(dev, mode); @@ -1004,23 +1030,14 @@ mraa_gpio_mode(mraa_gpio_context dev, mraa_gpio_mode_t mode) } mraa_result_t -mraa_gpio_dir(mraa_gpio_context dev, mraa_gpio_dir_t dir) +mraa_gpio_chardev_dir(mraa_gpio_context dev, mraa_gpio_dir_t dir) { - if (dev == NULL) { - syslog(LOG_ERR, "gpio: dir: context is invalid"); - return MRAA_ERROR_INVALID_HANDLE; - } + int line_handle; + unsigned flags = 0; + mraa_gpiod_group_t gpio_iter; - if (plat->chardev_capable) { - int line_handle; - unsigned flags = 0; - - if (dev->gpiod_handle != -1) { - close(dev->gpiod_handle); - dev->gpiod_handle = -1; - } - - mraa_gpiod_line_info* linfo = mraa_get_line_info_by_chip_number(dev->gpio_chip, dev->gpio_line); + for_each_gpio_group(gpio_iter, dev) { + mraa_gpiod_line_info* linfo = mraa_get_line_info_by_chip_number(gpio_iter->gpio_chip, gpio_iter->gpio_lines[0]); if (!linfo) { syslog(LOG_ERR, "[GPIOD_INTERFACE]: error getting line info"); return MRAA_ERROR_UNSPECIFIED; @@ -1028,45 +1045,74 @@ mraa_gpio_dir(mraa_gpio_context dev, mraa_gpio_dir_t dir) flags = linfo->flags; free(linfo); - switch (dir) { - case MRAA_GPIO_OUT: - flags |= GPIOHANDLE_REQUEST_OUTPUT; - flags &= ~GPIOHANDLE_REQUEST_INPUT; - break; - case MRAA_GPIO_IN: - flags |= GPIOHANDLE_REQUEST_INPUT; - flags &= ~GPIOHANDLE_REQUEST_OUTPUT; - break; - default: - return MRAA_ERROR_FEATURE_NOT_IMPLEMENTED; + /* We don't need to iterate any further. */ + break; + } + + switch (dir) { + case MRAA_GPIO_OUT: + flags |= GPIOHANDLE_REQUEST_OUTPUT; + flags &= ~GPIOHANDLE_REQUEST_INPUT; + break; + case MRAA_GPIO_IN: + flags |= GPIOHANDLE_REQUEST_INPUT; + flags &= ~GPIOHANDLE_REQUEST_OUTPUT; + break; + default: + return MRAA_ERROR_FEATURE_NOT_IMPLEMENTED; + } + + for_each_gpio_group(gpio_iter, dev) { + if (gpio_iter->gpiod_handle != -1) { + close(gpio_iter->gpiod_handle); + gpio_iter->gpiod_handle = -1; } - unsigned int line_offsets[1] = { dev->gpio_line }; - line_handle = mraa_get_lines_handle(dev->dev_fd, line_offsets, 1, flags, 0); + line_handle = mraa_get_lines_handle(gpio_iter->dev_fd, gpio_iter->gpio_lines, + gpio_iter->num_gpio_lines, flags, 0); if (line_handle <= 0) { syslog(LOG_ERR, "[GPIOD_INTERFACE]: error getting line handle"); return MRAA_ERROR_INVALID_RESOURCE; } - dev->gpiod_handle = line_handle; - } else { - if (IS_FUNC_DEFINED(dev, gpio_dir_replace)) { - return dev->advance_func->gpio_dir_replace(dev, dir); + gpio_iter->gpiod_handle = line_handle; + } + + return MRAA_SUCCESS; +} + +mraa_result_t +mraa_gpio_dir(mraa_gpio_context dev, mraa_gpio_dir_t dir) +{ + if (dev == NULL) { + syslog(LOG_ERR, "gpio: dir: context is invalid"); + return MRAA_ERROR_INVALID_HANDLE; + } + + if (plat->chardev_capable) + return mraa_gpio_chardev_dir(dev, dir); + + mraa_gpio_context it = dev; + + while (it) { + + if (IS_FUNC_DEFINED(it, gpio_dir_replace)) { + return it->advance_func->gpio_dir_replace(it, dir); } - if (IS_FUNC_DEFINED(dev, gpio_dir_pre)) { - mraa_result_t pre_ret = (dev->advance_func->gpio_dir_pre(dev, dir)); + if (IS_FUNC_DEFINED(it, gpio_dir_pre)) { + mraa_result_t pre_ret = (it->advance_func->gpio_dir_pre(it, dir)); if (pre_ret != MRAA_SUCCESS) { return pre_ret; } } - if (dev->value_fp != -1) { - close(dev->value_fp); - dev->value_fp = -1; + if (it->value_fp != -1) { + close(it->value_fp); + it->value_fp = -1; } char filepath[MAX_SIZE]; - snprintf(filepath, MAX_SIZE, SYSFS_CLASS_GPIO "/gpio%d/direction", dev->pin); + snprintf(filepath, MAX_SIZE, SYSFS_CLASS_GPIO "/gpio%d/direction", it->pin); int direction = open(filepath, O_RDWR); @@ -1075,12 +1121,12 @@ mraa_gpio_dir(mraa_gpio_context dev, mraa_gpio_dir_t dir) // If not fail as usual. switch (dir) { case MRAA_GPIO_OUT_HIGH: - return mraa_gpio_write(dev, 1); + return mraa_gpio_write(it, 1); case MRAA_GPIO_OUT_LOW: - return mraa_gpio_write(dev, 0); + return mraa_gpio_write(it, 0); default: syslog(LOG_ERR, "gpio%i: dir: Failed to open 'direction' for writing: %s", - dev->pin, strerror(errno)); + it->pin, strerror(errno)); return MRAA_ERROR_INVALID_RESOURCE; } } @@ -1107,96 +1153,50 @@ mraa_gpio_dir(mraa_gpio_context dev, mraa_gpio_dir_t dir) if (write(direction, bu, length * sizeof(char)) == -1) { close(direction); - syslog(LOG_ERR, "gpio%i: dir: Failed to write to 'direction': %s", dev->pin, strerror(errno)); + syslog(LOG_ERR, "gpio%i: dir: Failed to write to 'direction': %s", it->pin, strerror(errno)); return MRAA_ERROR_UNSPECIFIED; } close(direction); - if (IS_FUNC_DEFINED(dev, gpio_dir_post)) - return dev->advance_func->gpio_dir_post(dev, dir); + if (IS_FUNC_DEFINED(it, gpio_dir_post)) + return it->advance_func->gpio_dir_post(it, dir); + + it = it->next; } return MRAA_SUCCESS; } mraa_result_t -mraa_gpio_dir_multiple(mraa_gpio_context dev, mraa_gpio_dir_t dir) -{ - if (plat->chardev_capable) { - mraa_gpiod_group_t gpio_group; - - for (int i = 0; i < dev->num_chips; ++i) { - - gpio_group = &dev->gpio_group[i]; - if (!gpio_group->is_required) { - continue; - } - - int line_handle; - unsigned flags = 0; - - if (gpio_group->gpiod_handle != -1) { - close(gpio_group->gpiod_handle); - gpio_group->gpiod_handle = -1; - } - - /* TODO: we can't actually get past line(s) info and meld them all in one request. */ - /*mraa_gpiod_line_info *linfo = mraa_get_line_info_by_chip_number(gpio_group->gpio_chip, - dev->gpio_line); if (!linfo) { syslog(LOG_ERR, "[GPIOD_INTERFACE]: error getting line - info"); return MRAA_ERROR_UNSPECIFIED; - } - flags |= linfo->flags; - free(linfo);*/ - - switch (dir) { - case MRAA_GPIO_OUT: - flags |= GPIOHANDLE_REQUEST_OUTPUT; - flags &= ~GPIOHANDLE_REQUEST_INPUT; - break; - case MRAA_GPIO_IN: - flags |= GPIOHANDLE_REQUEST_INPUT; - flags &= ~GPIOHANDLE_REQUEST_OUTPUT; - break; - default: - return MRAA_ERROR_FEATURE_NOT_IMPLEMENTED; - } - - line_handle = mraa_get_lines_handle(gpio_group->dev_fd, gpio_group->gpio_lines, - gpio_group->num_gpio_lines, flags, 0); - if (line_handle <= 0) { - syslog(LOG_ERR, "[GPIOD_INTERFACE]: error getting line handle"); - return MRAA_ERROR_INVALID_RESOURCE; - } - - gpio_group->gpiod_handle = line_handle; - } - } else { - mraa_gpio_context it = dev; - - while (it) { - mraa_gpio_dir(it, dir); - it = it->next; - } - } - - return MRAA_SUCCESS; -} - -mraa_result_t -mraa_gpio_read_dir(mraa_gpio_context dev, mraa_gpio_dir_t* dir) +mraa_gpio_read_dir(mraa_gpio_context dev, mraa_gpio_dir_t *dir) { mraa_result_t result = MRAA_SUCCESS; + /* Initialize with 'unusable'. */ + unsigned flags = GPIOLINE_FLAG_KERNEL; + if (plat->chardev_capable) { - mraa_gpiod_line_info* linfo = mraa_get_line_info_by_chip_number(dev->gpio_chip, dev->gpio_line); - if (!linfo) { - syslog(LOG_ERR, "[GPIOD_INTERFACE]: error error getting line info"); + mraa_gpiod_group_t gpio_iter; + + for_each_gpio_group(gpio_iter, dev) { + mraa_gpiod_line_info* linfo = mraa_get_line_info_by_chip_number(gpio_iter->gpio_chip, gpio_iter->gpio_lines[0]); + if (!linfo) { + syslog(LOG_ERR, "[GPIOD_INTERFACE]: error getting line info"); + return MRAA_ERROR_UNSPECIFIED; + } + flags = linfo->flags; + free(linfo); + + /* We don't need to iterate further. */ + break; + } + + if (flags & GPIOLINE_FLAG_KERNEL) { + syslog(LOG_ERR, "[GPIOD_INTERFACE]: cannot read gpio direction. Line used by kernel."); return MRAA_ERROR_UNSPECIFIED; } - /* This is overkill for now, but in the future we can extract much more info from a line - * using the above method. */ - *dir = linfo->flags & GPIOLINE_FLAG_IS_OUT ? MRAA_GPIO_OUT : MRAA_GPIO_IN; - free(linfo); + + *dir = flags & GPIOLINE_FLAG_IS_OUT ? MRAA_GPIO_OUT : MRAA_GPIO_IN; } else { char value[5]; char filepath[MAX_SIZE]; @@ -1254,65 +1254,43 @@ mraa_gpio_read(mraa_gpio_context dev) } if (plat->chardev_capable) { - int status; - unsigned flags = 0; + int output_values[1] = { 0 }; - mraa_gpiod_line_info* linfo = mraa_get_line_info_by_chip_number(dev->gpio_chip, dev->gpio_line); - if (!linfo) { - syslog(LOG_ERR, "[GPIOD_INTERFACE]: error getting line info"); - return MRAA_ERROR_UNSPECIFIED; - } - flags = linfo->flags; - free(linfo); - - flags = (flags | GPIOHANDLE_REQUEST_INPUT) & ~GPIOHANDLE_REQUEST_OUTPUT; - - if (dev->gpiod_handle <= 0) { - unsigned int line_offsets[1] = { dev->gpio_line }; - dev->gpiod_handle = mraa_get_lines_handle(dev->dev_fd, line_offsets, 1, flags, 0); - if (dev->gpiod_handle <= 0) { - syslog(LOG_ERR, "[GPIOD_INTERFACE]: error getting gpio line handle"); + if (mraa_gpio_read_multi(dev, output_values) != MRAA_SUCCESS) return -1; - } - } - unsigned char values[1] = { 0 }; - status = mraa_get_line_values(dev->gpiod_handle, 1, values); - if (status < 0) { - syslog(LOG_ERR, "[GPIOD_INTERFACE]: error reading gpio"); - return -1; - } - - return values[0]; - } else { - if (IS_FUNC_DEFINED(dev, gpio_read_replace)) - return dev->advance_func->gpio_read_replace(dev); - - if (dev->mmap_read != NULL) - return dev->mmap_read(dev); - - if (dev->value_fp == -1) { - if (_mraa_gpio_get_valfp(dev) != MRAA_SUCCESS) { - return -1; - } - } else { - // if value_fp is new this is pointless - lseek(dev->value_fp, 0, SEEK_SET); - } - char bu[2]; - if (read(dev->value_fp, bu, 2 * sizeof(char)) != 2) { - syslog(LOG_ERR, "gpio%i: read: Failed to read a sensible value from sysfs: %s", - dev->pin, strerror(errno)); - return -1; - } - lseek(dev->value_fp, 0, SEEK_SET); - - return (int) strtol(bu, NULL, 10); + return output_values[0]; } + + if (IS_FUNC_DEFINED(dev, gpio_read_replace)) { + return dev->advance_func->gpio_read_replace(dev); + } + + if (dev->mmap_read != NULL) { + return dev->mmap_read(dev); + } + + if (dev->value_fp == -1) { + if (_mraa_gpio_get_valfp(dev) != MRAA_SUCCESS) { + return -1; + } + } else { + // if value_fp is new this is pointless + lseek(dev->value_fp, 0, SEEK_SET); + } + char bu[2]; + if (read(dev->value_fp, bu, 2 * sizeof(char)) != 2) { + syslog(LOG_ERR, "gpio%i: read: Failed to read a sensible value from sysfs: %s", + dev->pin, strerror(errno)); + return -1; + } + lseek(dev->value_fp, 0, SEEK_SET); + + return (int) strtol(bu, NULL, 10); } mraa_result_t -mraa_gpio_read_multiple(mraa_gpio_context dev, int output_values[]) +mraa_gpio_read_multi(mraa_gpio_context dev, int output_values[]) { if (dev == NULL) { syslog(LOG_ERR, "gpio: read multiple: context is invalid"); @@ -1322,37 +1300,32 @@ mraa_gpio_read_multiple(mraa_gpio_context dev, int output_values[]) if (plat->chardev_capable) { memset(output_values, 0, dev->num_pins * sizeof(int)); - mraa_gpiod_group_t gpio_group; - - for (int i = 0; i < dev->num_chips; ++i) { - gpio_group = &dev->gpio_group[i]; - if (!gpio_group->is_required) { - continue; - } + mraa_gpiod_group_t gpio_iter; + for_each_gpio_group(gpio_iter, dev) { int status; unsigned flags = GPIOHANDLE_REQUEST_INPUT; - if (gpio_group->gpiod_handle <= 0) { - gpio_group->gpiod_handle = mraa_get_lines_handle(gpio_group->dev_fd, gpio_group->gpio_lines, - gpio_group->num_gpio_lines, flags, 0); - if (gpio_group->gpiod_handle <= 0) { + if (gpio_iter->gpiod_handle <= 0) { + gpio_iter->gpiod_handle = mraa_get_lines_handle(gpio_iter->dev_fd, gpio_iter->gpio_lines, + gpio_iter->num_gpio_lines, flags, 0); + if (gpio_iter->gpiod_handle <= 0) { syslog(LOG_ERR, "[GPIOD_INTERFACE]: error getting gpio line handle"); return MRAA_ERROR_INVALID_HANDLE; } } - status = mraa_get_line_values(gpio_group->gpiod_handle, gpio_group->num_gpio_lines, - gpio_group->rw_values); + status = mraa_get_line_values(gpio_iter->gpiod_handle, gpio_iter->num_gpio_lines, + gpio_iter->rw_values); if (status < 0) { syslog(LOG_ERR, "[GPIOD_INTERFACE]: error writing gpio"); return MRAA_ERROR_INVALID_RESOURCE; } /* Write values back to the user provided array. */ - for (int j = 0; j < gpio_group->num_gpio_lines; ++j) { + for (int j = 0; j < gpio_iter->num_gpio_lines; ++j) { /* Use the internal reverse mapping table. */ - output_values[gpio_group->gpio_group_to_pins_table[j]] = gpio_group->rw_values[j]; + output_values[gpio_iter->gpio_group_to_pins_table[j]] = gpio_iter->rw_values[j]; } } } else { @@ -1361,10 +1334,12 @@ mraa_gpio_read_multiple(mraa_gpio_context dev, int output_values[]) while (it) { output_values[i] = mraa_gpio_read(it); + if (output_values[i] == -1) { syslog(LOG_ERR, "gpio: read_multiple: failed to read multiple gpio pins"); return MRAA_ERROR_INVALID_RESOURCE; } + i++; it = it->next; } @@ -1382,76 +1357,52 @@ mraa_gpio_write(mraa_gpio_context dev, int value) } if (plat->chardev_capable) { - int status; - unsigned flags = 0; + int input_values[1] = { value }; - mraa_gpiod_line_info* linfo = mraa_get_line_info_by_chip_number(dev->gpio_chip, dev->gpio_line); - if (!linfo) { - syslog(LOG_ERR, "[GPIOD_INTERFACE]: error getting line info"); - return MRAA_ERROR_UNSPECIFIED; - } - flags = linfo->flags; - free(linfo); + return mraa_gpio_write_multi(dev, input_values); + } - flags = (flags | GPIOHANDLE_REQUEST_OUTPUT) & ~GPIOHANDLE_REQUEST_INPUT; + if (dev->mmap_write != NULL) { + return dev->mmap_write(dev, value); + } - if (dev->gpiod_handle <= 0) { - unsigned int line_offsets[1] = { dev->gpio_line }; - dev->gpiod_handle = mraa_get_lines_handle(dev->dev_fd, line_offsets, 1, flags, 0); - if (dev->gpiod_handle <= 0) { - syslog(LOG_ERR, "[GPIOD_INTERFACE]: error getting gpio line handle"); - return -1; - } - } + if (IS_FUNC_DEFINED(dev, gpio_write_pre)) { + mraa_result_t pre_ret = (dev->advance_func->gpio_write_pre(dev, value)); + if (pre_ret != MRAA_SUCCESS) + return pre_ret; + } - unsigned char values[1] = { value }; - status = mraa_set_line_values(dev->gpiod_handle, 1, values); - if (status < 0) { - syslog(LOG_ERR, "[GPIOD_INTERFACE]: error writing gpio"); + if (IS_FUNC_DEFINED(dev, gpio_write_replace)) { + return dev->advance_func->gpio_write_replace(dev, value); + } + + if (dev->value_fp == -1) { + if (_mraa_gpio_get_valfp(dev) != MRAA_SUCCESS) { return MRAA_ERROR_INVALID_RESOURCE; } - } else { - if (dev->mmap_write != NULL) - return dev->mmap_write(dev, value); + } - if (IS_FUNC_DEFINED(dev, gpio_write_pre)) { - mraa_result_t pre_ret = (dev->advance_func->gpio_write_pre(dev, value)); - if (pre_ret != MRAA_SUCCESS) - return pre_ret; - } + if (lseek(dev->value_fp, 0, SEEK_SET) == -1) { + syslog(LOG_ERR, "gpio%i: write: Failed to lseek 'value': %s", dev->pin, strerror(errno)); + return MRAA_ERROR_UNSPECIFIED; + } - if (IS_FUNC_DEFINED(dev, gpio_write_replace)) { - return dev->advance_func->gpio_write_replace(dev, value); - } + char bu[MAX_SIZE]; + int length = snprintf(bu, sizeof(bu), "%d", value); + if (write(dev->value_fp, bu, length * sizeof(char)) == -1) { + syslog(LOG_ERR, "gpio%i: write: Failed to write to 'value': %s", dev->pin, strerror(errno)); + return MRAA_ERROR_UNSPECIFIED; + } - if (dev->value_fp == -1) { - if (_mraa_gpio_get_valfp(dev) != MRAA_SUCCESS) { - return MRAA_ERROR_INVALID_RESOURCE; - } - } - - if (lseek(dev->value_fp, 0, SEEK_SET) == -1) { - syslog(LOG_ERR, "gpio%i: write: Failed to lseek 'value': %s", dev->pin, strerror(errno)); - return MRAA_ERROR_UNSPECIFIED; - } - - char bu[MAX_SIZE]; - int length = snprintf(bu, sizeof(bu), "%d", value); - if (write(dev->value_fp, bu, length * sizeof(char)) == -1) { - syslog(LOG_ERR, "gpio%i: write: Failed to write to 'value': %s", dev->pin, strerror(errno)); - return MRAA_ERROR_UNSPECIFIED; - } - - if (IS_FUNC_DEFINED(dev, gpio_write_post)) { - return dev->advance_func->gpio_write_post(dev, value); - } + if (IS_FUNC_DEFINED(dev, gpio_write_post)) { + return dev->advance_func->gpio_write_post(dev, value); } return MRAA_SUCCESS; } mraa_result_t -mraa_gpio_write_multiple(mraa_gpio_context dev, int input_values[]) +mraa_gpio_write_multi(mraa_gpio_context dev, int input_values[]) { if (dev == NULL) { syslog(LOG_ERR, "gpio: write: context is invalid"); @@ -1459,43 +1410,37 @@ mraa_gpio_write_multiple(mraa_gpio_context dev, int input_values[]) } if (plat->chardev_capable) { - mraa_gpiod_group_t gpio_group; + mraa_gpiod_group_t gpio_iter; - /* First, let's copy input_values into an internal specific gpio_group structure. - We copy just values which have the index same as the pin belonging to the gpio_group - structure. */ - /* TODO: can move counters to internal memory of strucutre, instead of allocating here all - * the time. */ int* counters = calloc(dev->num_chips, sizeof(int)); + if (counters == NULL) { + syslog(LOG_ERR, "mraa_gpio_write_multi() malloc error"); + return MRAA_ERROR_NO_RESOURCES; + } + for (int i = 0; i < dev->num_pins; ++i) { int chip_id = dev->pin_to_gpio_table[i]; - gpio_group = &dev->gpio_group[chip_id]; - - gpio_group->rw_values[counters[chip_id]] = input_values[i]; + gpio_iter = &dev->gpio_group[chip_id]; + gpio_iter->rw_values[counters[chip_id]] = input_values[i]; counters[chip_id]++; } free(counters); - for (int i = 0; i < dev->num_chips; ++i) { - gpio_group = &dev->gpio_group[i]; - if (!gpio_group->is_required) { - continue; - } - + for_each_gpio_group(gpio_iter, dev) { int status; unsigned flags = GPIOHANDLE_REQUEST_OUTPUT; - if (gpio_group->gpiod_handle <= 0) { - gpio_group->gpiod_handle = mraa_get_lines_handle(gpio_group->dev_fd, gpio_group->gpio_lines, - gpio_group->num_gpio_lines, flags, 0); - if (gpio_group->gpiod_handle <= 0) { + if (gpio_iter->gpiod_handle <= 0) { + gpio_iter->gpiod_handle = mraa_get_lines_handle(gpio_iter->dev_fd, gpio_iter->gpio_lines, + gpio_iter->num_gpio_lines, flags, 0); + if (gpio_iter->gpiod_handle <= 0) { syslog(LOG_ERR, "[GPIOD_INTERFACE]: error getting gpio line handle"); return MRAA_ERROR_INVALID_HANDLE; } } - status = mraa_set_line_values(gpio_group->gpiod_handle, gpio_group->num_gpio_lines, - gpio_group->rw_values); + status = mraa_set_line_values(gpio_iter->gpiod_handle, gpio_iter->num_gpio_lines, + gpio_iter->rw_values); if (status < 0) { syslog(LOG_ERR, "[GPIOD_INTERFACE]: error writing gpio"); return MRAA_ERROR_INVALID_RESOURCE; @@ -1540,6 +1485,7 @@ mraa_gpio_unexport_force(mraa_gpio_context dev) mraa_gpio_isr_exit(dev); return MRAA_SUCCESS; } + static mraa_result_t mraa_gpio_unexport(mraa_gpio_context dev) { @@ -1551,6 +1497,7 @@ mraa_gpio_unexport(mraa_gpio_context dev) if (dev->owner) { return mraa_gpio_unexport_force(dev); } + return MRAA_ERROR_INVALID_PARAMETER; } @@ -1577,14 +1524,7 @@ _mraa_gpio_close_internal(mraa_gpio_context dev) close(dev->value_fp); } - if (plat->chardev_capable) { - if (dev->gpiod_handle != -1) { - close(dev->gpiod_handle); - } - close(dev->dev_fd); - } else { - mraa_gpio_unexport(dev); - } + mraa_gpio_unexport(dev); free(dev); @@ -1601,6 +1541,10 @@ mraa_gpio_close(mraa_gpio_context dev) return MRAA_ERROR_INVALID_HANDLE; } + if (dev->events) { + free(dev->events); + } + if (plat->chardev_capable) { _mraa_free_gpio_groups(dev); @@ -1627,8 +1571,10 @@ mraa_gpio_owner(mraa_gpio_context dev, mraa_boolean_t own) syslog(LOG_ERR, "gpio: owner: context is invalid"); return MRAA_ERROR_INVALID_HANDLE; } + syslog(LOG_DEBUG, "gpio%i: owner: Set owner to %d", dev->pin, (int) own); dev->owner = own; + return MRAA_SUCCESS; } @@ -1645,6 +1591,7 @@ mraa_gpio_use_mmaped(mraa_gpio_context dev, mraa_boolean_t mmap_en) } syslog(LOG_ERR, "gpio%i: use_mmaped: mmap not implemented on this platform", dev->pin); + return MRAA_ERROR_FEATURE_NOT_IMPLEMENTED; } @@ -1655,6 +1602,7 @@ mraa_gpio_get_pin(mraa_gpio_context dev) syslog(LOG_ERR, "gpio: get_pin: context is invalid"); return -1; } + return dev->phy_pin; } @@ -1665,6 +1613,7 @@ mraa_gpio_get_pin_raw(mraa_gpio_context dev) syslog(LOG_ERR, "gpio: get_pin: context is invalid"); return -1; } + return dev->pin; } diff --git a/src/gpio/gpio_chardev.c b/src/gpio/gpio_chardev.c index 27a1c14..0d9c698 100644 --- a/src/gpio/gpio_chardev.c +++ b/src/gpio/gpio_chardev.c @@ -50,38 +50,74 @@ void _mraa_free_gpio_groups(mraa_gpio_context dev) { - mraa_gpiod_group_t gpio_group = dev->gpio_group; + mraa_gpiod_group_t gpio_iter; - for (int i = 0; i < dev->num_chips; ++i) { - if (gpio_group[i].is_required) { - free(gpio_group[i].gpio_lines); - free(gpio_group[i].rw_values); - free(gpio_group[i].gpio_group_to_pins_table); + for_each_gpio_group(gpio_iter, dev) { + free(gpio_iter->gpio_lines); + free(gpio_iter->rw_values); + free(gpio_iter->gpio_group_to_pins_table); - if (gpio_group[i].gpiod_handle != -1) { - close(gpio_group[i].gpiod_handle); - } - - if (gpio_group[i].event_handles != NULL) { - for (int j = 0; j < gpio_group[i].num_gpio_lines; ++j) { - close(gpio_group[i].event_handles[j]); - } - - free(gpio_group[i].event_handles); - } - - close(gpio_group[i].dev_fd); + if (gpio_iter->gpiod_handle != -1) { + close(gpio_iter->gpiod_handle); } + + if (gpio_iter->event_handles != NULL) { + for (int j = 0; j < gpio_iter->num_gpio_lines; ++j) { + close(gpio_iter->event_handles[j]); + } + + free(gpio_iter->event_handles); + } + + close(gpio_iter->dev_fd); } - free(gpio_group); + free(dev->gpio_group); /* Also delete the pin to gpio chip mapping. */ free(dev->pin_to_gpio_table); + + /* User provided array saved internally. */ + free(dev->provided_pins); + + /* Finally, delete event array. */ + free(dev->events); +} + +void +_mraa_close_gpio_event_handles(mraa_gpio_context dev) +{ + mraa_gpiod_group_t gpio_iter; + + for_each_gpio_group(gpio_iter, dev) { + if (gpio_iter->event_handles != NULL) { + for (int j = 0; j < gpio_iter->num_gpio_lines; ++j) { + close(gpio_iter->event_handles[j]); + } + + free(gpio_iter->event_handles); + + /* In the end, _mraa_free_gpio_groups will be called. */ + gpio_iter->event_handles = NULL; + } + } +} + +void +_mraa_close_gpio_desc(mraa_gpio_context dev) +{ + mraa_gpiod_group_t gpio_iter; + + for_each_gpio_group(gpio_iter, dev) { + if (gpio_iter->gpiod_handle != -1) { + close(gpio_iter->gpiod_handle); + gpio_iter->gpiod_handle = -1; + } + } } int -_mraa__mraa_gpiod_ioctl(int fd, unsigned long gpio_request, void* data) +_mraa_gpiod_ioctl(int fd, unsigned long gpio_request, void* data) { int status; @@ -319,6 +355,37 @@ mraa_get_line_values(int line_handle, unsigned int num_lines, unsigned char outp return status; } + +mraa_boolean_t +mraa_is_gpio_line_kernel_owned(mraa_gpiod_line_info *linfo) +{ + return (linfo->flags & GPIOLINE_FLAG_IS_OUT); +} + +mraa_boolean_t +mraa_is_gpio_line_dir_out(mraa_gpiod_line_info *linfo) +{ + return (linfo->flags & GPIOLINE_FLAG_IS_OUT); +} + +mraa_boolean_t +mraa_is_gpio_line_active_low(mraa_gpiod_line_info *linfo) +{ + return (linfo->flags & GPIOLINE_FLAG_ACTIVE_LOW); +} + +mraa_boolean_t +mraa_is_gpio_line_open_drain(mraa_gpiod_line_info *linfo) +{ + return (linfo->flags & GPIOLINE_FLAG_OPEN_DRAIN); +} + +mraa_boolean_t +mraa_is_gpio_line_open_source(mraa_gpiod_line_info *linfo) +{ + return (linfo->flags & GPIOHANDLE_REQUEST_OPEN_SOURCE); +} + int mraa_get_number_of_gpio_chips() { @@ -343,4 +410,3 @@ mraa_get_number_of_gpio_chips() /* Assume opendir() error. */ return num_chips; } - diff --git a/src/mraa.c b/src/mraa.c index d9505f1..cbb7597 100644 --- a/src/mraa.c +++ b/src/mraa.c @@ -57,6 +57,7 @@ #include "firmata/firmata_mraa.h" #include "grovepi/grovepi.h" #include "gpio.h" +#include "gpio/gpio_chardev.h" #include "version.h" #include "i2c.h" #include "pwm.h" @@ -100,43 +101,7 @@ mraa_set_log_level(int level) mraa_boolean_t mraa_is_kernel_chardev_interface_compatible() { - struct utsname buf; - int status; - - status = uname(&buf); - - if (status) { - syslog(LOG_ERR, "uname() error"); - return 0; - } - - int major, minor; - char *token; - - token = strtok(buf.release, "."); - if (token == NULL) { - syslog(LOG_ERR, "Could not find kernel version major number"); - return 0; - } - status = mraa_atoi(token, &major); - if (status) { - syslog(LOG_ERR, "mraa_atoi() error"); - return 0; - } - - token = strtok(NULL, "."); - if (token == NULL) { - syslog(LOG_ERR, "Could not find kernel version minor number"); - return 0; - } - status = mraa_atoi(token, &minor); - if (status) { - syslog(LOG_ERR, "mraa_atoi() error"); - return 0; - } - - if (major < 4 || minor < 8) { - syslog(LOG_ERR, "Kernel version %i.%i not supported for chardev interface. Need version 4.8 or newer!", major, minor); + if (mraa_get_number_of_gpio_chips() <= 0) { return 0; }