[PATCH 6/9] gpiolib: use descriptors internally

Linus Walleij linus.walleij at linaro.org
Tue Feb 5 12:53:05 EST 2013


On Sat, Feb 2, 2013 at 5:29 PM, Alexandre Courbot <acourbot at nvidia.com> wrote:

> Make sure gpiolib works internally with descriptors and (chip, offset)
> pairs instead of using the global integer namespace. This prepares the

Its a numberspace not a namespace right?

> ground for the removal of the global gpio_desc[] array and the
> introduction of the descriptor-based GPIO API.
>
> Signed-off-by: Alexandre Courbot <acourbot at nvidia.com>

> +/*
> + * Internal gpiod_* API using descriptors instead of the integer namespace.
> + * Most of this should eventually go public.
> + */
> +static int gpiod_request(struct gpio_desc *desc, const char *label);
> +static void gpiod_free(struct gpio_desc *desc);
> +static int gpiod_direction_input(struct gpio_desc *desc);
> +static int gpiod_direction_output(struct gpio_desc *desc, int value);
> +static int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);
> +static int gpiod_get_value_cansleep(struct gpio_desc *desc);
> +static void gpiod_set_value_cansleep(struct gpio_desc *desc, int value);
> +static int gpiod_get_value(struct gpio_desc *desc);
> +static void gpiod_set_value(struct gpio_desc *desc, int value);
> +static int gpiod_cansleep(struct gpio_desc *desc);
> +static int gpiod_to_irq(struct gpio_desc *desc);
> +static int gpiod_export(struct gpio_desc *desc, bool direction_may_change);
> +static int gpiod_export_link(struct device *dev, const char *name,
> +                            struct gpio_desc *desc);
> +static int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value);
> +static void gpiod_unexport(struct gpio_desc *desc);

Usually I don't like these upfrons forward-declarations, but in this
case I *do* because they are here for a reason, to later become
extern. So I like it!

> +/*
> + * Return the GPIO number of the passed descriptor relative to its chip
> + */
> +static int gpio_chip_hwgpio(const struct gpio_desc *desc)
> +{
> +       return (desc - &gpio_desc[0]) - desc->chip->base;
> +}

That was a scary method. But it works as long as the
descriptors are in a static array I guess...

> +/**
> + * Convert a GPIO number to its descriptor
> + */
> +static struct gpio_desc *gpio_to_desc(unsigned gpio)
> +{
> +       if (WARN(!gpio_is_valid(gpio), "invalid GPIO %d\n", gpio))
> +               return NULL;

Don't we want to return ERR_PTR(-EINVAL); here?

Then you can use IS_ERR() on the pointers later.

This is the approach taken by the external API for clk
and pins.

> +/**
> + * Convert a GPIO descriptor to the integer namespace.
> + * This should disappear in the future but is needed since we still
> + * use GPIO numbers for error messages and sysfs nodes
> + */
> +static int desc_to_gpio(const struct gpio_desc *desc)
> +{
> +       return desc - &gpio_desc[0];
> +}

Aha OK the scary stuff goes away. Good...

> +
> +

You can never get enough whitespace ;-)

>  /* caller holds gpio_lock *OR* gpio is marked as requested */

That comment should be above the *next* function right?

Strictly speaking it does not apply to gpiod_to_chip() if
I read it right.

> +static struct gpio_chip *gpiod_to_chip(struct gpio_desc *desc)
> +{
> +       return desc->chip;
> +}
> +
>  struct gpio_chip *gpio_to_chip(unsigned gpio)
>  {
> -       return gpio_desc[gpio].chip;
> +       return gpiod_to_chip(gpio_to_desc(gpio));
>  }

...Then follows lots of nice stuff...

