gpiolib: provide gpio_device_find()

gpiochip_find() is wrong and its kernel doc is misleading as the
function doesn't return a reference to the gpio_chip but just a raw
pointer. The chip itself is not guaranteed to stay alive, in fact it can
be deleted at any point. Also: other than GPIO drivers themselves,
nobody else has any business accessing gpio_chip structs.

Provide a new gpio_device_find() function that returns a real reference
to the opaque gpio_device structure that is guaranteed to stay alive for
as long as there are active users of it.

Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
This commit is contained in:
Bartosz Golaszewski 2023-09-27 16:29:23 +02:00
parent 9e4555d1e5
commit cfe102f633
2 changed files with 56 additions and 18 deletions

View File

@ -1014,16 +1014,10 @@ void gpiochip_remove(struct gpio_chip *gc)
} }
EXPORT_SYMBOL_GPL(gpiochip_remove); EXPORT_SYMBOL_GPL(gpiochip_remove);
/** /*
* gpiochip_find() - iterator for locating a specific gpio_chip * FIXME: This will be removed soon.
* @data: data to pass to match function
* @match: Callback function to check gpio_chip
* *
* Similar to bus_find_device. It returns a reference to a gpio_chip as * This function is depracated, don't use.
* determined by a user supplied @match callback. The callback should return
* 0 if the device doesn't match and non-zero if it does. If the callback is
* non-zero, this function will return to the caller and not iterate over any
* more gpio_chips.
*/ */
struct gpio_chip *gpiochip_find(void *data, struct gpio_chip *gpiochip_find(void *data,
int (*match)(struct gpio_chip *gc, int (*match)(struct gpio_chip *gc,
@ -1031,21 +1025,62 @@ struct gpio_chip *gpiochip_find(void *data,
{ {
struct gpio_device *gdev; struct gpio_device *gdev;
struct gpio_chip *gc = NULL; struct gpio_chip *gc = NULL;
unsigned long flags;
spin_lock_irqsave(&gpio_lock, flags); gdev = gpio_device_find(data, match);
list_for_each_entry(gdev, &gpio_devices, list) if (gdev) {
if (gdev->chip && match(gdev->chip, data)) { gc = gdev->chip;
gc = gdev->chip; gpio_device_put(gdev);
break; }
}
spin_unlock_irqrestore(&gpio_lock, flags);
return gc; return gc;
} }
EXPORT_SYMBOL_GPL(gpiochip_find); EXPORT_SYMBOL_GPL(gpiochip_find);
/**
* gpio_device_find() - find a specific GPIO device
* @data: data to pass to match function
* @match: Callback function to check gpio_chip
*
* Returns:
* New reference to struct gpio_device.
*
* Similar to bus_find_device(). It returns a reference to a gpio_device as
* determined by a user supplied @match callback. The callback should return
* 0 if the device doesn't match and non-zero if it does. If the callback
* returns non-zero, this function will return to the caller and not iterate
* over any more gpio_devices.
*
* The callback takes the GPIO chip structure as argument. During the execution
* of the callback function the chip is protected from being freed. TODO: This
* actually has yet to be implemented.
*
* If the function returns non-NULL, the returned reference must be freed by
* the caller using gpio_device_put().
*/
struct gpio_device *gpio_device_find(void *data,
int (*match)(struct gpio_chip *gc,
void *data))
{
struct gpio_device *gdev;
/*
* Not yet but in the future the spinlock below will become a mutex.
* Annotate this function before anyone tries to use it in interrupt
* context like it happened with gpiochip_find().
*/
might_sleep();
guard(spinlock_irqsave)(&gpio_lock);
list_for_each_entry(gdev, &gpio_devices, list) {
if (gdev->chip && match(gdev->chip, data))
return gpio_device_get(gdev);
}
return NULL;
}
EXPORT_SYMBOL_GPL(gpio_device_find);
static int gpiochip_match_name(struct gpio_chip *gc, void *data) static int gpiochip_match_name(struct gpio_chip *gc, void *data)
{ {
const char *name = data; const char *name = data;

View File

@ -608,6 +608,9 @@ int devm_gpiochip_add_data_with_key(struct device *dev, struct gpio_chip *gc,
struct gpio_chip *gpiochip_find(void *data, struct gpio_chip *gpiochip_find(void *data,
int (*match)(struct gpio_chip *gc, void *data)); int (*match)(struct gpio_chip *gc, void *data));
struct gpio_device *gpio_device_find(void *data,
int (*match)(struct gpio_chip *gc, void *data));
struct gpio_device *gpio_device_get(struct gpio_device *gdev); struct gpio_device *gpio_device_get(struct gpio_device *gdev);
void gpio_device_put(struct gpio_device *gdev); void gpio_device_put(struct gpio_device *gdev);