summaryrefslogtreecommitdiffstats
path: root/drivers/gpio/gpiolib-cdev.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio/gpiolib-cdev.c')
-rw-r--r--drivers/gpio/gpiolib-cdev.c124
1 files changed, 67 insertions, 57 deletions
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index 5cca9e8803..5639abce6e 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -24,7 +24,6 @@
#include <linux/pinctrl/consumer.h>
#include <linux/poll.h>
#include <linux/rbtree.h>
-#include <linux/rwsem.h>
#include <linux/seq_file.h>
#include <linux/spinlock.h>
#include <linux/timekeeping.h>
@@ -61,11 +60,6 @@ static_assert(IS_ALIGNED(sizeof(struct gpio_v2_line_values), 8));
* interface to gpiolib GPIOs via ioctl()s.
*/
-typedef __poll_t (*poll_fn)(struct file *, struct poll_table_struct *);
-typedef long (*ioctl_fn)(struct file *, unsigned int, unsigned long);
-typedef ssize_t (*read_fn)(struct file *, char __user *,
- size_t count, loff_t *);
-
/*
* GPIO line handle management
*/
@@ -95,6 +89,10 @@ struct linehandle_state {
GPIOHANDLE_REQUEST_OPEN_DRAIN | \
GPIOHANDLE_REQUEST_OPEN_SOURCE)
+#define GPIOHANDLE_REQUEST_DIRECTION_FLAGS \
+ (GPIOHANDLE_REQUEST_INPUT | \
+ GPIOHANDLE_REQUEST_OUTPUT)
+
static int linehandle_validate_flags(u32 flags)
{
/* Return an error if an unknown flag is set */
@@ -175,21 +173,21 @@ static long linehandle_set_config(struct linehandle_state *lh,
if (ret)
return ret;
+ /* Lines must be reconfigured explicitly as input or output. */
+ if (!(lflags & GPIOHANDLE_REQUEST_DIRECTION_FLAGS))
+ return -EINVAL;
+
for (i = 0; i < lh->num_descs; i++) {
desc = lh->descs[i];
- linehandle_flags_to_desc_flags(gcnf.flags, &desc->flags);
+ linehandle_flags_to_desc_flags(lflags, &desc->flags);
- /*
- * Lines have to be requested explicitly for input
- * or output, else the line will be treated "as is".
- */
if (lflags & GPIOHANDLE_REQUEST_OUTPUT) {
int val = !!gcnf.default_values[i];
ret = gpiod_direction_output(desc, val);
if (ret)
return ret;
- } else if (lflags & GPIOHANDLE_REQUEST_INPUT) {
+ } else {
ret = gpiod_direction_input(desc);
if (ret)
return ret;
@@ -210,9 +208,9 @@ static long linehandle_ioctl(struct file *file, unsigned int cmd,
unsigned int i;
int ret;
- guard(rwsem_read)(&lh->gdev->sem);
+ guard(srcu)(&lh->gdev->srcu);
- if (!lh->gdev->chip)
+ if (!rcu_access_pointer(lh->gdev->chip))
return -ENODEV;
switch (cmd) {
@@ -337,7 +335,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
/* Request each GPIO */
for (i = 0; i < handlereq.lines; i++) {
u32 offset = handlereq.lineoffsets[i];
- struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset);
+ struct gpio_desc *desc = gpio_device_get_desc(gdev, offset);
if (IS_ERR(desc)) {
ret = PTR_ERR(desc);
@@ -1536,12 +1534,14 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
line = &lr->lines[i];
desc = lr->lines[i].desc;
flags = gpio_v2_line_config_flags(&lc, i);
- gpio_v2_line_config_flags_to_desc_flags(flags, &desc->flags);
- edflags = flags & GPIO_V2_LINE_EDGE_DETECTOR_FLAGS;
/*
- * Lines have to be requested explicitly for input
- * or output, else the line will be treated "as is".
+ * Lines not explicitly reconfigured as input or output
+ * are left unchanged.
*/
+ if (!(flags & GPIO_V2_LINE_DIRECTION_FLAGS))
+ continue;
+ gpio_v2_line_config_flags_to_desc_flags(flags, &desc->flags);
+ edflags = flags & GPIO_V2_LINE_EDGE_DETECTOR_FLAGS;
if (flags & GPIO_V2_LINE_FLAG_OUTPUT) {
int val = gpio_v2_line_config_output_value(&lc, i);
@@ -1549,7 +1549,7 @@ static long linereq_set_config(struct linereq *lr, void __user *ip)
ret = gpiod_direction_output(desc, val);
if (ret)
return ret;
- } else if (flags & GPIO_V2_LINE_FLAG_INPUT) {
+ } else {
ret = gpiod_direction_input(desc);
if (ret)
return ret;
@@ -1572,9 +1572,9 @@ static long linereq_ioctl(struct file *file, unsigned int cmd,
struct linereq *lr = file->private_data;
void __user *ip = (void __user *)arg;
- guard(rwsem_read)(&lr->gdev->sem);
+ guard(srcu)(&lr->gdev->srcu);
- if (!lr->gdev->chip)
+ if (!rcu_access_pointer(lr->gdev->chip))
return -ENODEV;
switch (cmd) {
@@ -1603,9 +1603,9 @@ static __poll_t linereq_poll(struct file *file,
struct linereq *lr = file->private_data;
__poll_t events = 0;
- guard(rwsem_read)(&lr->gdev->sem);
+ guard(srcu)(&lr->gdev->srcu);
- if (!lr->gdev->chip)
+ if (!rcu_access_pointer(lr->gdev->chip))
return EPOLLHUP | EPOLLERR;
poll_wait(file, &lr->wait, wait);
@@ -1625,9 +1625,9 @@ static ssize_t linereq_read(struct file *file, char __user *buf,
ssize_t bytes_read = 0;
int ret;
- guard(rwsem_read)(&lr->gdev->sem);
+ guard(srcu)(&lr->gdev->srcu);
- if (!lr->gdev->chip)
+ if (!rcu_access_pointer(lr->gdev->chip))
return -ENODEV;
if (count < sizeof(le))
@@ -1791,7 +1791,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip)
/* Request each GPIO */
for (i = 0; i < ulr.num_lines; i++) {
u32 offset = ulr.offsets[i];
- struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset);
+ struct gpio_desc *desc = gpio_device_get_desc(gdev, offset);
if (IS_ERR(desc)) {
ret = PTR_ERR(desc);
@@ -1926,9 +1926,9 @@ static __poll_t lineevent_poll(struct file *file,
struct lineevent_state *le = file->private_data;
__poll_t events = 0;
- guard(rwsem_read)(&le->gdev->sem);
+ guard(srcu)(&le->gdev->srcu);
- if (!le->gdev->chip)
+ if (!rcu_access_pointer(le->gdev->chip))
return EPOLLHUP | EPOLLERR;
poll_wait(file, &le->wait, wait);
@@ -1964,9 +1964,9 @@ static ssize_t lineevent_read(struct file *file, char __user *buf,
ssize_t ge_size;
int ret;
- guard(rwsem_read)(&le->gdev->sem);
+ guard(srcu)(&le->gdev->srcu);
- if (!le->gdev->chip)
+ if (!rcu_access_pointer(le->gdev->chip))
return -ENODEV;
/*
@@ -2047,9 +2047,9 @@ static long lineevent_ioctl(struct file *file, unsigned int cmd,
void __user *ip = (void __user *)arg;
struct gpiohandle_data ghd;
- guard(rwsem_read)(&le->gdev->sem);
+ guard(srcu)(&le->gdev->srcu);
- if (!le->gdev->chip)
+ if (!rcu_access_pointer(le->gdev->chip))
return -ENODEV;
/*
@@ -2176,7 +2176,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
lflags = eventreq.handleflags;
eflags = eventreq.eventflags;
- desc = gpiochip_get_desc(gdev->chip, offset);
+ desc = gpio_device_get_desc(gdev, offset);
if (IS_ERR(desc))
return PTR_ERR(desc);
@@ -2356,21 +2356,26 @@ static void gpio_v2_line_info_changed_to_v1(
static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
struct gpio_v2_line_info *info)
{
- struct gpio_chip *gc = desc->gdev->chip;
unsigned long dflags;
+ const char *label;
+
+ CLASS(gpio_chip_guard, guard)(desc);
+ if (!guard.gc)
+ return;
memset(info, 0, sizeof(*info));
info->offset = gpio_chip_hwgpio(desc);
- scoped_guard(spinlock_irqsave, &gpio_lock) {
- if (desc->name)
- strscpy(info->name, desc->name, sizeof(info->name));
+ if (desc->name)
+ strscpy(info->name, desc->name, sizeof(info->name));
- if (desc->label)
- strscpy(info->consumer, desc->label,
- sizeof(info->consumer));
+ dflags = READ_ONCE(desc->flags);
- dflags = READ_ONCE(desc->flags);
+ scoped_guard(srcu, &desc->gdev->desc_srcu) {
+ label = gpiod_get_label(desc);
+ if (label && test_bit(FLAG_REQUESTED, &dflags))
+ strscpy(info->consumer, label,
+ sizeof(info->consumer));
}
/*
@@ -2390,8 +2395,8 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
test_bit(FLAG_USED_AS_IRQ, &dflags) ||
test_bit(FLAG_EXPORT, &dflags) ||
test_bit(FLAG_SYSFS, &dflags) ||
- !gpiochip_line_is_valid(gc, info->offset) ||
- !pinctrl_gpio_can_use_line(gc, info->offset))
+ !gpiochip_line_is_valid(guard.gc, info->offset) ||
+ !pinctrl_gpio_can_use_line(guard.gc, info->offset))
info->flags |= GPIO_V2_LINE_FLAG_USED;
if (test_bit(FLAG_IS_OUT, &dflags))
@@ -2478,7 +2483,7 @@ static int lineinfo_get_v1(struct gpio_chardev_data *cdev, void __user *ip,
return -EFAULT;
/* this doubles as a range check on line_offset */
- desc = gpiochip_get_desc(cdev->gdev->chip, lineinfo.line_offset);
+ desc = gpio_device_get_desc(cdev->gdev, lineinfo.line_offset);
if (IS_ERR(desc))
return PTR_ERR(desc);
@@ -2515,7 +2520,7 @@ static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip,
if (memchr_inv(lineinfo.padding, 0, sizeof(lineinfo.padding)))
return -EINVAL;
- desc = gpiochip_get_desc(cdev->gdev->chip, lineinfo.offset);
+ desc = gpio_device_get_desc(cdev->gdev, lineinfo.offset);
if (IS_ERR(desc))
return PTR_ERR(desc);
@@ -2564,10 +2569,10 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
struct gpio_device *gdev = cdev->gdev;
void __user *ip = (void __user *)arg;
- guard(rwsem_read)(&gdev->sem);
+ guard(srcu)(&gdev->srcu);
/* We fail any subsequent ioctl():s when the chip is gone */
- if (!gdev->chip)
+ if (!rcu_access_pointer(gdev->chip))
return -ENODEV;
/* Fill in the struct and pass to userspace */
@@ -2650,9 +2655,9 @@ static __poll_t lineinfo_watch_poll(struct file *file,
struct gpio_chardev_data *cdev = file->private_data;
__poll_t events = 0;
- guard(rwsem_read)(&cdev->gdev->sem);
+ guard(srcu)(&cdev->gdev->srcu);
- if (!cdev->gdev->chip)
+ if (!rcu_access_pointer(cdev->gdev->chip))
return EPOLLHUP | EPOLLERR;
poll_wait(file, &cdev->wait, pollt);
@@ -2673,9 +2678,9 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf,
int ret;
size_t event_size;
- guard(rwsem_read)(&cdev->gdev->sem);
+ guard(srcu)(&cdev->gdev->srcu);
- if (!cdev->gdev->chip)
+ if (!rcu_access_pointer(cdev->gdev->chip))
return -ENODEV;
#ifndef CONFIG_GPIO_CDEV_V1
@@ -2750,17 +2755,17 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file)
struct gpio_chardev_data *cdev;
int ret = -ENOMEM;
- guard(rwsem_read)(&gdev->sem);
+ guard(srcu)(&gdev->srcu);
/* Fail on open if the backing gpiochip is gone */
- if (!gdev->chip)
+ if (!rcu_access_pointer(gdev->chip))
return -ENODEV;
cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
if (!cdev)
return -ENODEV;
- cdev->watched_lines = bitmap_zalloc(gdev->chip->ngpio, GFP_KERNEL);
+ cdev->watched_lines = bitmap_zalloc(gdev->ngpio, GFP_KERNEL);
if (!cdev->watched_lines)
goto out_free_cdev;
@@ -2840,6 +2845,7 @@ static const struct file_operations gpio_fileops = {
int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
{
+ struct gpio_chip *gc;
int ret;
cdev_init(&gdev->chrdev, &gpio_fileops);
@@ -2850,8 +2856,12 @@ int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
if (ret)
return ret;
- chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n",
- MAJOR(devt), gdev->id);
+ guard(srcu)(&gdev->srcu);
+ gc = srcu_dereference(gdev->chip, &gdev->srcu);
+ if (!gc)
+ return -ENODEV;
+
+ chip_dbg(gc, "added GPIO chardev (%d:%d)\n", MAJOR(devt), gdev->id);
return 0;
}