>>
>>
>>> +  uint32_t bank;
>>> +  int handled_count;
>>> +  int rv;
>>> +
>>> +  gpio = (gpio_pin*) arg;
>>> +
>>> +  bank = gpio->bank_number;
>>
>> Validate args for errors.
>
>
> These tasks are only created by the rtems_gpio_enable_interrupt() function,
> and all parameters are validated before creating/starting the task. Should
> they be validated again here?
>
No, as long as validated in the entry-ways to the module it should be fine.


>>> +
>>> +    AQUIRE_LOCK(bank_lock[bank]);
>>
>> What is the lock protecting?
>
>
> Since the rtems_gpio_disable_interrupt() kills this task when the interrupt
> is disabled, having the lock here (which rtems_gpio_disable_interrupt() must
> also acquire before doing anything) ensures that any interrupt disable call
> occurring during an interrupt process will have to wait while the on-going
> interrupt is processed. Otherwise the handler could leave the system on an
> inconsistent state. I shall make this clear with a comment.
>
Do we really want the task to be killed? Anyway, the naming of this
"gpio_disable_interrupt" and "gpio_enable_interrupt" is a bit
confusing to me, since it really is about creating/destroying the
interrupt handling thread? I don't quite understand the design there.


>>> +    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?
>
>
> As with generic_handler_task, these ISRs are only created by
> rtems_gpio_enable_interrupt() and rtems_gpio_disable_interrupt(), which
> validate the arguments sent here. Should they be re-validated here?
This is a little different, in that the code path gets reached
indirectly via interrupt handling. I recommand always verifying these
inputs, even if it is with debug asserts only.

>
> As for the gpio[0], the gpio pointer points to a whole gpio bank, so gpio[0]
> is the first pin of this bank. Since all pins in this bank have the same
> bank number, I can use any pin from it to obtain the bank number that this
> ISR is listening. Maybe gpio should be renamed to gpio_bank.
>
Ah. Something bugs me about redundantly storing the same bank number
32 times... Is there a better data structure for you to use to keep
track of all the pins and their metadata / configurations?


>>> +
>>> +  /* 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.)
>
>
> There may be bigger (up to 64?). But yes it would help to define a max pin
> number for a single bank, and have that asserted. Would it be best to set a
> max pin number per bank of 64 pins, and change this to a 64-bit bitmask to
> account for any number in-between? The reasoning behind the pin bitmask is
> that most platforms should be able to directly (or after some bitwise
> operations) write it to the registers.
>
Stick to 32.

>>> +  /* 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.
>
>
> maybe GPIO_INTERRUPT_EVENT?
>
Yes.

>>> +  }
>>> +
>>> +  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;
>
>
> I never know the bank number until the first iteration ( the i == 0 case
> bellow).
>
You can pre-compute this before the loop.




>>> +
>>> +    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?
>
>
> The API calculates the pin and bank numbers from the "global" pin number as
> a protection mechanism against a change in the GPIO layout. On a second
> thougth, I do not actually see a reason for it to change, unless there is an
> application where I would like to work with banks instead of individual
> pins, and would change the GPIO layout accordingly. But even in that case I
> could just create a separate conf file with those specific requirements (and
> maybe it can allow to define multiple pins with the same conf, to form a
> group that is to be treated as a single entity, if that can be a thing?).
Using configuration structs to abstract the pins/banks/groups-of-pins
makes sense to me. Having an efficient way to map from the pin number
to its configuration could solve the dilemma you face about wanting to
keep things simple. A linear index (array) works fine for 1:1
pin-to-conf. A tree (rbtree) works for sparse mappings. Or rely on the
application to keep track of the confs itself.

>
> One issue that arises with storing the bank and pin numbers directly is that
> it becomes more hardcoded. With a global pin number a bsp can define a pin
> with
>
> #define GPIO_40 40
>
> and use that to refer to that pin. By having to explicitly use the bank and
> pin numbers, I would have two numbers that would be hardcoded (and the
> separate conf file would apply here).
Or the application passes the configuration instead of the numbers.
Then the configuration can store things like pin/bank numbers.

>
> So yes, probably the bank and pin numbers can be directly stored. The other
> API calls can also receive the two numbers, or at least the
> rtems_gpio_request_pin could. If this function (and rtems_gpio_request_conf)
> would return a gpio_pin_id var/struct with the bank and pin number
> combination, the following calls would only use this gpio_pin_id, and could
> even avoid the bank/pin check if this type is opaque.
>
>>
>>> +
>>> +    AQUIRE_LOCK(bank_lock[bank]);
>>
>> And also store its own lock field?
>
>
> The lock field is per bank. Each conf struct only refer to an individual
> pin.
>
OK, or possibly a group of pins, fine. It could store a pointer to the
lock maybe, but this global array of locks is I suppose acceptable.


>>> +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?
>
>
> The rtems_bsp_gpio_get_value can return -1 if for some reason it failed to
> get a value. With the rv > 0 condition that would be sent to an application
> through the last return.
>
So in case of an error, the lock remains held? What does rv==0 signify?

>
>>> +    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?
>
>
> If the application indicates that this pin is to be enabled upon request, it
> should be enabled (set) regardless of the behavior of the next set/clears on
> that pin (which may be inverted, depending on the logic_invert flag). That
> is how I see it.
>
I don't know. It's a bit funny. If the pin is low-out
(logic_invert==true), then output_enabled means you want it pulled
low, no?


>>> +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?
>
>
> Indeed the gpio->handler_list is always NULL if empty, so yes this if block
> can be replaced with
>
> isr_node->next_isr = gpio->handler_list;
>
> Not sure if that would be more readable.
>
Realistically you should have helper functions for manipulating these
lists, which would make the code eminently more readable. Or use RTEMS
chains.

>>> +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?
>
>
> The handler task is to handle the (possible) many handlers an individual pin
> can have, if it acts as a shared IRQ line. What is shared across multiple
> pins is the generic_isr, which is as actual ISR routine (shared with all
> pins of a given bank).
>
I need to understand this handler task and generic_isr framework better.

>>> +
>>> +/**
>>> + * @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?
>
>
> You mean to replace the rtems_gpio_layout with an array of structs
> statically filled with the bank/pin combinations by each BSP? That does not
> seem very clean, specially since each all banks have the same number of pins
> (the last is the only possible exception), and the layout calculation is
> pretty easy/cheap.
>
> One way to improve the bank/pin situation would, maybe, be as follows:
>
> Replace the gpio_pin **gpio_pin_state matrix with an uni-dimensional array,
> such that (suposing pins_per_bank = 32) from the index 0 to 31 it would
> refer to pins in the first bank, from index 32 to 63 to pins from the second
> bank, and so on. This would allow to fetch the right GPIO pin using the
> pin_number directly.
>
> For the cases where I need to send a whole bank of pins, I could simply send
> a pointer to the index of the first pin of that bank. Then since I know how
> many pins each bank has I always know where the bank ends.
>
That might make sense. I'm just trying to think through the useful
options here. Before we get stuck with something in the API layer that
is inefficient to use.

Gedare
_______________________________________________
devel mailing list
devel@rtems.org
http://lists.rtems.org/mailman/listinfo/devel

Reply via email to