* 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 *);
+
+static __poll_t call_poll_locked(struct file *file,
+ struct poll_table_struct *wait,
+ struct gpio_device *gdev, poll_fn func)
+{
+ __poll_t ret;
+
+ down_read(&gdev->sem);
+ ret = func(file, wait);
+ up_read(&gdev->sem);
+
+ return ret;
+}
+
+static long call_ioctl_locked(struct file *file, unsigned int cmd,
+ unsigned long arg, struct gpio_device *gdev,
+ ioctl_fn func)
+{
+ long ret;
+
+ down_read(&gdev->sem);
+ ret = func(file, cmd, arg);
+ up_read(&gdev->sem);
+
+ return ret;
+}
+
+static ssize_t call_read_locked(struct file *file, char __user *buf,
+ size_t count, loff_t *f_ps,
+ struct gpio_device *gdev, read_fn func)
+{
+ ssize_t ret;
+
+ down_read(&gdev->sem);
+ ret = func(file, buf, count, f_ps);
+ up_read(&gdev->sem);
+
+ return ret;
+}
+
/*
* GPIO line handle management
*/
return 0;
}
-static long linehandle_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+static long linehandle_ioctl_unlocked(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
struct linehandle_state *lh = file->private_data;
void __user *ip = (void __user *)arg;
}
}
+static long linehandle_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct linehandle_state *lh = file->private_data;
+
+ return call_ioctl_locked(file, cmd, arg, lh->gdev,
+ linehandle_ioctl_unlocked);
+}
+
#ifdef CONFIG_COMPAT
static long linehandle_ioctl_compat(struct file *file, unsigned int cmd,
unsigned long arg)
return ret;
}
-static long linereq_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+static long linereq_ioctl_unlocked(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
struct linereq *lr = file->private_data;
void __user *ip = (void __user *)arg;
}
}
+static long linereq_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct linereq *lr = file->private_data;
+
+ return call_ioctl_locked(file, cmd, arg, lr->gdev,
+ linereq_ioctl_unlocked);
+}
+
#ifdef CONFIG_COMPAT
static long linereq_ioctl_compat(struct file *file, unsigned int cmd,
unsigned long arg)
}
#endif
-static __poll_t linereq_poll(struct file *file,
- struct poll_table_struct *wait)
+static __poll_t linereq_poll_unlocked(struct file *file,
+ struct poll_table_struct *wait)
{
struct linereq *lr = file->private_data;
__poll_t events = 0;
return events;
}
-static ssize_t linereq_read(struct file *file,
- char __user *buf,
- size_t count,
- loff_t *f_ps)
+static __poll_t linereq_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct linereq *lr = file->private_data;
+
+ return call_poll_locked(file, wait, lr->gdev, linereq_poll_unlocked);
+}
+
+static ssize_t linereq_read_unlocked(struct file *file, char __user *buf,
+ size_t count, loff_t *f_ps)
{
struct linereq *lr = file->private_data;
struct gpio_v2_line_event le;
return bytes_read;
}
+static ssize_t linereq_read(struct file *file, char __user *buf,
+ size_t count, loff_t *f_ps)
+{
+ struct linereq *lr = file->private_data;
+
+ return call_read_locked(file, buf, count, f_ps, lr->gdev,
+ linereq_read_unlocked);
+}
+
static void linereq_free(struct linereq *lr)
{
unsigned int i;
(GPIOEVENT_REQUEST_RISING_EDGE | \
GPIOEVENT_REQUEST_FALLING_EDGE)
-static __poll_t lineevent_poll(struct file *file,
- struct poll_table_struct *wait)
+static __poll_t lineevent_poll_unlocked(struct file *file,
+ struct poll_table_struct *wait)
{
struct lineevent_state *le = file->private_data;
__poll_t events = 0;
return events;
}
+static __poll_t lineevent_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct lineevent_state *le = file->private_data;
+
+ return call_poll_locked(file, wait, le->gdev, lineevent_poll_unlocked);
+}
+
struct compat_gpioeevent_data {
compat_u64 timestamp;
u32 id;
};
-static ssize_t lineevent_read(struct file *file,
- char __user *buf,
- size_t count,
- loff_t *f_ps)
+static ssize_t lineevent_read_unlocked(struct file *file, char __user *buf,
+ size_t count, loff_t *f_ps)
{
struct lineevent_state *le = file->private_data;
struct gpioevent_data ge;
return bytes_read;
}
+static ssize_t lineevent_read(struct file *file, char __user *buf,
+ size_t count, loff_t *f_ps)
+{
+ struct lineevent_state *le = file->private_data;
+
+ return call_read_locked(file, buf, count, f_ps, le->gdev,
+ lineevent_read_unlocked);
+}
+
static void lineevent_free(struct lineevent_state *le)
{
if (le->irq)
return 0;
}
-static long lineevent_ioctl(struct file *file, unsigned int cmd,
- unsigned long arg)
+static long lineevent_ioctl_unlocked(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
struct lineevent_state *le = file->private_data;
void __user *ip = (void __user *)arg;
return -EINVAL;
}
+static long lineevent_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct lineevent_state *le = file->private_data;
+
+ return call_ioctl_locked(file, cmd, arg, le->gdev,
+ lineevent_ioctl_unlocked);
+}
+
#ifdef CONFIG_COMPAT
static long lineevent_ioctl_compat(struct file *file, unsigned int cmd,
unsigned long arg)
return NOTIFY_OK;
}
-static __poll_t lineinfo_watch_poll(struct file *file,
- struct poll_table_struct *pollt)
+static __poll_t lineinfo_watch_poll_unlocked(struct file *file,
+ struct poll_table_struct *pollt)
{
struct gpio_chardev_data *cdev = file->private_data;
__poll_t events = 0;
return events;
}
-static ssize_t lineinfo_watch_read(struct file *file, char __user *buf,
- size_t count, loff_t *off)
+static __poll_t lineinfo_watch_poll(struct file *file,
+ struct poll_table_struct *pollt)
+{
+ struct gpio_chardev_data *cdev = file->private_data;
+
+ return call_poll_locked(file, pollt, cdev->gdev,
+ lineinfo_watch_poll_unlocked);
+}
+
+static ssize_t lineinfo_watch_read_unlocked(struct file *file, char __user *buf,
+ size_t count, loff_t *off)
{
struct gpio_chardev_data *cdev = file->private_data;
struct gpio_v2_line_info_changed event;
return bytes_read;
}
+static ssize_t lineinfo_watch_read(struct file *file, char __user *buf,
+ size_t count, loff_t *off)
+{
+ struct gpio_chardev_data *cdev = file->private_data;
+
+ return call_read_locked(file, buf, count, off, cdev->gdev,
+ lineinfo_watch_read_unlocked);
+}
+
/**
* gpio_chrdev_open() - open the chardev for ioctl operations
* @inode: inode for this chardev
struct gpio_chardev_data *cdev;
int ret = -ENOMEM;
+ down_read(&gdev->sem);
+
/* Fail on open if the backing gpiochip is gone */
- if (!gdev->chip)
- return -ENODEV;
+ if (!gdev->chip) {
+ ret = -ENODEV;
+ goto out_unlock;
+ }
cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
if (!cdev)
- return -ENOMEM;
+ goto out_unlock;
cdev->watched_lines = bitmap_zalloc(gdev->chip->ngpio, GFP_KERNEL);
if (!cdev->watched_lines)
if (ret)
goto out_unregister_notifier;
+ up_read(&gdev->sem);
+
return ret;
out_unregister_notifier:
bitmap_free(cdev->watched_lines);
out_free_cdev:
kfree(cdev);
+out_unlock:
+ up_read(&gdev->sem);
return ret;
}