Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
[pandora-kernel.git] / drivers / hid / usbhid / hid-core.c
index 7b85b69..1ebd324 100644 (file)
@@ -623,6 +623,7 @@ int usbhid_wait_io(struct hid_device *hid)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(usbhid_wait_io);
 
 static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle)
 {
@@ -783,13 +784,12 @@ static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid)
 {
        struct usbhid_device *usbhid = hid->driver_data;
 
-       usbhid->inbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_KERNEL,
+       usbhid->inbuf = usb_alloc_coherent(dev, usbhid->bufsize, GFP_KERNEL,
                        &usbhid->inbuf_dma);
-       usbhid->outbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_KERNEL,
+       usbhid->outbuf = usb_alloc_coherent(dev, usbhid->bufsize, GFP_KERNEL,
                        &usbhid->outbuf_dma);
-       usbhid->cr = usb_buffer_alloc(dev, sizeof(*usbhid->cr), GFP_KERNEL,
-                       &usbhid->cr_dma);
-       usbhid->ctrlbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_KERNEL,
+       usbhid->cr = kmalloc(sizeof(*usbhid->cr), GFP_KERNEL);
+       usbhid->ctrlbuf = usb_alloc_coherent(dev, usbhid->bufsize, GFP_KERNEL,
                        &usbhid->ctrlbuf_dma);
        if (!usbhid->inbuf || !usbhid->outbuf || !usbhid->cr ||
                        !usbhid->ctrlbuf)
@@ -807,16 +807,36 @@ static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t co
        struct usb_host_interface *interface = intf->cur_altsetting;
        int ret;
 
-       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
-               HID_REQ_SET_REPORT,
-               USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-               ((report_type + 1) << 8) | *buf,
-               interface->desc.bInterfaceNumber, buf + 1, count - 1,
-               USB_CTRL_SET_TIMEOUT);
-
-       /* count also the report id */
-       if (ret > 0)
-               ret++;
+       if (usbhid->urbout) {
+               int actual_length;
+               int skipped_report_id = 0;
+               if (buf[0] == 0x0) {
+                       /* Don't send the Report ID */
+                       buf++;
+                       count--;
+                       skipped_report_id = 1;
+               }
+               ret = usb_interrupt_msg(dev, usbhid->urbout->pipe,
+                       buf, count, &actual_length,
+                       USB_CTRL_SET_TIMEOUT);
+               /* return the number of bytes transferred */
+               if (ret == 0) {
+                       ret = actual_length;
+                       /* count also the report id */
+                       if (skipped_report_id)
+                               ret++;
+               }
+       } else {
+               ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                       HID_REQ_SET_REPORT,
+                       USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+                       ((report_type + 1) << 8) | *buf,
+                       interface->desc.bInterfaceNumber, buf + 1, count - 1,
+                       USB_CTRL_SET_TIMEOUT);
+               /* count also the report id */
+               if (ret > 0)
+                       ret++;
+       }
 
        return ret;
 }
@@ -844,10 +864,10 @@ static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid)
 {
        struct usbhid_device *usbhid = hid->driver_data;
 
-       usb_buffer_free(dev, usbhid->bufsize, usbhid->inbuf, usbhid->inbuf_dma);
-       usb_buffer_free(dev, usbhid->bufsize, usbhid->outbuf, usbhid->outbuf_dma);
-       usb_buffer_free(dev, sizeof(*(usbhid->cr)), usbhid->cr, usbhid->cr_dma);
-       usb_buffer_free(dev, usbhid->bufsize, usbhid->ctrlbuf, usbhid->ctrlbuf_dma);
+       usb_free_coherent(dev, usbhid->bufsize, usbhid->inbuf, usbhid->inbuf_dma);
+       usb_free_coherent(dev, usbhid->bufsize, usbhid->outbuf, usbhid->outbuf_dma);
+       kfree(usbhid->cr);
+       usb_free_coherent(dev, usbhid->bufsize, usbhid->ctrlbuf, usbhid->ctrlbuf_dma);
 }
 
 static int usbhid_parse(struct hid_device *hid)
@@ -1007,9 +1027,8 @@ static int usbhid_start(struct hid_device *hid)
 
        usb_fill_control_urb(usbhid->urbctrl, dev, 0, (void *) usbhid->cr,
                             usbhid->ctrlbuf, 1, hid_ctrl, hid);
-       usbhid->urbctrl->setup_dma = usbhid->cr_dma;
        usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma;
-       usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
+       usbhid->urbctrl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 
        if (!(hid->quirks & HID_QUIRK_NO_INIT_REPORTS))
                usbhid_init_reports(hid);
@@ -1019,12 +1038,15 @@ static int usbhid_start(struct hid_device *hid)
        /* Some keyboards don't work until their LEDs have been set.
         * Since BIOSes do set the LEDs, it must be safe for any device
         * that supports the keyboard boot protocol.
+        * In addition, enable remote wakeup by default for all keyboard
+        * devices supporting the boot protocol.
         */
        if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT &&
                        interface->desc.bInterfaceProtocol ==
-                               USB_INTERFACE_PROTOCOL_KEYBOARD)
+                               USB_INTERFACE_PROTOCOL_KEYBOARD) {
                usbhid_set_leds(hid);
-
+               device_set_wakeup_enable(&dev->dev, 1);
+       }
        return 0;
 
 fail:
@@ -1133,6 +1155,7 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *
        hid->vendor = le16_to_cpu(dev->descriptor.idVendor);
        hid->product = le16_to_cpu(dev->descriptor.idProduct);
        hid->name[0] = 0;
+       hid->quirks = usbhid_lookup_quirk(hid->vendor, hid->product);
        if (intf->cur_altsetting->desc.bInterfaceProtocol ==
                        USB_INTERFACE_PROTOCOL_MOUSE)
                hid->type = HID_TYPE_USBMOUSE;
@@ -1289,6 +1312,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);
@@ -1296,6 +1324,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);
@@ -1350,6 +1383,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;
 }
@@ -1358,9 +1396,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 */