[PATCH 1/2] RTEMS GPIO API definition and implementation.

Gedare Bloom gedare at rtems.org
Mon Jun 22 15:05:11 UTC 2015


(


On Mon, Jun 22, 2015 at 8:01 AM, Andre Marques
<andre.lousa.marques at 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 at rtems.org
> http://lists.rtems.org/mailman/listinfo/devel



More information about the devel mailing list