(
On Mon, Jun 22, 2015 at 8:01 AM, Andre Marques <andre.lousa.marq...@gmail.com> wrote: > --- > c/src/lib/libbsp/Makefile.am | 3 +- > c/src/lib/libbsp/preinstall.am | 4 + > c/src/lib/libbsp/shared/gpio.c | 1083 > ++++++++++++++++++++++++++++++++ > c/src/lib/libbsp/shared/include/gpio.h | 610 ++++++++++++++++++ > 4 files changed, 1699 insertions(+), 1 deletion(-) > create mode 100644 c/src/lib/libbsp/shared/gpio.c > create mode 100644 c/src/lib/libbsp/shared/include/gpio.h > > diff --git a/c/src/lib/libbsp/Makefile.am b/c/src/lib/libbsp/Makefile.am > index 3cab4d7..a039a98 100644 > --- a/c/src/lib/libbsp/Makefile.am > +++ b/c/src/lib/libbsp/Makefile.am > @@ -9,7 +9,7 @@ EXTRA_DIST = MERGE.PROCEDURE bsp.am > EXTRA_DIST += shared/bootcard.c shared/bspclean.c \ > shared/bsplibc.c shared/bsppost.c shared/console-polled.c \ > shared/console.c shared/gnatinstallhandler.c shared/sbrk.c \ > - shared/tod.c > + shared/tod.c shared/gpio.c > EXTRA_DIST += shared/vmeUniverse/vmeUniverse.c \ > shared/vmeUniverse/vmeUniverse.h \ > shared/vmeUniverse/vmeUniverseDMA.h \ > @@ -35,6 +35,7 @@ include_bsp_HEADERS = > include_bsp_HEADERS += shared/include/default-initial-extension.h > include_bsp_HEADERS += shared/include/fatal.h > include_bsp_HEADERS += shared/include/console-termios.h > +include_bsp_HEADERS += shared/include/gpio.h > > include $(srcdir)/preinstall.am > include $(top_srcdir)/automake/subdirs.am > diff --git a/c/src/lib/libbsp/preinstall.am b/c/src/lib/libbsp/preinstall.am > index 651f136..bbcb7c5 100644 > --- a/c/src/lib/libbsp/preinstall.am > +++ b/c/src/lib/libbsp/preinstall.am > @@ -30,3 +30,7 @@ $(PROJECT_INCLUDE)/bsp/console-termios.h: > shared/include/console-termios.h $(PRO > $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/console-termios.h > PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/console-termios.h > > +$(PROJECT_INCLUDE)/bsp/gpio.h: shared/include/gpio.h > $(PROJECT_INCLUDE)/bsp/$(dirstamp) > + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/gpio.h > +PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/gpio.h > + > diff --git a/c/src/lib/libbsp/shared/gpio.c b/c/src/lib/libbsp/shared/gpio.c > new file mode 100644 > index 0000000..12cdb29 > --- /dev/null > +++ b/c/src/lib/libbsp/shared/gpio.c > @@ -0,0 +1,1083 @@ > +/** > + * @file gpio.c > + * > + * @ingroup rtems_gpio > + * > + * @brief RTEMS GPIO API implementation. > + */ > + > +/* > + * Copyright (c) 2014-2015 Andre Marques <andre.lousa.marques at gmail.com> > + * > + * The license and distribution terms for this file may be > + * found in the file LICENSE in this distribution or at > + * http://www.rtems.org/license/LICENSE. > + */ > + > +#include <rtems/score/atomic.h> > +#include <rtems/status-checks.h> > +#include <bsp/irq-generic.h> > +#include <bsp/gpio.h> > + > +#include <assert.h> > +#include <stdlib.h> > + > +/** > + * @brief GPIO API mutex atributes. spelling: attributes > + */ > +#define MUTEX_ATRIBUTES \ here too. > + ( RTEMS_LOCAL \ > + | RTEMS_PRIORITY \ > + | RTEMS_BINARY_SEMAPHORE \ > + | RTEMS_INHERIT_PRIORITY \ > + | RTEMS_NO_PRIORITY_CEILING \ > + ) > + > +#define AQUIRE_LOCK(m) assert( rtems_semaphore_obtain(m, \ spelling: ACQUIRE > + RTEMS_WAIT, \ > + RTEMS_NO_TIMEOUT \ > + ) == RTEMS_SUCCESSFUL ) > + > +#define RELEASE_LOCK(m) assert( rtems_semaphore_release(m) == > RTEMS_SUCCESSFUL ) > + > +/*Encapsulates relevant data for a GPIO interrupt handler. */ Add Doxygen for all of these structs > +typedef struct _gpio_handler_list > +{ > + struct _gpio_handler_list *next_isr; Consider using an RTEMS chain (doubly-linked list) here. It would save on some list-manipulation code, and force you to use readable functions that already exist in the rtems namespace. If you want one list per pin, you can embed an rtems_chain_control in each gpio_pin. > + > + /* User-defined ISR routine. */ > + rtems_gpio_irq_state (*handler) (void *arg); > + > + /* User-defined arguments for the ISR routine. */ > + void *arg; > +} gpio_handler_list; > + > +/* Encapsulates relevant data about a GPIO pin. */ > +typedef struct > +{ > + uint32_t bank_number; > + uint32_t pin_number; > + > + rtems_gpio_function pin_function; > + > + /* Type of event which will trigger an interrupt. */ > + rtems_gpio_interrupt enabled_interrupt; enabled_interrupt brings to mind isr enable/disable, if this field is more than a bool, then a more descriptive name should be used. > + > + /* Id of the task that will be calling the user-defined ISR handlers > + * for this pin. */ > + rtems_id task_id; perhaps 'handler_task_id' can help readability > + > + /* ISR shared flag. */ > + rtems_gpio_handler_flag handler_flag; > + > + /* Linked list of interrupt handlers. */ > + gpio_handler_list *handler_list; > + > + /* GPIO input (pull resistor) pin mode. */ > + rtems_gpio_pull_mode resistor_mode; > + > + /* If true inverts digital in/out logic. */ > + int logic_invert; bool instead of int? > + > + /* Switch-deboucing information. */ > + int debouncing_tick_count; > + rtems_interval last_isr_tick; > +} gpio_pin; > + > +static gpio_pin** gpio_pin_state; > +static Atomic_Flag init_flag = ATOMIC_INITIALIZER_FLAG; > +static uint32_t gpio_count; > +static uint32_t bank_count; > +static uint32_t pins_per_bank; > +static uint32_t odd_bank_pins; > +static uint32_t* interrupt_counter; > +static rtems_id* bank_lock; This many global (file-scope) variables indicates poor modularity in the source. > + > +#define BANK_NUMBER(pin_number) pin_number / pins_per_bank > +#define PIN_NUMBER(pin_number) pin_number % pins_per_bank > + > +static int debounce_switch(gpio_pin* gpio) > +{ > + rtems_interval time; > + > + time = rtems_clock_get_ticks_since_boot(); > + > + /* If not enough time has elapsed since last interrupt. */ > + if ( (time - gpio->last_isr_tick) < gpio->debouncing_tick_count ) { > + return -1; > + } > + > + gpio->last_isr_tick = time; > + > + return 0; > +} > + > +static rtems_task generic_handler_task(rtems_task_argument arg) > +{ > + gpio_handler_list *handler_list; > + rtems_event_set out; > + gpio_pin* gpio; put the * next to gpio. > + uint32_t bank; > + int handled_count; > + int rv; > + > + gpio = (gpio_pin*) arg; > + > + bank = gpio->bank_number; Validate args for errors. > + > + while ( true ) { > + handled_count = 0; > + > + /* Wait for interrupt event. This is sent by the bank's generic_isr > handler. */ > + rtems_event_receive(RTEMS_EVENT_1, RTEMS_EVENT_ALL | RTEMS_WAIT, > + RTEMS_NO_TIMEOUT, > + &out); When you split across multiple lines, put one argument per line and no args on first/last line, e.g. rtems_event_receive( RTEMS_EVENT_1, RTEMS_EVENT_ALL | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &out ); > + > + AQUIRE_LOCK(bank_lock[bank]); What is the lock protecting? > + > + /* If this pin has the debouncing function attached, call it. */ > + if ( gpio->debouncing_tick_count > 0 ) { > + rv = debounce_switch(gpio); > + > + /* If the handler call was caused by a switch bounce, ignores and move > on. */ > + if ( rv < 0 ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + continue; > + } > + > + /* Record the current clock tick. */ > + gpio->last_isr_tick = rtems_clock_get_ticks_since_boot(); debounce_switch() already did this? > + } > + > + handler_list = gpio->handler_list; > + > + /* Iterate the ISR list. */ > + while ( handler_list != NULL ) { > + if ( (handler_list->handler)(handler_list->arg) == IRQ_HANDLED ) { > + ++handled_count; > + } > + > + handler_list = handler_list->next_isr; > + } > + > + /* If no handler assumed the interrupt, treat it as a spurious > interrupt. */ Wrong word, "assumed". Not sure what you mean to say though. Maybe "consumed"? Anyway, you can say "handled". > + if ( handled_count == 0 ) { > + bsp_interrupt_handler_default(rtems_bsp_gpio_get_vector(bank)); > + } > + > + RELEASE_LOCK(bank_lock[bank]); > + } > +} > + > +static void generic_isr(void* arg) > +{ > + rtems_vector_number vector; > + uint32_t event_status; > + uint32_t bank_pin_count; > + gpio_pin *gpio; > + uint32_t bank_number; > + int i; > + > + gpio = (gpio_pin *) arg; > + > + /* Get the bank/vector number of a pin (e.g.: 0) from this bank. */ > + bank_number = gpio[0].bank_number; > + Again, validate args. Why is it gpio[0] here? And not above? > + vector = rtems_bsp_gpio_get_vector(bank_number); > + > + /* If the current bank is an odd last bank (i.e.: not completely filled). > */ > + if ( odd_bank_pins > 0 && bank_number == bank_count - 1 ) { > + bank_pin_count = odd_bank_pins; > + } > + else { > + bank_pin_count = pins_per_bank; > + } This can be simplified by if (bank_number == bank_count - 1) bank_pin_count = last_bank_pins; /* or odd_bank_pins */ else bank_pin_count - pins_per_bank; I prefer "last_bank_pins" myself. odd_bank_pins is strange to me, is it a standard term? Is it the case that only the last bank is ever going to have a number of pins different from the others? Or should there instead just be an array of pins_per_bank[bank_count]? > + > + /* Prevents more interrupts from being generated on GPIO. */ > + bsp_interrupt_vector_disable(vector); > + > + /* Ensure that interrupts are disabled in this vector, before checking > + * the interrupt line. */ > + RTEMS_COMPILER_MEMORY_BARRIER(); Manually inserting memory barriers is hackish. If we need this after bsp_interrupt_vector_disable(), then you might consider proposing we add it directly there. > + > + /* Obtains a 32-bit bitmask, with the pins currently reporting interrupts > + * signaled with 1. */ > + event_status = rtems_bsp_gpio_interrupt_line(vector); > + Is it ever the case there are more than 32 pins in a bank? (If not, this should be assert()ed somewhere in the code.) > + /* Iterates through the bitmask and calls the corresponding handler > + * for active interrupts. */ > + for ( i = 0; i < bank_pin_count; ++i ) { > + /* If active, wake the corresponding pin's ISR task. */ > + if ( event_status & (1 << i) ) { > + rtems_event_send(gpio[i].task_id, RTEMS_EVENT_1); It may improve readability to #define something as RTEMS_EVENT_1. like #define GPIO_INTERRUPT_ACTIVE RTEMS_EVENT_1. > + } > + } > + > + /* Clear all active events. */ > + rtems_bsp_gpio_clear_interrupt_line(vector, event_status); > + > + /* Ensure that the interrupt line is cleared before re-activating > + * the interrupts on this vector. */ > + RTEMS_COMPILER_MEMORY_BARRIER(); Again, this should be done in bsp_interrupt_vector_enable? > + > + bsp_interrupt_vector_enable(vector); > +} > + > +static uint32_t get_pin_bitmask(uint32_t count, va_list args, uint32_t* > bank_number, rtems_status_code* sc) Line length > 80 chars It is probably better (more common in RTEMS) to return the status code directly and the pin_bitmask through an argument side-effect. Especially since this function already returns bank_number through the side effect, this makes a little more sense. You might consider splitting this function into two different ones: get_bank_number() get_pin_bitmask() Splitting it will reduce the complexity. Please document any use of variadic arguments. A function with both va args and multiple return values is already complex to understand without even reading its code... Is it better to pass a va_list here, or an array of integer pin numbers? (When all variadic arguments have the same type, the question is worth asking.) > +{ > + uint32_t bank; > + uint32_t pin; > + uint32_t bitmask; > + uint32_t pin_number; > + int i; > + > + if ( count < 1 ) { equivalent: count == 0. > + *sc = RTEMS_UNSATISFIED; > + > + return 0; And then you can just return RTEMS_UNSATISFIED without touching the bitmask. > + } > + > + bitmask = 0; > + > + for ( i = 0; i < count; ++i ) { > + pin_number = va_arg(args, uint32_t); > + > + if ( pin_number < 0 || pin_number >= gpio_count ) { > + *sc = RTEMS_INVALID_ID; > + > + return 0; > + } > + > + bank = BANK_NUMBER(pin_number); If you already know the bank_number, you can replace this with if(BANK_NUMBER(pin_number) != bank_number) return RTEMS_UNSATISFIED; > + pin = PIN_NUMBER(pin_number); > + > + if ( i == 0 ) { > + *bank_number = bank; > + > + AQUIRE_LOCK(bank_lock[bank]); > + } > + else if ( bank != *bank_number ) { > + *sc = RTEMS_UNSATISFIED; > + > + RELEASE_LOCK(bank_lock[*bank_number]); > + > + return 0; > + } > + > + if ( gpio_pin_state[bank][pin].pin_function != DIGITAL_OUTPUT ) { > + *sc = RTEMS_NOT_CONFIGURED; > + > + RELEASE_LOCK(bank_lock[bank]); > + > + return 0; > + } > + > + bitmask |= (1 << PIN_NUMBER(pin_number)); pin variable already stores PIN_NUMBER(pin_number). > + } > + > + *sc = RTEMS_SUCCESSFUL; > + > + RELEASE_LOCK(bank_lock[bank]); > + > + return bitmask; > +} > + > +rtems_status_code rtems_gpio_initialize(void) > +{ > + rtems_status_code sc; > + rtems_gpio_layout layout; > + int bank_pins; > + int pin; > + int bank; > + int i; > + > + if ( _Atomic_Flag_test_and_set(&init_flag, ATOMIC_ORDER_RELAXED) == true ) > { Note: More motivation for a public-facing API for our atomic ops: https://devel.rtems.org/ticket/2366 > + return RTEMS_SUCCESSFUL; > + } > + > + layout = rtems_bsp_gpio_initialize(); > + Validate return values. > + gpio_count = layout.pin_count; > + pins_per_bank = layout.pins_per_bank; > + > + bank_count = gpio_count / pins_per_bank; > + > + /* Account for remaining pins after filling the last bank. */ > + odd_bank_pins = gpio_count % pins_per_bank; > + > + if ( odd_bank_pins > 0 ) { > + ++bank_count; > + } > + > + /* Create GPIO bank mutexes. */ > + bank_lock = (rtems_id*) malloc(bank_count * sizeof(rtems_id)); Is there any way to get the max bank count at compile-time from the BSP? If so we can statically create some of these structures, locks, variables, etc. This dynamic memory allocation is troublesome. > + > + for ( i = 0; i < bank_count; ++i ) { > + sc = rtems_semaphore_create(rtems_build_name('G', 'I', 'N', 'T'), 1, > MUTEX_ATRIBUTES, 0, &bank_lock[i]); These semaphores need to be accounted for in confdefs.h, but this means we need an accurate number for bank_count. > + > + if ( sc != RTEMS_SUCCESSFUL ) { > + return sc; > + } > + } > + > + /* Create GPIO pin state matrix. */ > + gpio_pin_state = (gpio_pin**) malloc(bank_count * sizeof(gpio_pin*)); > + > + for ( i = 0; i < bank_count; ++i ) { > + if ( i == bank_count - 1 ) { > + bank_pins = odd_bank_pins; > + } > + else { > + bank_pins = pins_per_bank; > + } > + > + gpio_pin_state[i] = (gpio_pin*) malloc(bank_pins * sizeof(gpio_pin)); > + } > + > + /* Creates an interrupt counter per pin bank. */ > + interrupt_counter = (uint32_t*) calloc(bank_count, sizeof(uint32_t)); > + > + for ( i = 0; i < gpio_count; ++i ) { > + bank = BANK_NUMBER(i); > + pin = PIN_NUMBER(i); > + > + gpio_pin_state[bank][pin].bank_number = bank; > + gpio_pin_state[bank][pin].pin_number = pin; > + gpio_pin_state[bank][pin].pin_function = NOT_USED; > + gpio_pin_state[bank][pin].enabled_interrupt = NONE; > + gpio_pin_state[bank][pin].task_id = RTEMS_ID_NONE; > + gpio_pin_state[bank][pin].handler_list = NULL; > + gpio_pin_state[bank][pin].debouncing_tick_count = 0; > + gpio_pin_state[bank][pin].last_isr_tick = 0; > + } > + > + return RTEMS_SUCCESSFUL; > +} > + > +rtems_status_code rtems_gpio_request_conf(rtems_gpio_pin_conf* conf) > +{ > + rtems_gpio_interrupt_conf* interrupt_conf; > + rtems_status_code sc; > + bool new_request; > + uint32_t bank; > + uint32_t pin; > + > + new_request = false; > + > + sc = rtems_gpio_request_pin(conf->pin_number, conf->function, > conf->output_enabled, conf->logic_invert, conf->bsp_specific); line lengths > + > + if ( sc == RTEMS_SUCCESSFUL ) { > + new_request = true; > + } > + /* If the pin is being used, then this function call is an update call. > + * If not, an error occurred. */ > + else if ( sc != RTEMS_RESOURCE_IN_USE ) { > + RTEMS_SYSLOG_ERROR("rtems_gpio_request_pin failed with status code > %d\n", sc); I don't think this SYSLOG_ERROR is much used, so I recommend avoiding it in favor of just returning the error value, and maybe using a debug printk if you like. > + > + return RTEMS_UNSATISFIED; > + } > + > + sc = rtems_gpio_resistor_mode(conf->pin_number, conf->pull_mode); > + > + if ( sc != RTEMS_SUCCESSFUL ) { > + RTEMS_SYSLOG_ERROR("rtems_gpio_resistor_mode failed with status code > %d\n", sc); > + > + return RTEMS_UNSATISFIED; > + } > + > + interrupt_conf = (rtems_gpio_interrupt_conf*) conf->interrupt; > + > + if ( interrupt_conf != NULL ) { > + bank = BANK_NUMBER(conf->pin_number); > + pin = PIN_NUMBER(conf->pin_number); Why doesn't the conf store these, anyway? > + > + AQUIRE_LOCK(bank_lock[bank]); And also store its own lock field? > + > + if ( interrupt_conf->enabled_interrupt != > gpio_pin_state[bank][pin].enabled_interrupt ) { > + if ( new_request == false ) { > + sc = rtems_gpio_disable_interrupt(conf->pin_number); > + > + if ( sc != RTEMS_SUCCESSFUL ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + RTEMS_SYSLOG_ERROR("rtems_gpio_disable_interrupt failed with > status code %d\n", sc); > + > + return RTEMS_UNSATISFIED; > + } > + } > + > + sc = rtems_gpio_enable_interrupt(conf->pin_number, > + interrupt_conf->enabled_interrupt, > + interrupt_conf->handler_flag, > + interrupt_conf->handler, > + interrupt_conf->arg); Fix first/last lines. > + > + if ( sc != RTEMS_SUCCESSFUL ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + RTEMS_SYSLOG_ERROR("rtems_gpio_enable_interrupt failed with status > code %d\n", sc); > + > + return RTEMS_UNSATISFIED; > + } > + > + } > + > + if ( interrupt_conf->clock_tick_interval != > gpio_pin_state[bank][pin].debouncing_tick_count ) { > + gpio_pin_state[bank][pin].debouncing_tick_count = > interrupt_conf->clock_tick_interval; > + gpio_pin_state[bank][pin].last_isr_tick = 0; > + } > + > + RELEASE_LOCK(bank_lock[bank]); > + } > + > + return RTEMS_SUCCESSFUL; > +} > + > +rtems_status_code rtems_gpio_multi_set(uint32_t count, ...) > +{ > + rtems_status_code sc; > + uint32_t bank; > + uint32_t bitmask; > + va_list ap; > + > + va_start(ap, count); > + > + bitmask = get_pin_bitmask(count, ap, &bank, &sc); > + > + if ( sc != RTEMS_SUCCESSFUL ) { > + RTEMS_SYSLOG_ERROR("error parsing function arguments\n"); > + > + return sc; > + } > + > + va_end(ap); > + > + AQUIRE_LOCK(bank_lock[bank]); > + > + sc = rtems_bsp_gpio_multi_set(bank, bitmask); > + > + RELEASE_LOCK(bank_lock[bank]); > + > + return sc; > +} > + > +rtems_status_code rtems_gpio_multi_clear(uint32_t count, ...) > +{ > + rtems_status_code sc; > + uint32_t bank; > + uint32_t bitmask; > + va_list ap; > + > + va_start(ap, count); > + > + bitmask = get_pin_bitmask(count, ap, &bank, &sc); > + > + if ( sc != RTEMS_SUCCESSFUL ) { > + RTEMS_SYSLOG_ERROR("error parsing function arguments\n"); > + > + return sc; > + } > + > + va_end(ap); > + > + AQUIRE_LOCK(bank_lock[bank]); > + > + sc = rtems_bsp_gpio_multi_clear(bank, bitmask); > + > + RELEASE_LOCK(bank_lock[bank]); > + > + return sc; > +} > + > +rtems_status_code rtems_gpio_set(uint32_t pin_number) > +{ > + rtems_status_code sc; > + uint32_t bank; > + uint32_t pin; > + > + if ( pin_number < 0 || pin_number >= gpio_count ) { > + return RTEMS_INVALID_ID; > + } > + > + bank = BANK_NUMBER(pin_number); > + pin = PIN_NUMBER(pin_number); > + > + AQUIRE_LOCK(bank_lock[bank]); > + > + if ( gpio_pin_state[bank][pin].pin_function != DIGITAL_OUTPUT ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + RTEMS_SYSLOG_ERROR("Can only set digital output pins\n"); > + > + return RTEMS_NOT_CONFIGURED; > + } > + > + if ( gpio_pin_state[bank][pin].logic_invert ) { > + sc = rtems_bsp_gpio_clear(bank, pin); > + } > + else { > + sc = rtems_bsp_gpio_set(bank, pin); > + } > + > + RELEASE_LOCK(bank_lock[bank]); > + > + return sc; > +} > + > +rtems_status_code rtems_gpio_clear(uint32_t pin_number) > +{ > + rtems_status_code sc; > + uint32_t bank; > + uint32_t pin; > + > + if ( pin_number < 0 || pin_number >= gpio_count ) { > + return RTEMS_INVALID_ID; > + } > + > + bank = BANK_NUMBER(pin_number); > + pin = PIN_NUMBER(pin_number); > + > + AQUIRE_LOCK(bank_lock[bank]); > + > + if ( gpio_pin_state[bank][pin].pin_function != DIGITAL_OUTPUT ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + RTEMS_SYSLOG_ERROR("Can only clear digital output pins\n"); > + > + return RTEMS_NOT_CONFIGURED; > + } > + > + if ( gpio_pin_state[bank][pin].logic_invert ) { > + sc = rtems_bsp_gpio_set(bank, pin); > + } > + else { > + sc = rtems_bsp_gpio_clear(bank, pin); > + } > + > + RELEASE_LOCK(bank_lock[bank]); > + > + return sc; > +} > + > +int rtems_gpio_get_value(uint32_t pin_number) > +{ > + uint32_t bank; > + uint32_t pin; > + int rv; > + > + if ( pin_number < 0 || pin_number >= gpio_count ) { > + return -1; > + } > + > + bank = BANK_NUMBER(pin_number); > + pin = PIN_NUMBER(pin_number); > + > + AQUIRE_LOCK(bank_lock[bank]); > + > + if ( gpio_pin_state[bank][pin].pin_function != DIGITAL_INPUT) { > + RELEASE_LOCK(bank_lock[bank]); > + > + RTEMS_SYSLOG_ERROR("Can only read digital input pins\n"); > + > + return -1; > + } > + > + rv = rtems_bsp_gpio_get_value(bank, pin); > + > + if ( gpio_pin_state[bank][pin].logic_invert && rv > 0 ) { why rv > 0? > + RELEASE_LOCK(bank_lock[bank]); > + > + return !rv; > + } > + > + RELEASE_LOCK(bank_lock[bank]); > + > + return ( rv > 0 ) ? 1 : rv; > +} > + > +rtems_status_code rtems_gpio_request_pin(uint32_t pin_number, > rtems_gpio_function function, bool output_enabled, bool logic_invert, void* > bsp_specific) > +{ > + rtems_gpio_specific_data* bsp_data; > + rtems_status_code sc = RTEMS_SUCCESSFUL; > + uint32_t bank; > + uint32_t pin; > + > + if ( pin_number < 0 || pin_number >= gpio_count ) { > + return RTEMS_INVALID_ID; > + } > + > + bank = BANK_NUMBER(pin_number); > + pin = PIN_NUMBER(pin_number); > + > + AQUIRE_LOCK(bank_lock[bank]); > + > + /* If the pin is already being used returns with an error. */ > + if ( gpio_pin_state[bank][pin].pin_function != NOT_USED ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + return RTEMS_RESOURCE_IN_USE; > + } > + > + switch ( function ) { > + case DIGITAL_INPUT: > + sc = rtems_bsp_gpio_select_input(bank, pin, bsp_specific); > + break; > + case DIGITAL_OUTPUT: > + sc = rtems_bsp_gpio_select_output(bank, pin, bsp_specific); > + break; > + case BSP_SPECIFIC: > + bsp_data = (rtems_gpio_specific_data*) bsp_specific; > + > + if ( bsp_data == NULL ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + return RTEMS_UNSATISFIED; > + } > + > + sc = rtems_bsp_select_specific_io(bank, pin, bsp_data->io_function, > bsp_data->pin_data); > + break; > + case NOT_USED: > + default: > + RELEASE_LOCK(bank_lock[bank]); > + > + return RTEMS_NOT_DEFINED; > + } > + > + if ( sc != RTEMS_SUCCESSFUL ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + return sc; > + } > + > + /* If the function was successfuly assigned to the pin, > + * record that information on the gpio_pin_state structure. */ > + gpio_pin_state[bank][pin].pin_function = function; > + gpio_pin_state[bank][pin].logic_invert = logic_invert; > + > + if ( function == DIGITAL_OUTPUT ) { > + if ( output_enabled == true ) { > + sc = rtems_bsp_gpio_set(bank, pin); Shouldn't this also check logic_invert? > + } > + else { > + sc = rtems_bsp_gpio_clear(bank, pin); > + } > + } > + > + RELEASE_LOCK(bank_lock[bank]); > + > + return sc; > +} > + > +rtems_status_code rtems_gpio_resistor_mode(uint32_t pin_number, > rtems_gpio_pull_mode mode) > +{ > + rtems_status_code sc; > + uint32_t bank; > + uint32_t pin; > + > + if ( pin_number < 0 || pin_number >= gpio_count ) { > + return RTEMS_INVALID_ID; > + } > + > + bank = BANK_NUMBER(pin_number); > + pin = PIN_NUMBER(pin_number); > + > + AQUIRE_LOCK(bank_lock[bank]); > + > + /* If the desired actuation mode is already set, silently exits. > + * The NO_PULL_RESISTOR is a special case, as some platforms have > + * pull-up resistors enabled on startup, so this state may have to > + * be reinforced in the hardware. */ > + if ( gpio_pin_state[bank][pin].resistor_mode == mode && mode != > NO_PULL_RESISTOR ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + return RTEMS_SUCCESSFUL; > + } > + > + sc = rtems_bsp_gpio_set_resistor_mode(bank, pin, mode); > + > + if ( sc != RTEMS_SUCCESSFUL ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + return sc; > + } > + > + gpio_pin_state[bank][pin].resistor_mode = mode; > + > + RELEASE_LOCK(bank_lock[bank]); > + > + return RTEMS_SUCCESSFUL; > +} > + > +rtems_status_code rtems_gpio_release_pin(uint32_t pin_number) > +{ > + rtems_status_code sc; > + gpio_pin* gpio; > + uint32_t bank; > + uint32_t pin; > + > + if ( pin_number < 0 || pin_number >= gpio_count ) { > + return RTEMS_INVALID_ID; > + } > + > + bank = BANK_NUMBER(pin_number); > + pin = PIN_NUMBER(pin_number); > + > + AQUIRE_LOCK(bank_lock[bank]); > + > + gpio = &gpio_pin_state[bank][pin]; > + > + /* If the pin has an enabled interrupt then remove the handler(s), > + * and disable the interrupts on that pin. */ > + if ( gpio->enabled_interrupt != NONE ) { > + sc = rtems_gpio_disable_interrupt(pin_number); > + > + if ( sc != RTEMS_SUCCESSFUL ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + return sc; > + } > + } > + > + gpio->pin_function = NOT_USED; > + gpio->task_id = RTEMS_ID_NONE; > + gpio->debouncing_tick_count = 0; > + gpio->last_isr_tick = 0; > + > + RELEASE_LOCK(bank_lock[bank]); > + > + return RTEMS_SUCCESSFUL; > +} > + > +rtems_status_code rtems_gpio_debounce_switch(uint32_t pin_number, int ticks) > +{ > + uint32_t bank; > + uint32_t pin; > + > + if ( pin_number < 0 || pin_number >= gpio_count ) { > + return RTEMS_INVALID_ID; > + } > + > + bank = BANK_NUMBER(pin_number); > + pin = PIN_NUMBER(pin_number); > + > + AQUIRE_LOCK(bank_lock[bank]); > + > + if ( gpio_pin_state[bank][pin].pin_function != DIGITAL_INPUT ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + return RTEMS_NOT_CONFIGURED; > + } > + > + gpio_pin_state[bank][pin].debouncing_tick_count = ticks; > + gpio_pin_state[bank][pin].last_isr_tick = > rtems_clock_get_ticks_per_second(); Why setting last_isr_tick? If anything set it to 0, or to the ticks since boot, but not to ticks_per_second... > + > + RELEASE_LOCK(bank_lock[bank]); > + > + return RTEMS_SUCCESSFUL; > +} > + > +rtems_status_code rtems_gpio_interrupt_handler_install( > +uint32_t pin_number, > +rtems_gpio_irq_state (*handler) (void *arg), > +void *arg > +) > +{ > + gpio_handler_list *isr_node; > + gpio_pin* gpio; > + uint32_t bank; > + uint32_t pin; > + > + if ( pin_number < 0 || pin_number >= gpio_count ) { > + return RTEMS_INVALID_ID; > + } > + > + bank = BANK_NUMBER(pin_number); > + pin = PIN_NUMBER(pin_number); > + > + AQUIRE_LOCK(bank_lock[bank]); > + > + gpio = &gpio_pin_state[bank][pin]; > + > + /* If the current pin has no interrupt enabled > + * then it does not need an handler. */ > + if ( gpio->enabled_interrupt == NONE ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + return RTEMS_NOT_CONFIGURED; > + } > + /* If the pin already has an enabled interrupt but the installed handler > + * is set as unique. */ > + else if ( gpio->handler_flag == UNIQUE_HANDLER && gpio->handler_list != > NULL ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + return RTEMS_RESOURCE_IN_USE; > + } > + > + /* Update the pin's ISR list. */ > + isr_node = (gpio_handler_list *) malloc(sizeof(gpio_handler_list)); > + > + if ( isr_node == NULL ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + return RTEMS_NO_MEMORY; > + } > + > + isr_node->handler = handler; > + isr_node->arg = arg; > + > + if ( gpio->handler_flag == SHARED_HANDLER ) { > + isr_node->next_isr = gpio->handler_list; > + } > + else { > + isr_node->next_isr = NULL; Isn't this the same thing as gpio->handler_list in case of !SHARED_HANDLER? > + } > + > + gpio->handler_list = isr_node; > + > + RELEASE_LOCK(bank_lock[bank]); > + > + return RTEMS_SUCCESSFUL; > +} > + > +rtems_status_code rtems_gpio_enable_interrupt( > +uint32_t pin_number, > +rtems_gpio_interrupt interrupt, > +rtems_gpio_handler_flag flag, > +rtems_gpio_irq_state (*handler) (void *arg), > +void *arg > +) > +{ > + rtems_vector_number vector; > + rtems_status_code sc; > + gpio_pin* gpio; > + uint32_t bank; > + uint32_t pin; > + > + if ( pin_number < 0 || pin_number >= gpio_count ) { > + return RTEMS_INVALID_ID; > + } > + > + bank = BANK_NUMBER(pin_number); > + pin = PIN_NUMBER(pin_number); > + > + vector = rtems_bsp_gpio_get_vector(bank); > + > + AQUIRE_LOCK(bank_lock[bank]); > + > + gpio = &gpio_pin_state[bank][pin]; > + > + if ( gpio->pin_function != DIGITAL_INPUT ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + return RTEMS_NOT_CONFIGURED; > + } > + > + /* If trying to enable the same type of interrupt on the same pin, or if > the pin > + * already has an enabled interrupt. */ > + if ( interrupt == gpio->enabled_interrupt || gpio->enabled_interrupt != > NONE ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + return RTEMS_RESOURCE_IN_USE; > + } > + > + /* Creates and starts a new task which will call the corresponding > + * user-defined handlers for this pin. */ > + sc = rtems_task_create(rtems_build_name('G', 'P', 'I', 'O'), > + 5, > + RTEMS_MINIMUM_STACK_SIZE * 2, > + RTEMS_NO_TIMESLICE, > + RTEMS_DEFAULT_ATTRIBUTES, > + &gpio->task_id); > + > + if ( sc != RTEMS_SUCCESSFUL ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + return RTEMS_UNSATISFIED; > + } > + > + sc = rtems_task_start(gpio->task_id, generic_handler_task, > (rtems_task_argument) gpio); > + > + if ( sc != RTEMS_SUCCESSFUL ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + sc = rtems_task_delete(gpio->task_id); > + > + assert( sc == RTEMS_SUCCESSFUL ); > + > + return RTEMS_UNSATISFIED; > + } > + > + gpio->enabled_interrupt = interrupt; > + gpio->handler_flag = flag; > + > + /* Installs the interrupt handler. */ > + sc = rtems_gpio_interrupt_handler_install(pin_number, handler, arg); > + > + if ( sc != RTEMS_SUCCESSFUL ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + sc = rtems_task_delete(gpio->task_id); > + > + assert( sc == RTEMS_SUCCESSFUL ); > + > + return RTEMS_UNSATISFIED; > + } > + > + /* If the generic ISR has not been yet installed for this bank, installs > it. > + * This ISR will be responsible for calling the handler tasks, > + * which in turn will call the user-defined interrupt handlers.*/ > + if ( interrupt_counter[bank] == 0 ) { > + sc = rtems_interrupt_handler_install(vector, > + "GPIO_HANDLER", > + RTEMS_INTERRUPT_UNIQUE, > + (rtems_interrupt_handler) > generic_isr, > + gpio_pin_state[bank]); > + > + if ( sc != RTEMS_SUCCESSFUL ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + return RTEMS_UNSATISFIED; > + } > + } > + > + sc = rtems_bsp_enable_interrupt(bank, pin, interrupt); > + > + if ( sc != RTEMS_SUCCESSFUL ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + return RTEMS_UNSATISFIED; > + } > + > + ++interrupt_counter[bank]; > + > + RELEASE_LOCK(bank_lock[bank]); > + > + return RTEMS_SUCCESSFUL; > +} > + > +rtems_status_code rtems_gpio_interrupt_handler_remove( > +uint32_t pin_number, > +rtems_gpio_irq_state (*handler) (void *arg), > +void *arg > +) > +{ > + gpio_handler_list *isr_node, *next_node; > + gpio_pin* gpio; > + uint32_t bank; > + uint32_t pin; > + > + if ( pin_number < 0 || pin_number >= gpio_count ) { > + return RTEMS_INVALID_ID; > + } > + > + bank = BANK_NUMBER(pin_number); > + pin = PIN_NUMBER(pin_number); > + > + AQUIRE_LOCK(bank_lock[bank]); > + > + gpio = &gpio_pin_state[bank][pin]; > + > + isr_node = gpio->handler_list; > + > + if ( isr_node != NULL ) { > + if ( isr_node->handler == handler && isr_node->arg == arg ) { > + gpio->handler_list = isr_node->next_isr; > + > + free(isr_node); > + } > + else { > + while ( isr_node->next_isr != NULL ) { > + if ( (isr_node->next_isr)->handler == handler && > (isr_node->next_isr)->arg == arg ) { > + next_node = (isr_node->next_isr)->next_isr; > + > + free(isr_node->next_isr); > + > + isr_node->next_isr = next_node; > + } > + } > + } > + } > + > + /* If the removed handler was the last for this pin, disables further > + * interrupts on this pin. */ > + if ( gpio->handler_list == NULL ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + return rtems_gpio_disable_interrupt(pin_number); > + } > + > + RELEASE_LOCK(bank_lock[bank]); > + > + return RTEMS_SUCCESSFUL; > +} > + > +rtems_status_code rtems_gpio_disable_interrupt(uint32_t pin_number) > +{ > + gpio_handler_list *isr_node; > + rtems_vector_number vector; > + rtems_status_code sc; > + gpio_pin* gpio; > + uint32_t bank; > + uint32_t pin; > + > + if ( pin_number < 0 || pin_number >= gpio_count ) { > + return RTEMS_INVALID_ID; > + } > + > + bank = BANK_NUMBER(pin_number); > + pin = PIN_NUMBER(pin_number); > + > + vector = rtems_bsp_gpio_get_vector(bank); > + > + AQUIRE_LOCK(bank_lock[bank]); > + > + gpio = &gpio_pin_state[bank][pin]; > + > + if ( interrupt_counter[bank] == 0 || gpio->enabled_interrupt == NONE ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + return RTEMS_SUCCESSFUL; > + } > + > + sc = rtems_bsp_disable_interrupt(bank, pin, gpio->enabled_interrupt); > + > + if ( sc != RTEMS_SUCCESSFUL ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + return RTEMS_UNSATISFIED; > + } > + > + gpio->enabled_interrupt = NONE; > + > + while ( gpio->handler_list != NULL ) { > + isr_node = gpio->handler_list; > + > + gpio->handler_list = isr_node->next_isr; > + > + free(isr_node); > + } > + > + sc = rtems_task_delete(gpio->task_id); So the handler task cannot be shared by more than one pin? Is this requirement explicit somewhere? Is this the intent? > + > + assert( sc == RTEMS_SUCCESSFUL ); > + > + --interrupt_counter[bank]; > + > + /* If no GPIO interrupts are left in this bank, removes the handler. */ > + if ( interrupt_counter[bank] == 0 ) { > + sc = rtems_interrupt_handler_remove(vector, > + (rtems_interrupt_handler) > generic_isr, > + gpio_pin_state[bank]); > + > + if ( sc != RTEMS_SUCCESSFUL ) { > + RELEASE_LOCK(bank_lock[bank]); > + > + return RTEMS_UNSATISFIED; > + } > + } > + > + RELEASE_LOCK(bank_lock[bank]); > + > + return RTEMS_SUCCESSFUL; > +} > diff --git a/c/src/lib/libbsp/shared/include/gpio.h > b/c/src/lib/libbsp/shared/include/gpio.h > new file mode 100644 > index 0000000..6a84747 > --- /dev/null > +++ b/c/src/lib/libbsp/shared/include/gpio.h > @@ -0,0 +1,610 @@ > +/** > + * @file gpio.h > + * > + * @ingroup rtems_gpio > + * > + * @brief RTEMS GPIO API definition. > + */ > + > +/* > + * Copyright (c) 2014-2015 Andre Marques <andre.lousa.marques at gmail.com> > + * > + * The license and distribution terms for this file may be > + * found in the file LICENSE in this distribution or at > + * http://www.rtems.org/license/LICENSE. > + */ > + > +#ifndef LIBBSP_SHARED__GPIO_H > +#define LIBBSP_SHARED__GPIO_H why two underscores? > + > +#include <rtems.h> > + > +#ifdef __cplusplus > +extern "C" { > +#endif /* __cplusplus */ > + > +/** > + * @name GPIO data structures > + * > + * @{ > + */ > + > +/** > + * @brief The set of possible configurations for a GPIO pull-up resistor. > + * > + * Enumerated type to define the possible pull-up resistor configuratons > + * for a GPIO pin. > + */ > +typedef enum > +{ > + PULL_UP = 1, > + PULL_DOWN, > + NO_PULL_RESISTOR > +} rtems_gpio_pull_mode; > + > +/** > + * @brief The set of possible functions a pin can have. > + * > + * Enumerated type to define a pin function. > + */ > +typedef enum > +{ > + DIGITAL_INPUT = 0, > + DIGITAL_OUTPUT, > + BSP_SPECIFIC, > + NOT_USED > +} rtems_gpio_function; > + > +/** > + * @brief The set of possible interrupts a GPIO pin can generate. > + * > + * Enumerated type to define a GPIO pin interrupt. > + */ > +typedef enum > +{ > + FALLING_EDGE = 0, > + RISING_EDGE, > + LOW_LEVEL, > + HIGH_LEVEL, > + BOTH_EDGES, > + BOTH_LEVELS, > + NONE > +} rtems_gpio_interrupt; > + > +/** > + * @brief The set of possible handled states an user-defined interrupt > + * handler can return. > + * > + * Enumerated type to define an interrupt handler handled state. > + */ > +typedef enum > +{ > + IRQ_HANDLED, > + IRQ_NONE > +} rtems_gpio_irq_state; > + > +/** > + * @brief The set of flags to specify an user-defined interrupt handler > + * uniqueness on a GPIO pin. > + * > + * Enumerated type to define an interrupt handler shared flag. > + */ > +typedef enum > +{ > + SHARED_HANDLER, > + UNIQUE_HANDLER > +} rtems_gpio_handler_flag; This could also be bool. > + > +/** > + * @brief Object containing relevant information for assigning a BSP specific > + * function to a pin. > + * > + * Encapsulates relevant data for a BSP specific GPIO function. > + */ > +typedef struct > +{ > + /* The bsp defined function code. */ > + uint32_t io_function; > + > + void* pin_data; > +} rtems_gpio_specific_data; > + > +/** > + * @brief Object containing relevant information about a BSP's GPIO layout. > + */ > +typedef struct > +{ > + /* Total number of GPIO pins. */ > + uint32_t pin_count; > + > + /* Number of pins per bank. The last bank may be smaller, > + * depending on the total number of pins. */ > + uint32_t pins_per_bank; Maybe use an array instead. This can also reduce the need for all the arithmetic computing bank and pin from pin_number.. if you can just index directly into a struct containing all that information? What would be the expected cost in size to do it like this? > +} rtems_gpio_layout; > + > +/** > + * @brief Object containing configuration information > + * regarding interrupts. > + */ > +typedef struct > +{ > + rtems_gpio_interrupt enabled_interrupt; > + > + rtems_gpio_handler_flag handler_flag; > + > + /* Interrupt handler function. */ > + rtems_gpio_irq_state (*handler) (void *arg); > + > + /* Interrupt handler function arguments. */ > + void *arg; > + > + /* Software switch debounce settings. It should contain the amount of clock > + * ticks that must pass between interrupts to ensure that the interrupt > + * was not caused by a switch bounce. If set to 0 this feature is disabled > . */ > + uint32_t clock_tick_interval; perhaps put debounce in the name to make it more clear in the code. > +} rtems_gpio_interrupt_conf; spell out configuration > + > +/** > + * @brief Object containing configuration information > + * to request/update a GPIO pin. > + */ > +typedef struct > +{ > + uint32_t pin_number; > + rtems_gpio_function function; > + > + /* Pull resistor setting. */ > + rtems_gpio_pull_mode pull_mode; > + > + /* If digital out pin, set to TRUE to set the pin to logical high, > + * or FALSE for logical low. If not a digital out then this > + * is ignored. */ > + bool output_enabled; > + bool logic_invert; > + > + /* Pin interrupt configuration. Should be NULL if not used. */ > + rtems_gpio_interrupt_conf* interrupt; We normally put the * with the variable name. > + > + /* Struct with bsp specific data, to use during the pin request. > + * If function == BSP_SPECIFIC this should have a pointer to > + * a rtems_gpio_specific_data struct. > + * > + * If not this field may be NULL. This is passed to the bsp function so > any bsp specific data > + * can be passed to it through this pointer. */ > + void* bsp_specific; > +} rtems_gpio_pin_conf; > + > +/** @} */ > + > +/** > + * @name gpio Usage > + * > + * @{ > + */ > + > +/** > + * @brief Initializes the GPIO API. > + * > + * @retval RTEMS_SUCCESSFUL API successfully initialized. > + * @retval * For other error code see @rtems_semaphore_create(). > + */ > +extern rtems_status_code rtems_gpio_initialize(void); > + > +/** > + * @brief Requests a GPIO pin configuration from the API. remove "from the API". > + * It may be used either to request a new pin, or to update the > + * configuration/functions of a pin currently in use. > + * > + * @param[in] conf rtems_gpio_pin_conf structure filled with the pin > information > + * and desired configurations. > + * > + * @retval RTEMS_SUCCESSFUL Pin was configured successfully. > + * @retval RTEMS_UNSATISFIED Could not request/update the pin's > configuration. > + */ > +extern rtems_status_code rtems_gpio_request_conf(rtems_gpio_pin_conf* conf); > + > +/** > + * @brief Sets multiple output GPIO pins with the logical high. > + * > + * @param[in] count Number of GPIO pins to set. > + * @param[in] ... Comma-separated list of GPIO pin numbers. > + * > + * @retval RTEMS_SUCCESSFUL All pins were set successfully. > + * @retval RTEMS_INVALID_ID At least one pin number is invalid. > + * @retval RTEMS_NOT_CONFIGURED At least one of the received pins > + * is not configured as a digital output. > + * @retval RTEMS_UNSATISFIED Could not set the GPIO pins. > + */ > +extern rtems_status_code rtems_gpio_multi_set(uint32_t count, ...); > + > +/** > + * @brief Sets multiple output GPIO pins with the logical low. > + * > + * @param[in] count Number of GPIO pins to clear. > + * @param[in] ... Comma-separated list of GPIO pin numbers. > + * > + * @retval RTEMS_SUCCESSFUL All pins were cleared successfully. > + * @retval RTEMS_INVALID_ID At least one pin number is invalid. > + * @retval RTEMS_NOT_CONFIGURED At least one of the received pins > + * is not configured as a digital output. > + * @retval RTEMS_UNSATISFIED Could not clear the GPIO pins. > + */ > +extern rtems_status_code rtems_gpio_multi_clear(uint32_t count, ...); > + > +/** > + * @brief Sets an output GPIO pin with the logical high. > + * > + * @param[in] pin_number GPIO pin number. > + * > + * @retval RTEMS_SUCCESSFUL Pin was set successfully. > + * @retval RTEMS_INVALID_ID Pin number is invalid. > + * @retval RTEMS_NOT_CONFIGURED The received pin is not configured > + * as a digital output. > + * @retval RTEMS_UNSATISFIED Could not set the GPIO pin. > + */ > +extern rtems_status_code rtems_gpio_set(uint32_t pin_number); > + > +/** > + * @brief Sets an output GPIO pin with the logical low. > + * > + * @param[in] pin_number GPIO pin number. > + * > + * @retval RTEMS_SUCCESSFUL Pin was cleared successfully. > + * @retval RTEMS_INVALID_ID Pin number is invalid. > + * @retval RTEMS_NOT_CONFIGURED The received pin is not configured > + * as a digital output. > + * @retval RTEMS_UNSATISFIED Could not clear the GPIO pin. > + */ > +extern rtems_status_code rtems_gpio_clear(uint32_t pin_number); > + > +/** > + * @brief Returns the value (level) of a GPIO input pin. > + * > + * @param[in] pin_number GPIO pin number. > + * > + * @retval The function returns 0 or 1 depending on the pin current > + * logical value. > + * @retval -1 Pin number is invalid, or not a digital input pin. > + */ > +extern int rtems_gpio_get_value(uint32_t pin_number); > + > +/** > + * @brief Assigns a certain function to a GPIO pin. > + * > + * @param[in] pin_number GPIO pin number. > + * @param[in] function The new function for the pin. > + * @param[in] output_enabled If TRUE and @var function is DIGITAL_OUTPUT, > + * then the pin is set with the logical high. > + * Otherwise it is set with logical low. > + * @param[in] logic_invert Reverses the digital I/O logic for DIGITAL_INPUT > + * and DIGITAL_OUTPUT pins. > + * @param[in] bsp_specific Pointer to a bsp defined structure with > bsp-specific > + * data. This field is not handled by the API. > + * > + * @retval RTEMS_SUCCESSFUL Pin was configured successfully. > + * @retval RTEMS_INVALID_ID Pin number is invalid. > + * @retval RTEMS_RESOURCE_IN_USE The received pin is already being used. > + * @retval RTEMS_UNSATISFIED Could not assign the GPIO function. > + * @retval RTEMS_NOT_DEFINED GPIO function not defined, or NOT_USED. > + */ > +extern rtems_status_code rtems_gpio_request_pin(uint32_t pin_number, > rtems_gpio_function function, bool output_enable, bool logic_invert, void* > bsp_specific); > + > +/** > + * @brief Configures a single GPIO pin pull resistor. > + * > + * @param[in] pin_number GPIO pin number. > + * @param[in] mode The pull resistor mode. > + * > + * @retval RTEMS_SUCCESSFUL Pull resistor successfully configured. > + * @retval RTEMS_INVALID_ID Pin number is invalid. > + * @retval RTEMS_UNSATISFIED Could not set the pull mode. > + */ > +extern rtems_status_code rtems_gpio_resistor_mode(uint32_t pin_number, > rtems_gpio_pull_mode mode); > + > +/** > + * @brief Releases a GPIO pin from the API, making it available to be used ditto. > + * again. > + * > + * @param[in] pin_number GPIO pin number. > + * > + * @retval RTEMS_SUCCESSFUL Pin successfully disabled on the API. delete "on the API" > + * @retval RTEMS_INVALID_ID Pin number is invalid. > + * @retval * Could not disable an ative interrupt on this pin, typo: active > + * @see rtems_gpio_disable_interrupt(). > + */ > +extern rtems_status_code rtems_gpio_release_pin(uint32_t pin_number); > + > +/** > + * @brief Attaches a debouncing function to a given pin/switch. > + * Debouncing is done by requiring a certain number of clock ticks to > + * pass between interrupts. Any interrupt fired too close to the last > + * will be ignored as it is probably the result of an involuntary > + * switch/button bounce after being released. > + * > + * @param[in] pin_number GPIO pin number. > + * @param[in] ticks Minimum number of clock ticks that must pass between > + * interrupts so it can be considered a legitimate > + * interrupt. > + * > + * @retval RTEMS_SUCCESSFUL De-bounce function successfully attached to the > pin. > + * @retval RTEMS_INVALID_ID Pin number is invalid. > + * @retval RTEMS_NOT_CONFIGURED The current pin is not configured as a > digital > + * input, hence it can not be connected to a > switch. > + */ > +extern rtems_status_code rtems_gpio_debounce_switch(uint32_t pin_number, int > ticks); > + > +/** > + * @brief Connects a new user-defined interrupt handler to a given pin. > + * > + * @param[in] pin_number GPIO pin number. > + * @param[in] handler Pointer to a function that will be called every time > + * the enabled interrupt for the given pin is generated. > + * This function must return information about its > + * handled/unhandled state. > + * @param[in] arg Void pointer to the arguments of the user-defined handler. > + * > + * @retval RTEMS_SUCCESSFUL Handler successfully connected to this pin. > + * @retval RTEMS_NO_MEMORY Could not connect more user-defined handlers to > + * the given pin. > + * @retval RTEMS_NOT_CONFIGURED The given pin has no interrupt configured. > + * @retval RTEMS_INVALID_ID Pin number is invalid. > + * @retval RTEMS_RESOURCE_IN_USE The current user-defined handler for this > pin > + * is unique. > + */ > +extern rtems_status_code rtems_gpio_interrupt_handler_install( > +uint32_t pin_number, > +rtems_gpio_irq_state (*handler) (void *arg), > +void *arg > +); > + > +/** > + * @brief Enables interrupts to be generated on a given GPIO pin. > + * When fired that interrupt will call the given handler. > + * > + * @param[in] pin_number GPIO pin number. > + * @param[in] interrupt Type of interrupt to enable for the pin. > + * @param[in] handler Pointer to a function that will be called every time > + * @var interrupt is generated. This function must return > + * information about its handled/unhandled state. > + * @param[in] arg Void pointer to the arguments of the user-defined handler. > + * > + * @retval RTEMS_SUCCESSFUL Interrupt successfully enabled for this pin. > + * @retval RTEMS_UNSATISFIED Could not install the GPIO ISR, create/start > + * the handler task, or enable the interrupt > + * on the pin. > + * @retval RTEMS_INVALID_ID Pin number is invalid. > + * @retval RTEMS_RESOURCE_IN_USE The pin already has an enabled interrupt. > + */ > +extern rtems_status_code rtems_gpio_enable_interrupt( > +uint32_t pin_number, > +rtems_gpio_interrupt interrupt, > +rtems_gpio_handler_flag flag, > +rtems_gpio_irq_state (*handler) (void *arg), > +void *arg > +); > + > +/** > + * @brief Disconnects an user-defined interrupt handler from the given pin. > + * If in the end there are no more user-defined handlers connected > + * to the pin, interrupts are disabled on the given pin. > + * > + * @param[in] pin_number GPIO pin number. > + * @param[in] handler Pointer to the user-defined handler > + * @param[in] arg Void pointer to the arguments of the user-defined handler. > + * > + * @retval RTEMS_SUCCESSFUL Handler successfully disconnected from this pin. > + * @retval RTEMS_INVALID_ID Pin number is invalid. > + * @retval * @see rtems_gpio_disable_interrupt() > + */ > +rtems_status_code rtems_gpio_interrupt_handler_remove( > +uint32_t pin_number, > +rtems_gpio_irq_state (*handler) (void *arg), > +void *arg > +); > + > +/** > + * @brief Stops interrupts from being generated on a given GPIO pin > + * and removes the corresponding handler. > + * > + * @param[in] pin_number GPIO pin number. > + * > + * @retval RTEMS_SUCCESSFUL Interrupt successfully disabled for this pin. > + * @retval RTEMS_INVALID_ID Pin number is invalid. > + * @retval RTEMS_UNSATISFIED Could not remove the current interrupt handler, > + * could not recognise the current active interrupt > + * on this pin or could not disable interrupts on > + * this pin. > + */ > +extern rtems_status_code rtems_gpio_disable_interrupt(uint32_t pin_number); > + > +/** > + * @brief Defines the GPIO pin layout. This must be implemented by each BSP. > + */ > +extern rtems_gpio_layout rtems_bsp_gpio_initialize(void); > + This should perhaps return a status, and have layout filled in as a side-effect, passing it as a pointer rather than getting a struct returned. > +/** > + * @brief Sets multiple output GPIO pins with the logical high. This must be > implemented > + * by each BSP. > + * > + * @param[in] bank GPIO bank number. > + * @param[in] bitmask Bitmask of GPIO pins to set in the given bank. > + * > + * @retval RTEMS_SUCCESSFUL All pins were set successfully. > + * @retval RTEMS_UNSATISFIED Could not set at least one of the pins. > + */ > +extern rtems_status_code rtems_bsp_gpio_multi_set(uint32_t bank, uint32_t > bitmask); > + > +/** > + * @brief Sets multiple output GPIO pins with the logical low. This must be > implemented > + * by each BSP. > + * > + * @param[in] bank GPIO bank number. > + * @param[in] bitmask Bitmask of GPIO pins to clear in the given bank. > + * > + * @retval RTEMS_SUCCESSFUL All pins were cleared successfully. > + * @retval RTEMS_UNSATISFIED Could not clear at least one of the pins. > + */ > +extern rtems_status_code rtems_bsp_gpio_multi_clear(uint32_t bank, uint32_t > bitmask); > + > +/** > + * @brief Sets an output GPIO pin with the logical high. This must be > implemented > + * by each BSP. > + * > + * @param[in] bank GPIO bank number. > + * @param[in] pin GPIO pin number within the given bank. > + * > + * @retval RTEMS_SUCCESSFUL Pin was set successfully. > + * @retval RTEMS_UNSATISFIED Could not set the given pin. > + */ > +extern rtems_status_code rtems_bsp_gpio_set(uint32_t bank, uint32_t pin); > + > +/** > + * @brief Sets an output GPIO pin with the logical low. This must be > implemented > + * by each BSP. > + * > + * @param[in] bank GPIO bank number. > + * @param[in] pin GPIO pin number within the given bank. > + * > + * @retval RTEMS_SUCCESSFUL Pin was cleared successfully. > + * @retval RTEMS_UNSATISFIED Could not clear the given pin. > + */ > +extern rtems_status_code rtems_bsp_gpio_clear(uint32_t bank, uint32_t pin); > + > +/** > + * @brief Returns the value (level) of a GPIO input pin. This must be > implemented > + * by each BSP. > + * > + * @param[in] bank GPIO bank number. > + * @param[in] pin GPIO pin number within the given bank. > + * > + * @retval The function must return 0 or 1 depending on the pin current > + * logical value. > + * @retval -1 Could not read the pin level. > + */ > +extern int rtems_bsp_gpio_get_value(uint32_t bank, uint32_t pin); > + > +/** > + * @brief Assigns the digital input function to the given pin. > + * This must be implemented by each BSP. > + * > + * @param[in] bank GPIO bank number. > + * @param[in] pin GPIO pin number within the given bank. > + * @param[in] bsp_specific Pointer to a bsp defined structure with > bsp-specific > + * data. > + * > + * @retval RTEMS_SUCCESSFUL Function was asssigned successfully. > + * @retval RTEMS_UNSATISFIED Could not assign the function to the pin. > + */ > +extern rtems_status_code rtems_bsp_gpio_select_input(uint32_t bank, uint32_t > pin, void* bsp_specific); > + > +/** > + * @brief Assigns the digital output function to the given pin. > + * This must be implemented by each BSP. > + * > + * @param[in] bank GPIO bank number. > + * @param[in] pin GPIO pin number within the given bank. > + * @param[in] bsp_specific Pointer to a bsp defined structure with > bsp-specific > + * data. > + * > + * @retval RTEMS_SUCCESSFUL Function was asssigned successfully. > + * @retval RTEMS_UNSATISFIED Could not assign the function to the pin. > + */ > +extern rtems_status_code rtems_bsp_gpio_select_output(uint32_t bank, > uint32_t pin, void* bsp_specific); > + > +/** > + * @brief Assigns a bsp specific function to the given pin. > + * This must be implemented by each BSP. > + * > + * @param[in] bank GPIO bank number. > + * @param[in] pin GPIO pin number within the given bank. > + * @param[in] function Bsp defined GPIO function. > + * @param[in] pin_data Pointer to a bsp defined structure with bsp-specific > + * data. > + * > + * @retval RTEMS_SUCCESSFUL Function was asssigned successfully. > + * @retval RTEMS_UNSATISFIED Could not assign the function to the pin. > + */ > +extern rtems_status_code rtems_bsp_select_specific_io(uint32_t bank, > uint32_t pin, uint32_t function, void* pin_data); > + > +/** > + * @brief Configures a single GPIO pin pull resistor. > + * This must be implemented by each BSP. > + * > + * @param[in] bank GPIO bank number. > + * @param[in] pin GPIO pin number within the given bank. > + * @param[in] mode The pull resistor mode. > + * > + * @retval RTEMS_SUCCESSFUL Pull resistor successfully configured. > + * @retval RTEMS_UNSATISFIED Could not set the pull mode. > + */ > +extern rtems_status_code rtems_bsp_gpio_set_resistor_mode(uint32_t bank, > uint32_t pin, rtems_gpio_pull_mode mode); > + > +/** > + * @brief Reads and returns a vector/bank interrupt event line. > + * This must be implemented by each BSP. > + * > + * @param[in] vector GPIO vector/bank. > + * > + * @retval Bitmask (max 32-bit) representing a GPIO bank, where a bit set > + * indicates an active interrupt on that pin. > + */ > +extern uint32_t rtems_bsp_gpio_interrupt_line(rtems_vector_number vector); > + > +/** > + * @brief Clears a vector/bank interrupt event line. > + * This must be implemented by each BSP. > + * > + * @param[in] vector GPIO vector/bank. > + * @param[in] event_status Bitmask with the processed interrupts on the given > + * vector. This bitmask is the same as calculated in > + * @see rtems_bsp_gpio_interrupt_line(). > + */ > +extern void rtems_bsp_gpio_clear_interrupt_line(rtems_vector_number vector, > uint32_t event_status); > + > +/** > + * @brief Calculates a vector number for a given GPIO bank. > + * This must be implemented by each BSP. > + * > + * @param[in] bank GPIO bank number. > + * > + * @retval The corresponding rtems_vector_number. > + */ > +extern rtems_vector_number rtems_bsp_gpio_get_vector(uint32_t bank); > + > +/** > + * @brief Enables interrupts to be generated on a given GPIO pin. > + * This must be implemented by each BSP. > + * > + * @param[in] bank GPIO bank number. > + * @param[in] pin GPIO pin number within the given bank. > + * @param[in] interrupt Type of interrupt to enable for the pin. > + * > + * @retval RTEMS_SUCCESSFUL Interrupt successfully enabled for this pin. > + * @retval RTEMS_UNSATISFIED Could not enable the interrupt on the pin. > + */ > +extern rtems_status_code rtems_bsp_enable_interrupt(uint32_t bank, uint32_t > pin, rtems_gpio_interrupt interrupt); > + > +/** > + * @brief Disables interrupt on hardware. This must be implemented by each > BSP. > + */ > + > +/** > + * @brief Stops interrupts from being generated on a given GPIO pin. > + * This must be implemented by each BSP. > + * > + * @param[in] bank GPIO bank number. > + * @param[in] pin GPIO pin number within the given bank. > + * @param[in] enabled_interrupt Interrupt type currently active on this pin. > + * > + * @retval RTEMS_SUCCESSFUL Interrupt successfully disabled for this pin. > + * @retval RTEMS_UNSATISFIED Could not disable interrupts on this pin. > + */ > +extern rtems_status_code rtems_bsp_disable_interrupt(uint32_t bank, uint32_t > pin, rtems_gpio_interrupt enabled_interrupt); > + > +/** @} */ > + > +#ifdef __cplusplus > +} > +#endif /* __cplusplus */ > + > +#endif /* LIBBSP_SHARED__GPIO_H */ > -- > 2.0.5 > > _______________________________________________ > devel mailing list > devel@rtems.org > http://lists.rtems.org/mailman/listinfo/devel _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel