Merge branches 'upstream-fixes', 'bkl-removal', 'debugfs-fixes' and 'hid-suspend...
authorJiri Kosina <jkosina@suse.cz>
Wed, 19 May 2010 12:05:06 +0000 (14:05 +0200)
committerJiri Kosina <jkosina@suse.cz>
Wed, 19 May 2010 12:05:06 +0000 (14:05 +0200)
drivers/hid/hid-core.c
drivers/hid/hid-magicmouse.c
drivers/hid/hidraw.c
drivers/hid/usbhid/hid-core.c
drivers/hid/usbhid/hiddev.c
include/linux/hid.h

index 69fdf1e..33cac48 100644 (file)
@@ -1081,35 +1081,28 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i
 
        buf = kmalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_ATOMIC);
 
-       if (!buf) {
-               report = hid_get_report(report_enum, data);
+       if (!buf)
                goto nomem;
-       }
-
-       snprintf(buf, HID_DEBUG_BUFSIZE - 1,
-                       "\nreport (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un");
-       hid_debug_event(hid, buf);
-
-       report = hid_get_report(report_enum, data);
-       if (!report) {
-               kfree(buf);
-               return -1;
-       }
 
        /* dump the report */
        snprintf(buf, HID_DEBUG_BUFSIZE - 1,
-                       "report %d (size %u) = ", report->id, size);
+                       "\nreport (size %u) (%snumbered) = ", size, report_enum->numbered ? "" : "un");
        hid_debug_event(hid, buf);
+
        for (i = 0; i < size; i++) {
                snprintf(buf, HID_DEBUG_BUFSIZE - 1,
                                " %02x", data[i]);
                hid_debug_event(hid, buf);
        }
        hid_debug_event(hid, "\n");
-
        kfree(buf);
 
 nomem:
+       report = hid_get_report(report_enum, data);
+
+       if (!report)
+               return -1;
+
        if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) {
                ret = hdrv->raw_event(hid, report, data, size);
                if (ret != 0)
index 0d471fc..f10d56a 100644 (file)
@@ -354,12 +354,15 @@ static int magicmouse_probe(struct hid_device *hdev,
                goto err_free;
        }
 
-       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_HIDINPUT);
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
        if (ret) {
                dev_err(&hdev->dev, "magicmouse hw start failed\n");
                goto err_free;
        }
 