(...)
>  static ssize_t gpio_direction_show(struct device *dev,
>                 struct device_attribute *attr, char *buf)
>  {
> -       const struct gpio_desc  *desc = dev_get_drvdata(dev);
> -       unsigned                gpio = desc - gpio_desc;
> +       struct gpio_desc        *desc = dev_get_drvdata(dev);

Why not const anymore?

(Applies to all these similar cases in the patch.)

consting is nice. Especially in subsystem code.
I know it is hard to get compilation right without warnings
at times. But it pays off.

In the pinctrl subsystem I spend endless hours reading this
wiki page:

http://en.wikipedia.org/wiki/Const-correctness

I still don't quite get it. And it also wears off. But I like to use it.

> @@ -594,29 +647,32 @@ static ssize_t export_store(struct class *class,
> +       desc = gpio_to_desc(gpio);

I hope you have tested this hunk of the patch from userspace?

> @@ -637,17 +694,18 @@ static ssize_t unexport_store(struct class *class,

This too?

(etc for the userspace interfaces)

> @@ -1538,48 +1622,54 @@ int gpio_direction_input(unsigned gpio)
>                 }
>         }
>
> -       status = chip->direction_input(chip, gpio);
> +       status = chip->direction_input(chip, offset);
>         if (status == 0)
>                 clear_bit(FLAG_IS_OUT, &desc->flags);
>
> -       trace_gpio_direction(chip->base + gpio, 1, status);
> +       trace_gpio_direction(desc_to_gpio(desc), 1, status);
>  lose:
>         return status;
>  fail:
>         spin_unlock_irqrestore(&gpio_lock, flags);
> -       if (status)
> +       if (status) {
> +               int gpio = -1;
> +               if (desc)
> +                       gpio = desc_to_gpio(desc);
>                 pr_debug("%s: gpio-%d status %d\n",
>                         __func__, gpio, status);
> +       }
>         return status;
>  }

So using ERR_PTR/PTR_ERR helps you propagate
errors in situations like these.

Just:

if (IS_ERR(desc))
    status = PTR_ERR(desc);

> -int gpio_direction_output(unsigned gpio, int value)
> +static int gpiod_direction_output(struct gpio_desc *desc, int value)
>  {
>         unsigned long           flags;
>         struct gpio_chip        *chip;
> -       struct gpio_desc        *desc = &gpio_desc[gpio];
>         int                     status = -EINVAL;
> +       int offset;

Use hwgpio?


>         /* Open drain pin should not be driven to 1 */
>         if (value && test_bit(FLAG_OPEN_DRAIN,  &desc->flags))
> -               return gpio_direction_input(gpio);
> +               return gpiod_direction_input(desc);
>
>         /* Open source pin should not be driven to 0 */
>         if (!value && test_bit(FLAG_OPEN_SOURCE,  &desc->flags))
> -               return gpio_direction_input(gpio);
> +               return gpiod_direction_input(desc);
>
>         spin_lock_irqsave(&gpio_lock, flags);
>
> -       if (!gpio_is_valid(gpio))
> +       if (!desc)

if (IS_ERR(desc)) ?

> @@ -1589,11 +1679,12 @@ int gpio_direction_output(unsigned gpio, int value)
>
>         might_sleep_if(chip->can_sleep);
>
> +       offset = gpio_chip_hwgpio(desc);

Maybe rename to hwgpio? Or is that done later?

We should stick with either hwgpio or offset everywhere or
it will be a mess.

(...)
>  fail:
>         spin_unlock_irqrestore(&gpio_lock, flags);
> -       if (status)
> +       if (status) {
> +               int gpio = -1;
> +               if (desc)
> +                       gpio = desc_to_gpio(desc);
>                 pr_debug("%s: gpio-%d status %d\n",
>                         __func__, gpio, status);
> +       }
>         return status;

Again IS_ERR()/ERR_PTR(). -1 is not nice.

>  /**
> @@ -1622,24 +1722,22 @@ EXPORT_SYMBOL_GPL(gpio_direction_output);
>   * @gpio: the gpio to set debounce time
>   * @debounce: debounce time is microseconds
>   */
> -int gpio_set_debounce(unsigned gpio, unsigned debounce)
> +static int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce)
>  {
>         unsigned long           flags;
>         struct gpio_chip        *chip;
> -       struct gpio_desc        *desc = &gpio_desc[gpio];
>         int                     status = -EINVAL;
> +       int                     offset;

hwgpio?

(then follows some repating cases)

(then some nice code)

> -int gpio_get_value_cansleep(unsigned gpio)
> +static int gpiod_get_value_cansleep(struct gpio_desc *desc)
>  {
>         struct gpio_chip        *chip;
>         int value;
> +       int offset;

hwgpio?

Basically the patch is very nice but I'd like you to iron out and proofread
as per above so we have strong consistency, strong const:ing,
and stringent naming (offset vs hwgpio come to mind).

Yours,
Linus Walleij



More information about the linux-arm-kernel mailing list