diff options
Diffstat (limited to 'drivers/iio/industrialio-core.c')
-rw-r--r-- | drivers/iio/industrialio-core.c | 56 |
1 files changed, 42 insertions, 14 deletions
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 0402be441ffb..e53c771d66eb 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1612,6 +1612,7 @@ struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv) } dev_set_name(&dev->dev, "iio:device%d", dev->id); INIT_LIST_HEAD(&iio_dev_opaque->buffer_list); + INIT_LIST_HEAD(&iio_dev_opaque->ioctl_handlers); return dev; } @@ -1705,26 +1706,47 @@ static int iio_chrdev_release(struct inode *inode, struct file *filp) return 0; } -/* Somewhat of a cross file organization violation - ioctls here are actually - * event related */ +void iio_device_ioctl_handler_register(struct iio_dev *indio_dev, + struct iio_ioctl_handler *h) +{ + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + + list_add_tail(&h->entry, &iio_dev_opaque->ioctl_handlers); +} + +void iio_device_ioctl_handler_unregister(struct iio_ioctl_handler *h) +{ + list_del(&h->entry); +} + static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct iio_dev *indio_dev = filp->private_data; - int __user *ip = (int __user *)arg; - int fd; + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + struct iio_ioctl_handler *h; + int ret = -ENODEV; + + mutex_lock(&indio_dev->info_exist_lock); + /** + * The NULL check here is required to prevent crashing when a device + * is being removed while userspace would still have open file handles + * to try to access this device. + */ if (!indio_dev->info) - return -ENODEV; - - if (cmd == IIO_GET_EVENT_FD_IOCTL) { - fd = iio_event_getfd(indio_dev); - if (fd < 0) - return fd; - if (copy_to_user(ip, &fd, sizeof(fd))) - return -EFAULT; - return 0; + goto out_unlock; + + ret = -EINVAL; + list_for_each_entry(h, &iio_dev_opaque->ioctl_handlers, entry) { + ret = h->ioctl(indio_dev, filp, cmd, arg); + if (ret != IIO_IOCTL_UNHANDLED) + break; } - return -EINVAL; + +out_unlock: + mutex_unlock(&indio_dev->info_exist_lock); + + return ret; } static const struct file_operations iio_buffer_fileops = { @@ -1841,6 +1863,9 @@ EXPORT_SYMBOL(__iio_device_register); **/ void iio_device_unregister(struct iio_dev *indio_dev) { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + struct iio_ioctl_handler *h, *t; + cdev_device_del(&indio_dev->chrdev, &indio_dev->dev); mutex_lock(&indio_dev->info_exist_lock); @@ -1851,6 +1876,9 @@ void iio_device_unregister(struct iio_dev *indio_dev) indio_dev->info = NULL; + list_for_each_entry_safe(h, t, &iio_dev_opaque->ioctl_handlers, entry) + list_del(&h->entry); + iio_device_wakeup_eventset(indio_dev); iio_buffer_wakeup_poll(indio_dev); |