diff options
Diffstat (limited to 'drivers/hid/usbhid/hiddev.c')
-rw-r--r-- | drivers/hid/usbhid/hiddev.c | 100 |
1 files changed, 17 insertions, 83 deletions
diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index c24d2fa3e3b6..fedd88df9a18 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -67,8 +67,6 @@ struct hiddev_list { struct mutex thread_lock; }; -static struct hiddev *hiddev_table[HIDDEV_MINORS]; - /* * Find a report, given the report's type and ID. The ID can be specified * indirectly by REPORT_ID_FIRST (which returns the first report of the given @@ -265,22 +263,21 @@ static int hiddev_release(struct inode * inode, struct file * file) static int hiddev_open(struct inode *inode, struct file *file) { struct hiddev_list *list; - int res, i; - - /* See comment in hiddev_connect() for BKL explanation */ - lock_kernel(); - i = iminor(inode) - HIDDEV_MINOR_BASE; + struct usb_interface *intf; + struct hid_device *hid; + struct hiddev *hiddev; + int res; - if (i >= HIDDEV_MINORS || i < 0 || !hiddev_table[i]) + intf = usbhid_find_interface(iminor(inode)); + if (!intf) return -ENODEV; + hid = usb_get_intfdata(intf); + hiddev = hid->hiddev; if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL))) return -ENOMEM; mutex_init(&list->thread_lock); - - list->hiddev = hiddev_table[i]; - - + list->hiddev = hiddev; file->private_data = list; /* @@ -289,7 +286,7 @@ static int hiddev_open(struct inode *inode, struct file *file) */ if (list->hiddev->exist) { if (!list->hiddev->open++) { - res = usbhid_open(hiddev_table[i]->hid); + res = usbhid_open(hiddev->hid); if (res < 0) { res = -EIO; goto bail; @@ -301,12 +298,12 @@ static int hiddev_open(struct inode *inode, struct file *file) } spin_lock_irq(&list->hiddev->list_lock); - list_add_tail(&list->node, &hiddev_table[i]->list); + list_add_tail(&list->node, &hiddev->list); spin_unlock_irq(&list->hiddev->list_lock); if (!list->hiddev->open++) if (list->hiddev->exist) { - struct hid_device *hid = hiddev_table[i]->hid; + struct hid_device *hid = hiddev->hid; res = usbhid_get_power(hid); if (res < 0) { res = -EIO; @@ -314,13 +311,10 @@ static int hiddev_open(struct inode *inode, struct file *file) } usbhid_open(hid); } - - unlock_kernel(); return 0; bail: file->private_data = NULL; kfree(list); - unlock_kernel(); return res; } @@ -593,7 +587,7 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) struct hiddev_list *list = file->private_data; struct hiddev *hiddev = list->hiddev; struct hid_device *hid = hiddev->hid; - struct usb_device *dev = hid_to_usb_dev(hid); + struct usb_device *dev; struct hiddev_collection_info cinfo; struct hiddev_report_info rinfo; struct hiddev_field_info finfo; @@ -607,9 +601,11 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) /* Called without BKL by compat methods so no BKL taken */ /* FIXME: Who or what stop this racing with a disconnect ?? */ - if (!hiddev->exist) + if (!hiddev->exist || !hid) return -EIO; + dev = hid_to_usb_dev(hid); + switch (cmd) { case HIDIOCGVERSION: @@ -849,6 +845,7 @@ static const struct file_operations hiddev_fops = { #ifdef CONFIG_COMPAT .compat_ioctl = hiddev_compat_ioctl, #endif + .llseek = noop_llseek, }; static char *hiddev_devnode(struct device *dev, mode_t *mode) @@ -894,37 +891,13 @@ int hiddev_connect(struct hid_device *hid, unsigned int force) hid->hiddev = hiddev; hiddev->hid = hid; hiddev->exist = 1; - - /* - * BKL here is used to avoid race after usb_register_dev(). - * Once the device node has been created, open() could happen on it. - * The code below will then fail, as hiddev_table hasn't been - * updated. - * - * The obvious fix -- introducing mutex to guard hiddev_table[] - * doesn't work, as usb_open() and usb_register_dev() both take - * minor_rwsem, thus we'll have ABBA deadlock. - * - * Before BKL pushdown, usb_open() had been acquiring it in right - * order, so _open() was safe to use it to protect from this race. - * Now the order is different, but AB-BA deadlock still doesn't occur - * as BKL is dropped on schedule() (i.e. while sleeping on - * minor_rwsem). Fugly. - */ - lock_kernel(); retval = usb_register_dev(usbhid->intf, &hiddev_class); if (retval) { err_hid("Not able to get a minor for this device."); hid->hiddev = NULL; - unlock_kernel(); kfree(hiddev); return -1; - } else { - hid->minor = usbhid->intf->minor; - hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev; } - unlock_kernel(); - return 0; } @@ -942,7 +915,6 @@ void hiddev_disconnect(struct hid_device *hid) hiddev->exist = 0; mutex_unlock(&hiddev->existancelock); - hiddev_table[hiddev->hid->minor - HIDDEV_MINOR_BASE] = NULL; usb_deregister_dev(usbhid->intf, &hiddev_class); if (hiddev->open) { @@ -952,41 +924,3 @@ void hiddev_disconnect(struct hid_device *hid) kfree(hiddev); } } - -/* Currently this driver is a USB driver. It's not a conventional one in - * the sense that it doesn't probe at the USB level. Instead it waits to - * be connected by HID through the hiddev_connect / hiddev_disconnect - * routines. The reason to register as a USB device is to gain part of the - * minor number space from the USB major. - * - * In theory, should the HID code be generalized to more than one physical - * medium (say, IEEE 1384), this driver will probably need to register its - * own major number, and in doing so, no longer need to register with USB. - * At that point the probe routine and hiddev_driver struct below will no - * longer be useful. - */ - - -/* We never attach in this manner, and rely on HID to connect us. This - * is why there is no disconnect routine defined in the usb_driver either. - */ -static int hiddev_usbd_probe(struct usb_interface *intf, - const struct usb_device_id *hiddev_info) -{ - return -ENODEV; -} - -static /* const */ struct usb_driver hiddev_driver = { - .name = "hiddev", - .probe = hiddev_usbd_probe, -}; - -int __init hiddev_init(void) -{ - return usb_register(&hiddev_driver); -} - -void hiddev_exit(void) -{ - usb_deregister(&hiddev_driver); -} |