+       /* we are handling the input ourselves */
+       hidinput_disconnect(hdev);
+
        report = hid_register_report(hdev, HID_INPUT_REPORT, TOUCH_REPORT_ID);
        if (!report) {
                dev_err(&hdev->dev, "unable to register touch report\n");
index a9becf9..3ccd478 100644 (file)
@@ -106,38 +106,48 @@ out:
 static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
 {
        unsigned int minor = iminor(file->f_path.dentry->d_inode);
-       /* FIXME: What stops hidraw_table going NULL */
-       struct hid_device *dev = hidraw_table[minor]->hid;
+       struct hid_device *dev;
        __u8 *buf;
        int ret = 0;
 
-       if (!dev->hid_output_raw_report)
-               return -ENODEV;
+       mutex_lock(&minors_lock);
+       dev = hidraw_table[minor]->hid;
+
+       if (!dev->hid_output_raw_report) {
+               ret = -ENODEV;
+               goto out;
+       }
 
        if (count > HID_MAX_BUFFER_SIZE) {
                printk(KERN_WARNING "hidraw: pid %d passed too large report\n",
                                task_pid_nr(current));
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
 
        if (count < 2) {
                printk(KERN_WARNING "hidraw: pid %d passed too short report\n",
                                task_pid_nr(current));
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
 
        buf = kmalloc(count * sizeof(__u8), GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
+       if (!buf) {
+               ret = -ENOMEM;
+               goto out;
+       }
 
        if (copy_from_user(buf, buffer, count)) {
                ret = -EFAULT;
-               goto out;
+               goto out_free;
        }
 
        ret = dev->hid_output_raw_report(dev, buf, count, HID_OUTPUT_REPORT);
-out:
+out_free:
        kfree(buf);
+out:
+       mutex_unlock(&minors_lock);
        return ret;
 }
 
@@ -165,11 +175,8 @@ static int hidraw_open(struct inode *inode, struct file *file)
                goto out;
        }
 
-       lock_kernel();
        mutex_lock(&minors_lock);
        if (!hidraw_table[minor]) {
-               printk(KERN_EMERG "hidraw device with minor %d doesn't exist\n",
-                               minor);
                kfree(list);
                err = -ENODEV;
                goto out_unlock;
@@ -197,7 +204,6 @@ static int hidraw_open(struct inode *inode, struct file *file)
 
 out_unlock:
        mutex_unlock(&minors_lock);
-       unlock_kernel();
 out:
        return err;
 
@@ -209,11 +215,8 @@ static int hidraw_release(struct inode * inode, struct file * file)
        struct hidraw *dev;
        struct hidraw_list *list = file->private_data;
 
-       if (!hidraw_table[minor]) {
-               printk(KERN_EMERG "hidraw device with minor %d doesn't exist\n",
-                               minor);
+       if (!hidraw_table[minor])
                return -ENODEV;
-       }
 
        list_del(&list->node);
        dev = hidraw_table[minor];
@@ -238,11 +241,12 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
        struct inode *inode = file->f_path.dentry->d_inode;
        unsigned int minor = iminor(inode);
        long ret = 0;
-       /* FIXME: What stops hidraw_table going NULL */
-       struct hidraw *dev = hidraw_table[minor];
+       struct hidraw *dev;
        void __user *user_arg = (void __user*) arg;
 
-       lock_kernel();
+       mutex_lock(&minors_lock);
+       dev = hidraw_table[minor];
+
        switch (cmd) {
                case HIDIOCGRDESCSIZE:
                        if (put_user(dev->hid->rsize, (int __user *)arg))
@@ -315,7 +319,7 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
 
                ret = -ENOTTY;
        }
-       unlock_kernel();
+       mutex_unlock(&minors_lock);
        return ret;
 }
 
index a2ebe19..ca3751f 100644 (file)
@@ -1313,6 +1313,11 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
                {
                        set_bit(HID_REPORTED_IDLE, &usbhid->iofl);
                        spin_unlock_irq(&usbhid->lock);
+                       if (hid->driver && hid->driver->suspend) {
+                               status = hid->driver->suspend(hid, message);
+                               if (status < 0)
+                                       return status;
+                       }
                } else {
                        usbhid_mark_busy(usbhid);
                        spin_unlock_irq(&usbhid->lock);
@@ -1320,6 +1325,11 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
                }
 
        } else {
+               if (hid->driver && hid->driver->suspend) {
+                       status = hid->driver->suspend(hid, message);
+                       if (status < 0)
+                               return status;
+               }
                spin_lock_irq(&usbhid->lock);
                set_bit(HID_REPORTED_IDLE, &usbhid->iofl);
                spin_unlock_irq(&usbhid->lock);
@@ -1374,6 +1384,11 @@ static int hid_resume(struct usb_interface *intf)
                hid_io_error(hid);
        usbhid_restart_queues(usbhid);
 
+       if (status >= 0 && hid->driver && hid->driver->resume) {
+               int ret = hid->driver->resume(hid);
+               if (ret < 0)
+                       status = ret;
+       }
        dev_dbg(&intf->dev, "resume status %d\n", status);
        return 0;
 }
@@ -1382,9 +1397,16 @@ static int hid_reset_resume(struct usb_interface *intf)
 {
        struct hid_device *hid = usb_get_intfdata(intf);
        struct usbhid_device *usbhid = hid->driver_data;
+       int status;
 
        clear_bit(HID_REPORTED_IDLE, &usbhid->iofl);
-       return hid_post_reset(intf);
+       status = hid_post_reset(intf);
+       if (status >= 0 && hid->driver && hid->driver->reset_resume) {
+               int ret = hid->driver->reset_resume(hid);
+               if (ret < 0)
+                       status = ret;
+       }
+       return status;
 }
 
 #endif /* CONFIG_PM */
index 433602a..c24d2fa 100644 (file)
@@ -267,6 +267,7 @@ 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;
 
@@ -894,8 +895,22 @@ int hiddev_connect(struct hid_device *hid, unsigned int force)
        hiddev->hid = hid;
        hiddev->exist = 1;
 
-       /* when lock_kernel() usage is fixed in usb_open(),
-        * we could also fix it here */
+       /*
+        * 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) {
index f1f2b6f..895001f 100644 (file)
@@ -591,6 +591,9 @@ struct hid_usage_id {
  * @report_fixup: called before report descriptor parsing (NULL means nop)
  * @input_mapping: invoked on input registering before mapping an usage
  * @input_mapped: invoked on input registering after mapping an usage
+ * @suspend: invoked on suspend (NULL means nop)
+ * @resume: invoked on resume if device was not reset (NULL means nop)
+ * @reset_resume: invoked on resume if device was reset (NULL means nop)
  *
  * raw_event and event should return 0 on no action performed, 1 when no
  * further processing should be done and negative on error
@@ -631,6 +634,11 @@ struct hid_driver {
        int (*input_mapped)(struct hid_device *hdev,
                        struct hid_input *hidinput, struct hid_field *field,
                        struct hid_usage *usage, unsigned long **bit, int *max);
+#ifdef CONFIG_PM
+       int (*suspend)(struct hid_device *hdev, pm_message_t message);
+       int (*resume)(struct hid_device *hdev);
+       int (*reset_resume)(struct hid_device *hdev);
+#endif
 /* private: */
        struct device_driver driver;
 };