Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 3 Apr 2009 22:25:44 +0000 (15:25 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 3 Apr 2009 22:25:44 +0000 (15:25 -0700)
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid:
  HID: remove compat stuff
  HID: constify arrays of struct apple_key_translation
  HID: add support for Kye/Genius Ergo 525V
  HID: Support Apple mini aluminum keyboard
  HID: support for Kensington slimblade device
  HID: DragonRise game controller force feedback driver
  HID: add support for another version of 0e8f:0003 device in hid-pl
  HID: fix race between usb_register_dev() and hiddev_open()
  HID: bring back possibility to specify vid/pid ignore on module load
  HID: make HID_DEBUG defaults consistent
  HID: autosuspend -- fix lockup of hid on reset
  HID: hid_reset_resume() needs to be defined only when CONFIG_PM is set
  HID: fix USB HID devices after STD with autosuspend
  HID: do not try to compile PM code with CONFIG_PM unset
  HID: autosuspend support for USB HID

35 files changed:
Documentation/feature-removal-schedule.txt
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-a4tech.c
drivers/hid/hid-apple.c
drivers/hid/hid-belkin.c
drivers/hid/hid-cherry.c
drivers/hid/hid-chicony.c
drivers/hid/hid-core.c
drivers/hid/hid-cypress.c
drivers/hid/hid-drff.c [new file with mode: 0644]
drivers/hid/hid-dummy.c [deleted file]
drivers/hid/hid-ezkey.c
drivers/hid/hid-gaff.c
drivers/hid/hid-gyration.c
drivers/hid/hid-ids.h
drivers/hid/hid-kensington.c [new file with mode: 0644]
drivers/hid/hid-kye.c [new file with mode: 0644]
drivers/hid/hid-lg.c
drivers/hid/hid-microsoft.c
drivers/hid/hid-monterey.c
drivers/hid/hid-ntrig.c
drivers/hid/hid-petalynx.c
drivers/hid/hid-pl.c
drivers/hid/hid-samsung.c
drivers/hid/hid-sony.c
drivers/hid/hid-sunplus.c
drivers/hid/hid-tmff.c
drivers/hid/hid-topseed.c
drivers/hid/hid-zpff.c
drivers/hid/hidraw.c
drivers/hid/usbhid/hid-core.c
drivers/hid/usbhid/hiddev.c
drivers/hid/usbhid/usbhid.h
include/linux/hid.h

index a23361e80c645835c7ef34b9fc969da22f93bed9..39246fc11257af2c68402aa1ad5192e530e4f618 100644 (file)
@@ -283,13 +283,6 @@ Who:       Glauber Costa <gcosta@redhat.com>
 
 ---------------------------
 
-What:  remove HID compat support
-When:  2.6.29
-Why:   needed only as a temporary solution until distros fix themselves up
-Who:   Jiri Slaby <jirislaby@gmail.com>
-
----------------------------
-
 What: print_fn_descriptor_symbol()
 When: October 2009
 Why:  The %pF vsprintf format provides the same functionality in a
index 504cfaa6160f620a9af93d04d3d25b1129411c7c..7e67dcb3d4f657d4bab800b8390b4af976b4554d 100644 (file)
@@ -29,11 +29,11 @@ config HID
 
          For docs and specs, see http://www.usb.org/developers/hidpage/
 
-         If unsure, say Y
+         If unsure, say Y.
 
 config HID_DEBUG
        bool "HID debugging support"
-       default y if !EMBEDDED
+       default y
        depends on HID
        ---help---
        This option lets the HID layer output diagnostics about its internal
@@ -44,7 +44,7 @@ config HID_DEBUG
        This feature is useful for those who are either debugging the HID parser
        or any HID hardware device.
 
-       If unsure, say N
+       If unsure, say Y.
 
 config HIDRAW
        bool "/dev/hidraw raw HID device support"
@@ -70,18 +70,6 @@ source "drivers/hid/usbhid/Kconfig"
 menu "Special HID drivers"
        depends on HID
 
-config HID_COMPAT
-       bool "Load all HID drivers on hid core load"
-       default y
-       ---help---
-       Compatible option for older userspace. If you have system without udev
-       support of module loading through aliases and also old
-       module-init-tools which can't handle hid bus, choose Y here. Otherwise
-       say N. If you say N and your userspace is old enough, the only
-       functionality you lose is modules autoloading.
-
-       If unsure, say Y.
-
 config HID_A4TECH
        tristate "A4 tech" if EMBEDDED
        depends on USB_HID
@@ -128,6 +116,14 @@ config HID_CYPRESS
        ---help---
        Support for cypress mouse and barcode readers.
 
+config DRAGONRISE_FF
+       tristate "DragonRise Inc. force feedback support"
+       depends on USB_HID
+       select INPUT_FF_MEMLESS
+       ---help---
+       Say Y here if you want to enable force feedback support for DragonRise Inc.
+       game controllers.
+
 config HID_EZKEY
        tristate "Ezkey" if EMBEDDED
        depends on USB_HID
@@ -135,6 +131,13 @@ config HID_EZKEY
        ---help---
        Support for Ezkey BTC 8193 keyboard.
 
+config HID_KYE
+       tristate "Kye" if EMBEDDED
+       depends on USB_HID
+       default !EMBEDDED
+       ---help---
+       Support for Kye/Genius Ergo Mouse.
+
 config HID_GYRATION
        tristate "Gyration" if EMBEDDED
        depends on USB_HID
@@ -142,6 +145,13 @@ config HID_GYRATION
        ---help---
        Support for Gyration remote control.
 
+config HID_KENSINGTON
+       tristate "Kensington" if EMBEDDED
+       depends on USB_HID
+       default !EMBEDDED
+       ---help---
+       Support for Kensington Slimblade Trackball.
+
 config HID_LOGITECH
        tristate "Logitech" if EMBEDDED
        depends on USB_HID
index fbd021f153f1a3642abde8a327c5e6a6ee6d633e..1f7cb0fd4505c3e82aa3d521e35a01dac124f88c 100644 (file)
@@ -8,10 +8,6 @@ obj-$(CONFIG_HID)              += hid.o
 hid-$(CONFIG_HID_DEBUG)                += hid-debug.o
 hid-$(CONFIG_HIDRAW)           += hidraw.o
 
-ifdef CONFIG_HID_COMPAT
-obj-m                          += hid-dummy.o
-endif
-
 hid-logitech-objs              := hid-lg.o
 ifdef CONFIG_LOGITECH_FF
        hid-logitech-objs       += hid-lgff.o
@@ -26,8 +22,11 @@ obj-$(CONFIG_HID_BELKIN)     += hid-belkin.o
 obj-$(CONFIG_HID_CHERRY)       += hid-cherry.o
 obj-$(CONFIG_HID_CHICONY)      += hid-chicony.o
 obj-$(CONFIG_HID_CYPRESS)      += hid-cypress.o
+obj-$(CONFIG_DRAGONRISE_FF)    += hid-drff.o
 obj-$(CONFIG_HID_EZKEY)                += hid-ezkey.o
 obj-$(CONFIG_HID_GYRATION)     += hid-gyration.o
+obj-$(CONFIG_HID_KENSINGTON)   += hid-kensington.o
+obj-$(CONFIG_HID_KYE)          += hid-kye.o
 obj-$(CONFIG_HID_LOGITECH)     += hid-logitech.o
 obj-$(CONFIG_HID_MICROSOFT)    += hid-microsoft.o
 obj-$(CONFIG_HID_MONTEREY)     += hid-monterey.o
index ebca00e6c103f742b22a9044d9d057ef456487c3..42ea359e94cffcb4b2fe626664fc9e671e870848 100644 (file)
@@ -158,5 +158,3 @@ static void a4_exit(void)
 module_init(a4_init);
 module_exit(a4_exit);
 MODULE_LICENSE("GPL");
-
-HID_COMPAT_LOAD_DRIVER(a4tech);
index aa28aed0e46cb4d400cdd8f419cfbb8cedefa877..7359d9d88e4683d5fee89c8599d4b22ea2b508f6 100644 (file)
@@ -53,7 +53,7 @@ struct apple_key_translation {
        u8 flags;
 };
 
-static struct apple_key_translation apple_fn_keys[] = {
+static const struct apple_key_translation apple_fn_keys[] = {
        { KEY_BACKSPACE, KEY_DELETE },
        { KEY_ENTER,    KEY_INSERT },
        { KEY_F1,       KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY },
@@ -75,7 +75,7 @@ static struct apple_key_translation apple_fn_keys[] = {
        { }
 };
 
-static struct apple_key_translation powerbook_fn_keys[] = {
+static const struct apple_key_translation powerbook_fn_keys[] = {
        { KEY_BACKSPACE, KEY_DELETE },
        { KEY_F1,       KEY_BRIGHTNESSDOWN,     APPLE_FLAG_FKEY },
        { KEY_F2,       KEY_BRIGHTNESSUP,       APPLE_FLAG_FKEY },
@@ -94,7 +94,7 @@ static struct apple_key_translation powerbook_fn_keys[] = {
        { }
 };
 
-static struct apple_key_translation powerbook_numlock_keys[] = {
+static const struct apple_key_translation powerbook_numlock_keys[] = {
        { KEY_J,        KEY_KP1 },
        { KEY_K,        KEY_KP2 },
        { KEY_L,        KEY_KP3 },
@@ -117,16 +117,16 @@ static struct apple_key_translation powerbook_numlock_keys[] = {
        { }
 };
 
-static struct apple_key_translation apple_iso_keyboard[] = {
+static const struct apple_key_translation apple_iso_keyboard[] = {
        { KEY_GRAVE,    KEY_102ND },
        { KEY_102ND,    KEY_GRAVE },
        { }
 };
 
-static struct apple_key_translation *apple_find_translation(
-               struct apple_key_translation *table, u16 from)
+static const struct apple_key_translation *apple_find_translation(
+               const struct apple_key_translation *table, u16 from)
 {
-       struct apple_key_translation *trans;
+       const struct apple_key_translation *trans;
 
        /* Look for the translation */
        for (trans = table; trans->from; trans++)
@@ -140,7 +140,7 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
                struct hid_usage *usage, __s32 value)
 {
        struct apple_sc *asc = hid_get_drvdata(hid);
-       struct apple_key_translation *trans;
+       const struct apple_key_translation *trans;
 
        if (usage->code == KEY_FN) {
                asc->fn_on = !!value;
@@ -253,7 +253,7 @@ static void apple_report_fixup(struct hid_device *hdev, __u8 *rdesc,
 
 static void apple_setup_input(struct input_dev *input)
 {
-       struct apple_key_translation *trans;
+       const struct apple_key_translation *trans;
 
        set_bit(KEY_NUMLOCK, input->keybit);
 
@@ -387,6 +387,12 @@ static const struct hid_device_id apple_devices[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS),
                .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
                        APPLE_RDESC_JIS },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_ANSI),
+               .driver_data = APPLE_HAS_FN },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_ISO),
+               .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_JIS),
+               .driver_data = APPLE_HAS_FN },
        { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI),
                .driver_data = APPLE_HAS_FN },
        { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO),
@@ -468,5 +474,3 @@ static void apple_exit(void)
 module_init(apple_init);
 module_exit(apple_exit);
 MODULE_LICENSE("GPL");
-
-HID_COMPAT_LOAD_DRIVER(apple);
index 12c8a9ba6ed69badc7340d770954b249c29d1076..2f6723133a4b7d4eb45f50b45ea50185493f8c8c 100644 (file)
@@ -101,5 +101,3 @@ static void belkin_exit(void)
 module_init(belkin_init);
 module_exit(belkin_exit);
 MODULE_LICENSE("GPL");
-
-HID_COMPAT_LOAD_DRIVER(belkin);
index b833b9769abab3f11af9fbf2dbffcda687a34a37..ab8209e7e45ca41fe691d97cd24bc8011db2179e 100644 (file)
@@ -83,5 +83,3 @@ static void ch_exit(void)
 module_init(ch_init);
 module_exit(ch_exit);
 MODULE_LICENSE("GPL");
-
-HID_COMPAT_LOAD_DRIVER(cherry);
index a54d4096e0f7f80722249025e986dbc365a5218a..7f91076d8493ae5b653f01f6814028cab391b357 100644 (file)
@@ -76,5 +76,3 @@ static void ch_exit(void)
 module_init(ch_init);
 module_exit(ch_exit);
 MODULE_LICENSE("GPL");
-
-HID_COMPAT_LOAD_DRIVER(chicony);
index 1cc967448f4d5c760a8d75329c4e76bb82f4000f..5746a5903bcea1a17bfcc7349a2a867bd0833d32 100644 (file)
@@ -1236,6 +1236,9 @@ static const struct hid_device_id hid_blacklist[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI) },
        { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO) },
        { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_ANSI) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_ISO) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_JIS) },
        { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI) },
        { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO) },
        { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS) },
@@ -1262,6 +1265,7 @@ static const struct hid_device_id hid_blacklist[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) },
        { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) },
        { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) },
        { HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) },
        { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) },
        { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR) },
@@ -1269,6 +1273,8 @@ static const struct hid_device_id hid_blacklist[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0012) },
        { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) },
@@ -1813,14 +1819,21 @@ void hid_unregister_driver(struct hid_driver *hdrv)
 }
 EXPORT_SYMBOL_GPL(hid_unregister_driver);
 
-#ifdef CONFIG_HID_COMPAT
-static void hid_compat_load(struct work_struct *ws)
+int hid_check_keys_pressed(struct hid_device *hid)
 {
-       request_module("hid-dummy");
+       struct hid_input *hidinput;
+       int i;
+
+       list_for_each_entry(hidinput, &hid->inputs, list) {
+               for (i = 0; i < BITS_TO_LONGS(KEY_MAX); i++)
+                       if (hidinput->input->key[i])
+                               return 1;
+       }
+
+       return 0;
 }
-static DECLARE_WORK(hid_compat_work, hid_compat_load);
-static struct workqueue_struct *hid_compat_wq;
-#endif
+
+EXPORT_SYMBOL_GPL(hid_check_keys_pressed);
 
 static int __init hid_init(void)
 {
@@ -1836,15 +1849,6 @@ static int __init hid_init(void)
        if (ret)
                goto err_bus;
 
-#ifdef CONFIG_HID_COMPAT
-       hid_compat_wq = create_singlethread_workqueue("hid_compat");
-       if (!hid_compat_wq) {
-               hidraw_exit();
-               goto err;
-       }
-       queue_work(hid_compat_wq, &hid_compat_work);
-#endif
-
        return 0;
 err_bus:
        bus_unregister(&hid_bus_type);
@@ -1854,9 +1858,6 @@ err:
 
 static void __exit hid_exit(void)
 {
-#ifdef CONFIG_HID_COMPAT
-       destroy_workqueue(hid_compat_wq);
-#endif
        hidraw_exit();
        bus_unregister(&hid_bus_type);
 }
index 5d69d27b935d629b2d04c39b982957fe4f533fe6..9d6d3b91773bbbcbed9858c8e73b89627277aae9 100644 (file)
@@ -154,5 +154,3 @@ static void cp_exit(void)
 module_init(cp_init);
 module_exit(cp_exit);
 MODULE_LICENSE("GPL");
-
-HID_COMPAT_LOAD_DRIVER(cypress);
diff --git a/drivers/hid/hid-drff.c b/drivers/hid/hid-drff.c
new file mode 100644 (file)
index 0000000..34f3eb6
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Force feedback support for DragonRise Inc. game controllers
+ *
+ * From what I have gathered, these devices are mass produced in China and are
+ * distributed under several vendors. They often share the same design as
+ * the original PlayStation DualShock controller.
+ *
+ * 0079:0006 "DragonRise Inc.   Generic   USB  Joystick  "
+ *  - tested with a Tesun USB-703 game controller.
+ *
+ * Copyright (c) 2009 Richard Walmsley <richwalm@gmail.com>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/input.h>
+#include <linux/usb.h>
+#include <linux/hid.h>
+
+#include "hid-ids.h"
+#include "usbhid/usbhid.h"
+
+struct drff_device {
+       struct hid_report *report;
+};
+
+static int drff_play(struct input_dev *dev, void *data,
+                                struct ff_effect *effect)
+{
+       struct hid_device *hid = input_get_drvdata(dev);
+       struct drff_device *drff = data;
+       int strong, weak;
+
+       strong = effect->u.rumble.strong_magnitude;
+       weak = effect->u.rumble.weak_magnitude;
+
+       dbg_hid("called with 0x%04x 0x%04x", strong, weak);
+
+       if (strong || weak) {
+               strong = strong * 0xff / 0xffff;
+               weak = weak * 0xff / 0xffff;
+
+               /* While reverse engineering this device, I found that when
+                  this value is set, it causes the strong rumble to function
+                  at a near maximum speed, so we'll bypass it. */
+               if (weak == 0x0a)
+                       weak = 0x0b;
+
+               drff->report->field[0]->value[0] = 0x51;
+               drff->report->field[0]->value[1] = 0x00;
+               drff->report->field[0]->value[2] = weak;
+               drff->report->field[0]->value[4] = strong;
+               usbhid_submit_report(hid, drff->report, USB_DIR_OUT);
+
+               drff->report->field[0]->value[0] = 0xfa;
+               drff->report->field[0]->value[1] = 0xfe;
+       } else {
+               drff->report->field[0]->value[0] = 0xf3;
+               drff->report->field[0]->value[1] = 0x00;
+       }
+
+       drff->report->field[0]->value[2] = 0x00;
+       drff->report->field[0]->value[4] = 0x00;
+       dbg_hid("running with 0x%02x 0x%02x", strong, weak);
+       usbhid_submit_report(hid, drff->report, USB_DIR_OUT);
+
+       return 0;
+}
+
+static int drff_init(struct hid_device *hid)
+{
+       struct drff_device *drff;
+       struct hid_report *report;
+       struct hid_input *hidinput = list_first_entry(&hid->inputs,
+                                               struct hid_input, list);
+       struct list_head *report_list =
+                       &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+       struct input_dev *dev = hidinput->input;
+       int error;
+
+       if (list_empty(report_list)) {
+               dev_err(&hid->dev, "no output reports found\n");
+               return -ENODEV;
+       }
+
+       report = list_first_entry(report_list, struct hid_report, list);
+       if (report->maxfield < 1) {
+               dev_err(&hid->dev, "no fields in the report\n");
+               return -ENODEV;
+       }
+
+       if (report->field[0]->report_count < 7) {
+               dev_err(&hid->dev, "not enough values in the field\n");
+               return -ENODEV;
+       }
+
+       drff = kzalloc(sizeof(struct drff_device), GFP_KERNEL);
+       if (!drff)
+               return -ENOMEM;
+
+       set_bit(FF_RUMBLE, dev->ffbit);
+
+       error = input_ff_create_memless(dev, drff, drff_play);
+       if (error) {
+               kfree(drff);
+               return error;
+       }
+
+       drff->report = report;
+       drff->report->field[0]->value[0] = 0xf3;
+       drff->report->field[0]->value[1] = 0x00;
+       drff->report->field[0]->value[2] = 0x00;
+       drff->report->field[0]->value[3] = 0x00;
+       drff->report->field[0]->value[4] = 0x00;
+       drff->report->field[0]->value[5] = 0x00;
+       drff->report->field[0]->value[6] = 0x00;
+       usbhid_submit_report(hid, drff->report, USB_DIR_OUT);
+
+       dev_info(&hid->dev, "Force Feedback for DragonRise Inc. game "
+              "controllers by Richard Walmsley <richwalm@gmail.com>\n");
+
+       return 0;
+}
+
+static int dr_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+       int ret;
+
+       dev_dbg(&hdev->dev, "DragonRise Inc. HID hardware probe...");
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               dev_err(&hdev->dev, "parse failed\n");
+               goto err;
+       }
+
+       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
+       if (ret) {
+               dev_err(&hdev->dev, "hw start failed\n");
+               goto err;
+       }
+
+       drff_init(hdev);
+
+       return 0;
+err:
+       return ret;
+}
+
+static const struct hid_device_id dr_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006),  },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, dr_devices);
+
+static struct hid_driver dr_driver = {
+       .name = "dragonrise",
+       .id_table = dr_devices,
+       .probe = dr_probe,
+};
+
+static int __init dr_init(void)
+{
+       return hid_register_driver(&dr_driver);
+}
+
+static void __exit dr_exit(void)
+{
+       hid_unregister_driver(&dr_driver);
+}
+
+module_init(dr_init);
+module_exit(dr_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c
deleted file mode 100644 (file)
index b4cc0f7..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-#include <linux/autoconf.h>
-#include <linux/module.h>
-#include <linux/hid.h>
-
-static int __init hid_dummy_init(void)
-{
-#ifdef CONFIG_HID_A4TECH_MODULE
-       HID_COMPAT_CALL_DRIVER(a4tech);
-#endif
-#ifdef CONFIG_HID_APPLE_MODULE
-       HID_COMPAT_CALL_DRIVER(apple);
-#endif
-#ifdef CONFIG_HID_BELKIN_MODULE
-       HID_COMPAT_CALL_DRIVER(belkin);
-#endif
-#ifdef CONFIG_HID_BRIGHT_MODULE
-       HID_COMPAT_CALL_DRIVER(bright);
-#endif
-#ifdef CONFIG_HID_CHERRY_MODULE
-       HID_COMPAT_CALL_DRIVER(cherry);
-#endif
-#ifdef CONFIG_HID_CHICONY_MODULE
-       HID_COMPAT_CALL_DRIVER(chicony);
-#endif
-#ifdef CONFIG_HID_CYPRESS_MODULE
-       HID_COMPAT_CALL_DRIVER(cypress);
-#endif
-#ifdef CONFIG_HID_DELL_MODULE
-       HID_COMPAT_CALL_DRIVER(dell);
-#endif
-#ifdef CONFIG_HID_EZKEY_MODULE
-       HID_COMPAT_CALL_DRIVER(ezkey);
-#endif
-#ifdef CONFIG_HID_GYRATION_MODULE
-       HID_COMPAT_CALL_DRIVER(gyration);
-#endif
-#ifdef CONFIG_HID_LOGITECH_MODULE
-       HID_COMPAT_CALL_DRIVER(logitech);
-#endif
-#ifdef CONFIG_HID_MICROSOFT_MODULE
-       HID_COMPAT_CALL_DRIVER(microsoft);
-#endif
-#ifdef CONFIG_HID_MONTEREY_MODULE
-       HID_COMPAT_CALL_DRIVER(monterey);
-#endif
-#ifdef CONFIG_HID_NTRIG_MODULE
-       HID_COMPAT_CALL_DRIVER(ntrig);
-#endif
-#ifdef CONFIG_HID_PANTHERLORD_MODULE
-       HID_COMPAT_CALL_DRIVER(pantherlord);
-#endif
-#ifdef CONFIG_HID_PETALYNX_MODULE
-       HID_COMPAT_CALL_DRIVER(petalynx);
-#endif
-#ifdef CONFIG_HID_SAMSUNG_MODULE
-       HID_COMPAT_CALL_DRIVER(samsung);
-#endif
-#ifdef CONFIG_HID_SONY_MODULE
-       HID_COMPAT_CALL_DRIVER(sony);
-#endif
-#ifdef CONFIG_HID_SUNPLUS_MODULE
-       HID_COMPAT_CALL_DRIVER(sunplus);
-#endif
-#ifdef CONFIG_GREENASIA_FF_MODULE
-       HID_COMPAT_CALL_DRIVER(greenasia);
-#endif
-#ifdef CONFIG_THRUSTMASTER_FF_MODULE
-       HID_COMPAT_CALL_DRIVER(thrustmaster);
-#endif
-#ifdef CONFIG_ZEROPLUS_FF_MODULE
-       HID_COMPAT_CALL_DRIVER(zeroplus);
-#endif
-
-       return -EIO;
-}
-module_init(hid_dummy_init);
-
-MODULE_LICENSE("GPL");
index deb42f931b7ef53edcecb6f8273d2758f9f683e3..0a1fe054799ba2899efb74ab8dbb984be13609f9 100644 (file)
@@ -91,5 +91,3 @@ static void ez_exit(void)
 module_init(ez_init);
 module_exit(ez_exit);
 MODULE_LICENSE("GPL");
-
-HID_COMPAT_LOAD_DRIVER(ezkey);
index 71211f6a4f027161e88b2a85a0a39ec4315b62ee..510ad3ab8d3369b5cb298813310b48790d73ed27 100644 (file)
@@ -181,5 +181,3 @@ static void __exit ga_exit(void)
 module_init(ga_init);
 module_exit(ga_exit);
 MODULE_LICENSE("GPL");
-
-HID_COMPAT_LOAD_DRIVER(greenasia);
index 04a0afec52acede3f6dd087ef7cd576f23e9c60a..d42d222097a80e7cfa049165676b0283c19ac3f7 100644 (file)
@@ -94,5 +94,3 @@ static void gyration_exit(void)
 module_init(gyration_init);
 module_exit(gyration_exit);
 MODULE_LICENSE("GPL");
-
-HID_COMPAT_LOAD_DRIVER(gyration);
index 88511970508d71b8ceae437ae78726ca35563cc8..bdeda4c7cc13ecd00202385050f03136122ff103 100644 (file)
@@ -67,6 +67,9 @@
 #define USB_DEVICE_ID_APPLE_GEYSER4_ANSI       0x021a
 #define USB_DEVICE_ID_APPLE_GEYSER4_ISO        0x021b
 #define USB_DEVICE_ID_APPLE_GEYSER4_JIS        0x021c
+#define USB_DEVICE_ID_APPLE_ALU_MINI_ANSI      0x021d
+#define USB_DEVICE_ID_APPLE_ALU_MINI_ISO       0x021e
+#define USB_DEVICE_ID_APPLE_ALU_MINI_JIS       0x021f
 #define USB_DEVICE_ID_APPLE_ALU_ANSI   0x0220
 #define USB_DEVICE_ID_APPLE_ALU_ISO    0x0221
 #define USB_DEVICE_ID_APPLE_ALU_JIS    0x0222
 #define USB_VENDOR_ID_DMI              0x0c0b
 #define USB_DEVICE_ID_DMI_ENC          0x5fab
 
+#define USB_VENDOR_ID_DRAGONRISE       0x0079
+
 #define USB_VENDOR_ID_ELO              0x04E7
 #define USB_DEVICE_ID_ELO_TS2700       0x0020
 
 #define USB_DEVICE_ID_LD_POWERCONTROL  0x2030
 #define USB_DEVICE_ID_LD_MACHINETEST   0x2040
 
+#define USB_VENDOR_ID_KENSINGTON       0x047d
+#define USB_DEVICE_ID_KS_SLIMBLADE     0x2041
+
 #define USB_VENDOR_ID_LOGITECH         0x046d
 #define USB_DEVICE_ID_LOGITECH_RECEIVER        0xc101
 #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST  0xc110
 #define USB_VENDOR_ID_ZEROPLUS         0x0c12
 
 #define USB_VENDOR_ID_KYE              0x0458
+#define USB_DEVICE_ID_KYE_ERGO_525V    0x0087
 #define USB_DEVICE_ID_KYE_GPEN_560     0x5003
 
+
 #endif
diff --git a/drivers/hid/hid-kensington.c b/drivers/hid/hid-kensington.c
new file mode 100644 (file)
index 0000000..7353bd7
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ *  HID driver for Kensigton Slimblade Trackball
+ *
+ *  Copyright (c) 2009 Jiri Kosina
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define ks_map_key(c)  hid_map_usage(hi, usage, bit, max, EV_KEY, (c))
+
+static int ks_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+               struct hid_field *field, struct hid_usage *usage,
+               unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR)
+               return 0;
+
+       switch (usage->hid & HID_USAGE) {
+       case 0x01: ks_map_key(BTN_MIDDLE);      break;
+       case 0x02: ks_map_key(BTN_SIDE);        break;
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static const struct hid_device_id ks_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, ks_devices);
+
+static struct hid_driver ks_driver = {
+       .name = "kensington",
+       .id_table = ks_devices,
+       .input_mapping = ks_input_mapping,
+};
+
+static int ks_init(void)
+{
+       return hid_register_driver(&ks_driver);
+}
+
+static void ks_exit(void)
+{
+       hid_unregister_driver(&ks_driver);
+}
+
+module_init(ks_init);
+module_exit(ks_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-kye.c b/drivers/hid/hid-kye.c
new file mode 100644 (file)
index 0000000..72ee3fe
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ *  HID driver for Kye/Genius devices not fully compliant with HID standard
+ *
+ *  Copyright (c) 2009 Jiri Kosina
+ *  Copyright (c) 2009 Tomas Hanak
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+/* the fixups that need to be done:
+ *   - change led usage page to button for extra buttons
+ *   - report size 8 count 1 must be size 1 count 8 for button bitfield
+ *   - change the button usage range to 4-7 for the extra buttons
+ */
+static void kye_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int rsize)
+{
+       if (rsize >= 74 &&
+               rdesc[61] == 0x05 && rdesc[62] == 0x08 &&
+               rdesc[63] == 0x19 && rdesc[64] == 0x08 &&
+               rdesc[65] == 0x29 && rdesc[66] == 0x0f &&
+               rdesc[71] == 0x75 && rdesc[72] == 0x08 &&
+               rdesc[73] == 0x95 && rdesc[74] == 0x01) {
+               dev_info(&hdev->dev, "fixing up Kye/Genius Ergo Mouse report "
+                               "descriptor\n");
+               rdesc[62] = 0x09;
+               rdesc[64] = 0x04;
+               rdesc[66] = 0x07;
+               rdesc[72] = 0x01;
+               rdesc[74] = 0x08;
+       }
+}
+
+static const struct hid_device_id kye_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, kye_devices);
+
+static struct hid_driver kye_driver = {
+       .name = "kye",
+       .id_table = kye_devices,
+       .report_fixup = kye_report_fixup,
+};
+
+static int kye_init(void)
+{
+       return hid_register_driver(&kye_driver);
+}
+
+static void kye_exit(void)
+{
+       hid_unregister_driver(&kye_driver);
+}
+
+module_init(kye_init);
+module_exit(kye_exit);
+MODULE_LICENSE("GPL");
index 83e07c9f4144d01de82813ece5077a0f4ed76257..7b80cb694982015e1f2c10fbc7795b9d98f0754c 100644 (file)
@@ -326,5 +326,3 @@ static void lg_exit(void)
 module_init(lg_init);
 module_exit(lg_exit);
 MODULE_LICENSE("GPL");
-
-HID_COMPAT_LOAD_DRIVER(logitech);
index 25b10dcad90d52c38dce6069397a1df19f13c8d0..5e9e37a0506dd96adc8164855fdfac69c7e6fd1e 100644 (file)
@@ -210,5 +210,3 @@ static void ms_exit(void)
 module_init(ms_init);
 module_exit(ms_exit);
 MODULE_LICENSE("GPL");
-
-HID_COMPAT_LOAD_DRIVER(microsoft);
index f3a85a065f18f490190d0e883e25783398f96206..240f87618be6edef76b34b14f87b890da82d16db 100644 (file)
@@ -78,5 +78,3 @@ static void mr_exit(void)
 module_init(mr_init);
 module_exit(mr_exit);
 MODULE_LICENSE("GPL");
-
-HID_COMPAT_LOAD_DRIVER(monterey);
index db44fbd7bdf6aad2eeb9ed11d4af4c5f09bca14c..c5b252be9c217888b46f587a58e41d847142e651 100644 (file)
@@ -78,5 +78,3 @@ static void ntrig_exit(void)
 module_init(ntrig_init);
 module_exit(ntrig_exit);
 MODULE_LICENSE("GPL");
-
-HID_COMPAT_LOAD_DRIVER(ntrig);
index 10945fe12d50eda52239e8c5e35ce0b42a8231ea..2e83e8ff891a47aff5086e89b9b37c5d1767af6b 100644 (file)
@@ -118,5 +118,3 @@ static void pl_exit(void)
 module_init(pl_init);
 module_exit(pl_exit);
 MODULE_LICENSE("GPL");
-
-HID_COMPAT_LOAD_DRIVER(petalynx);
index 46941f979b9d058202f9ce71f8b70e341457c62a..4db9a3483760f6ade53829076198f105161e1cd4 100644 (file)
@@ -9,9 +9,12 @@
  *   - contains two reports, one for each port (HID_QUIRK_MULTI_INPUT)
  *
  *  0e8f:0003 "GreenAsia Inc.    USB Joystick     "
- *   - tested with K??ng Gaming gamepad
+ *   - tested with König Gaming gamepad
  *
- *  Copyright (c) 2007 Anssi Hannula <anssi.hannula@gmail.com>
+ *  0e8f:0003 "GASIA USB Gamepad"
+ *   - another version of the König gamepad
+ *
+ *  Copyright (c) 2007, 2009 Anssi Hannula <anssi.hannula@gmail.com>
  */
 
 /*
@@ -46,6 +49,8 @@
 
 struct plff_device {
        struct hid_report *report;
+       s32 *strong;
+       s32 *weak;
 };
 
 static int hid_plff_play(struct input_dev *dev, void *data,
@@ -62,8 +67,8 @@ static int hid_plff_play(struct input_dev *dev, void *data,
        left = left * 0x7f / 0xffff;
        right = right * 0x7f / 0xffff;
 
-       plff->report->field[0]->value[2] = left;
-       plff->report->field[0]->value[3] = right;
+       *plff->strong = left;
+       *plff->weak = right;
        debug("running with 0x%02x 0x%02x", left, right);
        usbhid_submit_report(hid, plff->report, USB_DIR_OUT);
 
@@ -80,6 +85,8 @@ static int plff_init(struct hid_device *hid)
        struct list_head *report_ptr = report_list;
        struct input_dev *dev;
        int error;
+       s32 *strong;
+       s32 *weak;
 
        /* The device contains one output report per physical device, all
           containing 1 field, which contains 4 ff00.0002 usages and 4 16bit
@@ -87,7 +94,12 @@ static int plff_init(struct hid_device *hid)
 
           The input reports also contain a field which contains
           8 ff00.0001 usages and 8 boolean values. Their meaning is
-          currently unknown. */
+          currently unknown.
+          
+          A version of the 0e8f:0003 exists that has all the values in
+          separate fields and misses the extra input field, thus resembling
+          Zeroplus (hid-zpff) devices.
+       */
 
        if (list_empty(report_list)) {
                dev_err(&hid->dev, "no output reports found\n");
@@ -110,8 +122,21 @@ static int plff_init(struct hid_device *hid)
                        return -ENODEV;
                }
 
-               if (report->field[0]->report_count < 4) {
-                       dev_err(&hid->dev, "not enough values in the field\n");
+               if (report->field[0]->report_count >= 4) {
+                       report->field[0]->value[0] = 0x00;
+                       report->field[0]->value[1] = 0x00;
+                       strong = &report->field[0]->value[2];
+                       weak = &report->field[0]->value[3];
+                       debug("detected single-field device");
+               } else if (report->maxfield >= 4 && report->field[0]->maxusage == 1 &&
+                               report->field[0]->usage[0].hid == (HID_UP_LED | 0x43)) {
+                       report->field[0]->value[0] = 0x00;
+                       report->field[1]->value[0] = 0x00;
+                       strong = &report->field[2]->value[0];
+                       weak = &report->field[3]->value[0];
+                       debug("detected 4-field device");
+               } else {
+                       dev_err(&hid->dev, "not enough fields or values\n");
                        return -ENODEV;
                }
 
@@ -130,10 +155,11 @@ static int plff_init(struct hid_device *hid)
                }
 
                plff->report = report;
-               plff->report->field[0]->value[0] = 0x00;
-               plff->report->field[0]->value[1] = 0x00;
-               plff->report->field[0]->value[2] = 0x00;
-               plff->report->field[0]->value[3] = 0x00;
+               plff->strong = strong;
+               plff->weak = weak;
+
+               *strong = 0x00;
+               *weak = 0x00;
                usbhid_submit_report(hid, plff->report, USB_DIR_OUT);
        }
 
@@ -180,7 +206,7 @@ static const struct hid_device_id pl_devices[] = {
                .driver_data = 1 }, /* Twin USB Joystick */
        { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR),
                .driver_data = 1 }, /* Twin USB Joystick */
-       { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003), }, /* GreenAsia Inc. USB Joystick */
+       { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003), },
        { }
 };
 MODULE_DEVICE_TABLE(hid, pl_devices);
@@ -204,5 +230,3 @@ static void pl_exit(void)
 module_init(pl_init);
 module_exit(pl_exit);
 MODULE_LICENSE("GPL");
-
-HID_COMPAT_LOAD_DRIVER(pantherlord);
index 15f3c04924506008eb6d3f85a2c9a21393fa87c3..07083aa6c19ae615a81338c77855d143162df113 100644 (file)
@@ -96,5 +96,3 @@ static void samsung_exit(void)
 module_init(samsung_init);
 module_exit(samsung_exit);
 MODULE_LICENSE("GPL");
-
-HID_COMPAT_LOAD_DRIVER(samsung);
index dd5a3979a4defd1e606669da4ccc91800a0c60c3..c2599388a3504ce4df3bb110f976dfab47581cf5 100644 (file)
@@ -148,5 +148,3 @@ static void sony_exit(void)
 module_init(sony_init);
 module_exit(sony_exit);
 MODULE_LICENSE("GPL");
-
-HID_COMPAT_LOAD_DRIVER(sony);
index 5ba68f7dbb780e1626de6ff1be615e27a8f3a162..e0a8fd36a85b70e174d4763ea50e9d709bb456fd 100644 (file)
@@ -78,5 +78,3 @@ static void sp_exit(void)
 module_init(sp_init);
 module_exit(sp_exit);
 MODULE_LICENSE("GPL");
-
-HID_COMPAT_LOAD_DRIVER(sunplus);
index 1b7cba0f7e1f71b289f984ba7b26f95dfdab173c..7c1f7b50330cd6d5bd6b13e9388560c2ef3a5950 100644 (file)
@@ -265,5 +265,3 @@ static void tm_exit(void)
 module_init(tm_init);
 module_exit(tm_exit);
 MODULE_LICENSE("GPL");
-
-HID_COMPAT_LOAD_DRIVER(thrustmaster);
index cca64a0564a9ed3cb2b369bc8d842c5ea8ac7a18..152ccfabeba5cf59d523d59feeac2e0abde04f4a 100644 (file)
@@ -73,5 +73,3 @@ static void ts_exit(void)
 module_init(ts_init);
 module_exit(ts_exit);
 MODULE_LICENSE("GPL");
-
-HID_COMPAT_LOAD_DRIVER(topseed);
index ea82f3718b218c2d6262a5b7f50db3f627ddfde1..85a198a185372967fa487a744d1d5517216a9136 100644 (file)
@@ -158,5 +158,3 @@ static void zp_exit(void)
 module_init(zp_init);
 module_exit(zp_exit);
 MODULE_LICENSE("GPL");
-
-HID_COMPAT_LOAD_DRIVER(zeroplus);
index 02b19db5442ebd1d1fce7c0716bb2a071f528e12..e263d47311790b7e215bfd4dbf119999e87064aa 100644 (file)
@@ -181,9 +181,17 @@ static int hidraw_open(struct inode *inode, struct file *file)
 
        dev = hidraw_table[minor];
        if (!dev->open++) {
+               if (dev->hid->ll_driver->power) {
+                       err = dev->hid->ll_driver->power(dev->hid, PM_HINT_FULLON);
+                       if (err < 0)
+                               goto out_unlock;
+               }
                err = dev->hid->ll_driver->open(dev->hid);
-               if (err < 0)
+               if (err < 0) {
+                       if (dev->hid->ll_driver->power)
+                               dev->hid->ll_driver->power(dev->hid, PM_HINT_NORMAL);
                        dev->open--;
+               }
        }
 
 out_unlock:
@@ -209,10 +217,13 @@ static int hidraw_release(struct inode * inode, struct file * file)
        list_del(&list->node);
        dev = hidraw_table[minor];
        if (!--dev->open) {
-               if (list->hidraw->exist)
+               if (list->hidraw->exist) {
+                       if (dev->hid->ll_driver->power)
+                               dev->hid->ll_driver->power(dev->hid, PM_HINT_NORMAL);
                        dev->hid->ll_driver->close(dev->hid);
-               else
+               } else {
                        kfree(list->hidraw);
+               }
        }
 
        kfree(list);
index f0a0f72238aba24c66c8ecaab0ca917064609add..4306cb1b8ce5c91275be6566825ce02ec6f210fb 100644 (file)
@@ -5,6 +5,7 @@
  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
  *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
  *  Copyright (c) 2006-2008 Jiri Kosina
+ *  Copyright (c) 2007-2008 Oliver Neukum
  */
 
 /*
@@ -27,6 +28,7 @@
 #include <asm/byteorder.h>
 #include <linux/input.h>
 #include <linux/wait.h>
+#include <linux/workqueue.h>
 
 #include <linux/usb.h>
 
@@ -53,6 +55,10 @@ static unsigned int hid_mousepoll_interval;
 module_param_named(mousepoll, hid_mousepoll_interval, uint, 0644);
 MODULE_PARM_DESC(mousepoll, "Polling interval of mice");
 
+static unsigned int ignoreled;
+module_param_named(ignoreled, ignoreled, uint, 0644);
+MODULE_PARM_DESC(ignoreled, "Autosuspend with active leds");
+
 /* Quirks specified at module load time */
 static char *quirks_param[MAX_USBHID_BOOT_QUIRKS] = { [ 0 ... (MAX_USBHID_BOOT_QUIRKS - 1) ] = NULL };
 module_param_array_named(quirks, quirks_param, charp, NULL, 0444);
@@ -63,8 +69,13 @@ MODULE_PARM_DESC(quirks, "Add/modify USB HID quirks by specifying "
 /*
  * Input submission and I/O error handler.
  */
+static DEFINE_MUTEX(hid_open_mut);
+static struct workqueue_struct *resumption_waker;
 
 static void hid_io_error(struct hid_device *hid);
+static int hid_submit_out(struct hid_device *hid);
+static int hid_submit_ctrl(struct hid_device *hid);
+static void hid_cancel_delayed_stuff(struct usbhid_device *usbhid);
 
 /* Start up the input URB */
 static int hid_start_in(struct hid_device *hid)
@@ -73,15 +84,16 @@ static int hid_start_in(struct hid_device *hid)
        int rc = 0;
        struct usbhid_device *usbhid = hid->driver_data;
 
-       spin_lock_irqsave(&usbhid->inlock, flags);
-       if (hid->open > 0 && !test_bit(HID_SUSPENDED, &usbhid->iofl) &&
+       spin_lock_irqsave(&usbhid->lock, flags);
+       if (hid->open > 0 &&
                        !test_bit(HID_DISCONNECTED, &usbhid->iofl) &&
+                       !test_bit(HID_REPORTED_IDLE, &usbhid->iofl) &&
                        !test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) {
                rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC);
                if (rc != 0)
                        clear_bit(HID_IN_RUNNING, &usbhid->iofl);
        }
-       spin_unlock_irqrestore(&usbhid->inlock, flags);
+       spin_unlock_irqrestore(&usbhid->lock, flags);
        return rc;
 }
 
@@ -145,7 +157,7 @@ static void hid_io_error(struct hid_device *hid)
        unsigned long flags;
        struct usbhid_device *usbhid = hid->driver_data;
 
-       spin_lock_irqsave(&usbhid->inlock, flags);
+       spin_lock_irqsave(&usbhid->lock, flags);
 
        /* Stop when disconnected */
        if (test_bit(HID_DISCONNECTED, &usbhid->iofl))
@@ -175,7 +187,51 @@ static void hid_io_error(struct hid_device *hid)
        mod_timer(&usbhid->io_retry,
                        jiffies + msecs_to_jiffies(usbhid->retry_delay));
 done:
-       spin_unlock_irqrestore(&usbhid->inlock, flags);
+       spin_unlock_irqrestore(&usbhid->lock, flags);
+}
+
+static void usbhid_mark_busy(struct usbhid_device *usbhid)
+{
+       struct usb_interface *intf = usbhid->intf;
+
+       usb_mark_last_busy(interface_to_usbdev(intf));
+}
+
+static int usbhid_restart_out_queue(struct usbhid_device *usbhid)
+{
+       struct hid_device *hid = usb_get_intfdata(usbhid->intf);
+       int kicked;
+
+       if (!hid)
+               return 0;
+
+       if ((kicked = (usbhid->outhead != usbhid->outtail))) {
+               dbg("Kicking head %d tail %d", usbhid->outhead, usbhid->outtail);
+               if (hid_submit_out(hid)) {
+                       clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
+                       wake_up(&usbhid->wait);
+               }
+       }
+       return kicked;
+}
+
+static int usbhid_restart_ctrl_queue(struct usbhid_device *usbhid)
+{
+       struct hid_device *hid = usb_get_intfdata(usbhid->intf);
+       int kicked;
+
+       WARN_ON(hid == NULL);
+       if (!hid)
+               return 0;
+
+       if ((kicked = (usbhid->ctrlhead != usbhid->ctrltail))) {
+               dbg("Kicking head %d tail %d", usbhid->ctrlhead, usbhid->ctrltail);
+               if (hid_submit_ctrl(hid)) {
+                       clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
+                       wake_up(&usbhid->wait);
+               }
+       }
+       return kicked;
 }
 
 /*
@@ -190,12 +246,23 @@ static void hid_irq_in(struct urb *urb)
 
        switch (urb->status) {
        case 0:                 /* success */
+               usbhid_mark_busy(usbhid);
                usbhid->retry_delay = 0;
                hid_input_report(urb->context, HID_INPUT_REPORT,
                                 urb->transfer_buffer,
                                 urb->actual_length, 1);
+               /*
+                * autosuspend refused while keys are pressed
+                * because most keyboards don't wake up when
+                * a key is released
+                */
+               if (hid_check_keys_pressed(hid))
+                       set_bit(HID_KEYS_PRESSED, &usbhid->iofl);
+               else
+                       clear_bit(HID_KEYS_PRESSED, &usbhid->iofl);
                break;
        case -EPIPE:            /* stall */
+               usbhid_mark_busy(usbhid);
                clear_bit(HID_IN_RUNNING, &usbhid->iofl);
                set_bit(HID_CLEAR_HALT, &usbhid->iofl);
                schedule_work(&usbhid->reset_work);
@@ -209,6 +276,7 @@ static void hid_irq_in(struct urb *urb)
        case -EPROTO:           /* protocol error or unplug */
        case -ETIME:            /* protocol error or unplug */
        case -ETIMEDOUT:        /* Should never happen, but... */
+               usbhid_mark_busy(usbhid);
                clear_bit(HID_IN_RUNNING, &usbhid->iofl);
                hid_io_error(hid);
                return;
@@ -239,16 +307,25 @@ static int hid_submit_out(struct hid_device *hid)
        report = usbhid->out[usbhid->outtail].report;
        raw_report = usbhid->out[usbhid->outtail].raw_report;
 
-       usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0);
-       usbhid->urbout->dev = hid_to_usb_dev(hid);
-       memcpy(usbhid->outbuf, raw_report, usbhid->urbout->transfer_buffer_length);
-       kfree(raw_report);
+       if (!test_bit(HID_REPORTED_IDLE, &usbhid->iofl)) {
+               usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0);
+               usbhid->urbout->dev = hid_to_usb_dev(hid);
+               memcpy(usbhid->outbuf, raw_report, usbhid->urbout->transfer_buffer_length);
+               kfree(raw_report);
 
-       dbg_hid("submitting out urb\n");
+               dbg_hid("submitting out urb\n");
 
-       if (usb_submit_urb(usbhid->urbout, GFP_ATOMIC)) {
-               err_hid("usb_submit_urb(out) failed");
-               return -1;
+               if (usb_submit_urb(usbhid->urbout, GFP_ATOMIC)) {
+                       err_hid("usb_submit_urb(out) failed");
+                       return -1;
+               }
+       } else {
+               /*
+                * queue work to wake up the device.
+                * as the work queue is freezeable, this is safe
+                * with respect to STD and STR
+                */
+               queue_work(resumption_waker, &usbhid->restart_work);
        }
 
        return 0;
@@ -266,41 +343,50 @@ static int hid_submit_ctrl(struct hid_device *hid)
        raw_report = usbhid->ctrl[usbhid->ctrltail].raw_report;
        dir = usbhid->ctrl[usbhid->ctrltail].dir;
 
-       len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
-       if (dir == USB_DIR_OUT) {
-               usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0);
-               usbhid->urbctrl->transfer_buffer_length = len;
-               memcpy(usbhid->ctrlbuf, raw_report, len);
-               kfree(raw_report);
-       } else {
-               int maxpacket, padlen;
-
-               usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0);
-               maxpacket = usb_maxpacket(hid_to_usb_dev(hid), usbhid->urbctrl->pipe, 0);
-               if (maxpacket > 0) {
-                       padlen = DIV_ROUND_UP(len, maxpacket);
-                       padlen *= maxpacket;
-                       if (padlen > usbhid->bufsize)
-                               padlen = usbhid->bufsize;
-               } else
-                       padlen = 0;
-               usbhid->urbctrl->transfer_buffer_length = padlen;
-       }
-       usbhid->urbctrl->dev = hid_to_usb_dev(hid);
+       if (!test_bit(HID_REPORTED_IDLE, &usbhid->iofl)) {
+               len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
+               if (dir == USB_DIR_OUT) {
+                       usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0);
+                       usbhid->urbctrl->transfer_buffer_length = len;
+                       memcpy(usbhid->ctrlbuf, raw_report, len);
+                       kfree(raw_report);
+               } else {
+                       int maxpacket, padlen;
+
+                       usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0);
+                       maxpacket = usb_maxpacket(hid_to_usb_dev(hid), usbhid->urbctrl->pipe, 0);
+                       if (maxpacket > 0) {
+                               padlen = DIV_ROUND_UP(len, maxpacket);
+                               padlen *= maxpacket;
+                               if (padlen > usbhid->bufsize)
+                                       padlen = usbhid->bufsize;
+                       } else
+                               padlen = 0;
+                       usbhid->urbctrl->transfer_buffer_length = padlen;
+               }
+               usbhid->urbctrl->dev = hid_to_usb_dev(hid);
 
-       usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir;
-       usbhid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : HID_REQ_GET_REPORT;
-       usbhid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) | report->id);
-       usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum);
-       usbhid->cr->wLength = cpu_to_le16(len);
+               usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir;
+               usbhid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : HID_REQ_GET_REPORT;
+               usbhid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) | report->id);
+               usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum);
+               usbhid->cr->wLength = cpu_to_le16(len);
 
-       dbg_hid("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u\n",
-               usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report",
-               usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength);
+               dbg_hid("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u\n",
+                       usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report",
+                       usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength);
 
-       if (usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC)) {
-               err_hid("usb_submit_urb(ctrl) failed");
-               return -1;
+               if (usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC)) {
+                       err_hid("usb_submit_urb(ctrl) failed");
+                       return -1;
+               }
+       } else {
+               /*
+                * queue work to wake up the device.
+                * as the work queue is freezeable, this is safe
+                * with respect to STD and STR
+                */
+               queue_work(resumption_waker, &usbhid->restart_work);
        }
 
        return 0;
@@ -332,7 +418,7 @@ static void hid_irq_out(struct urb *urb)
                                "received\n", urb->status);
        }
 
-       spin_lock_irqsave(&usbhid->outlock, flags);
+       spin_lock_irqsave(&usbhid->lock, flags);
 
        if (unplug)
                usbhid->outtail = usbhid->outhead;
@@ -344,12 +430,12 @@ static void hid_irq_out(struct urb *urb)
                        clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
                        wake_up(&usbhid->wait);
                }
-               spin_unlock_irqrestore(&usbhid->outlock, flags);
+               spin_unlock_irqrestore(&usbhid->lock, flags);
                return;
        }
 
        clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
-       spin_unlock_irqrestore(&usbhid->outlock, flags);
+       spin_unlock_irqrestore(&usbhid->lock, flags);
        wake_up(&usbhid->wait);
 }
 
@@ -361,12 +447,11 @@ static void hid_ctrl(struct urb *urb)
 {
        struct hid_device *hid = urb->context;
        struct usbhid_device *usbhid = hid->driver_data;
-       unsigned long flags;
-       int unplug = 0;
+       int unplug = 0, status = urb->status;
 
-       spin_lock_irqsave(&usbhid->ctrllock, flags);
+       spin_lock(&usbhid->lock);
 
-       switch (urb->status) {
+       switch (status) {
        case 0:                 /* success */
                if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN)
                        hid_input_report(urb->context,
@@ -383,7 +468,7 @@ static void hid_ctrl(struct urb *urb)
                break;
        default:                /* error */
                dev_warn(&urb->dev->dev, "ctrl urb status %d "
-                               "received\n", urb->status);
+                               "received\n", status);
        }
 
        if (unplug)
@@ -396,19 +481,18 @@ static void hid_ctrl(struct urb *urb)
                        clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
                        wake_up(&usbhid->wait);
                }
-               spin_unlock_irqrestore(&usbhid->ctrllock, flags);
+               spin_unlock(&usbhid->lock);
                return;
        }
 
        clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
-       spin_unlock_irqrestore(&usbhid->ctrllock, flags);
+       spin_unlock(&usbhid->lock);
        wake_up(&usbhid->wait);
 }
 
-void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir)
+void __usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir)
 {
        int head;
-       unsigned long flags;
        struct usbhid_device *usbhid = hid->driver_data;
        int len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
 
@@ -416,18 +500,13 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns
                return;
 
        if (usbhid->urbout && dir == USB_DIR_OUT && report->type == HID_OUTPUT_REPORT) {
-
-               spin_lock_irqsave(&usbhid->outlock, flags);
-
                if ((head = (usbhid->outhead + 1) & (HID_OUTPUT_FIFO_SIZE - 1)) == usbhid->outtail) {
-                       spin_unlock_irqrestore(&usbhid->outlock, flags);
                        dev_warn(&hid->dev, "output queue full\n");
                        return;
                }
 
                usbhid->out[usbhid->outhead].raw_report = kmalloc(len, GFP_ATOMIC);
                if (!usbhid->out[usbhid->outhead].raw_report) {
-                       spin_unlock_irqrestore(&usbhid->outlock, flags);
                        dev_warn(&hid->dev, "output queueing failed\n");
                        return;
                }
@@ -438,15 +517,10 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns
                if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl))
                        if (hid_submit_out(hid))
                                clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
-
-               spin_unlock_irqrestore(&usbhid->outlock, flags);
                return;
        }
 
-       spin_lock_irqsave(&usbhid->ctrllock, flags);
-
        if ((head = (usbhid->ctrlhead + 1) & (HID_CONTROL_FIFO_SIZE - 1)) == usbhid->ctrltail) {
-               spin_unlock_irqrestore(&usbhid->ctrllock, flags);
                dev_warn(&hid->dev, "control queue full\n");
                return;
        }
@@ -454,7 +528,6 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns
        if (dir == USB_DIR_OUT) {
                usbhid->ctrl[usbhid->ctrlhead].raw_report = kmalloc(len, GFP_ATOMIC);
                if (!usbhid->ctrl[usbhid->ctrlhead].raw_report) {
-                       spin_unlock_irqrestore(&usbhid->ctrllock, flags);
                        dev_warn(&hid->dev, "control queueing failed\n");
                        return;
                }
@@ -467,15 +540,25 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns
        if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl))
                if (hid_submit_ctrl(hid))
                        clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
+}
+
+void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir)
+{
+       struct usbhid_device *usbhid = hid->driver_data;
+       unsigned long flags;
 
-       spin_unlock_irqrestore(&usbhid->ctrllock, flags);
+       spin_lock_irqsave(&usbhid->lock, flags);
+       __usbhid_submit_report(hid, report, dir);
+       spin_unlock_irqrestore(&usbhid->lock, flags);
 }
 EXPORT_SYMBOL_GPL(usbhid_submit_report);
 
 static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
 {
        struct hid_device *hid = input_get_drvdata(dev);
+       struct usbhid_device *usbhid = hid->driver_data;
        struct hid_field *field;
+       unsigned long flags;
        int offset;
 
        if (type == EV_FF)
@@ -490,6 +573,15 @@ static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, un
        }
 
        hid_set_field(field, offset, value);
+       if (value) {
+               spin_lock_irqsave(&usbhid->lock, flags);
+               usbhid->ledcount++;
+               spin_unlock_irqrestore(&usbhid->lock, flags);
+       } else {
+               spin_lock_irqsave(&usbhid->lock, flags);
+               usbhid->ledcount--;
+               spin_unlock_irqrestore(&usbhid->lock, flags);
+       }
        usbhid_submit_report(hid, field->report, USB_DIR_OUT);
 
        return 0;
@@ -538,15 +630,22 @@ int usbhid_open(struct hid_device *hid)
        struct usbhid_device *usbhid = hid->driver_data;
        int res;
 
+       mutex_lock(&hid_open_mut);
        if (!hid->open++) {
                res = usb_autopm_get_interface(usbhid->intf);
+               /* the device must be awake to reliable request remote wakeup */
                if (res < 0) {
                        hid->open--;
+                       mutex_unlock(&hid_open_mut);
                        return -EIO;
                }
+               usbhid->intf->needs_remote_wakeup = 1;
+               if (hid_start_in(hid))
+                       hid_io_error(hid);
+               usb_autopm_put_interface(usbhid->intf);
        }
-       if (hid_start_in(hid))
-               hid_io_error(hid);
+       mutex_unlock(&hid_open_mut);
        return 0;
 }
 
@@ -554,10 +653,22 @@ void usbhid_close(struct hid_device *hid)
 {
        struct usbhid_device *usbhid = hid->driver_data;
 
+       mutex_lock(&hid_open_mut);
+
+       /* protecting hid->open to make sure we don't restart
+        * data acquistion due to a resumption we no longer
+        * care about
+        */
+       spin_lock_irq(&usbhid->lock);
        if (!--hid->open) {
+               spin_unlock_irq(&usbhid->lock);
                usb_kill_urb(usbhid->urbin);
-               usb_autopm_put_interface(usbhid->intf);
+               flush_scheduled_work();
+               usbhid->intf->needs_remote_wakeup = 0;
+       } else {
+               spin_unlock_irq(&usbhid->lock);
        }
+       mutex_unlock(&hid_open_mut);
 }
 
 /*
@@ -687,6 +798,25 @@ static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t co
        return ret;
 }
 
+static void usbhid_restart_queues(struct usbhid_device *usbhid)
+{
+       if (usbhid->urbout)
+               usbhid_restart_out_queue(usbhid);
+       usbhid_restart_ctrl_queue(usbhid);
+}
+
+static void __usbhid_restart_queues(struct work_struct *work)
+{
+       struct usbhid_device *usbhid =
+               container_of(work, struct usbhid_device, restart_work);
+       int r;
+
+       r = usb_autopm_get_interface(usbhid->intf);
+       if (r < 0)
+               return;
+       usb_autopm_put_interface(usbhid->intf);
+}
+
 static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid)
 {
        struct usbhid_device *usbhid = hid->driver_data;
@@ -711,6 +841,9 @@ static int usbhid_parse(struct hid_device *hid)
        quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor),
                        le16_to_cpu(dev->descriptor.idProduct));
 
+       if (quirks & HID_QUIRK_IGNORE)
+               return -ENODEV;
+
        /* Many keyboards and mice don't like to be polled for reports,
         * so we will always set the HID_QUIRK_NOGET flag for them. */
        if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT) {
@@ -850,11 +983,11 @@ static int usbhid_start(struct hid_device *hid)
 
        init_waitqueue_head(&usbhid->wait);
        INIT_WORK(&usbhid->reset_work, hid_reset);
+       INIT_WORK(&usbhid->restart_work, __usbhid_restart_queues);
        setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid);
 
-       spin_lock_init(&usbhid->inlock);
-       spin_lock_init(&usbhid->outlock);
-       spin_lock_init(&usbhid->ctrllock);
+       spin_lock_init(&usbhid->lock);
+       spin_lock_init(&usbhid->lock);
 
        usbhid->intf = intf;
        usbhid->ifnum = interface->desc.bInterfaceNumber;
@@ -906,15 +1039,14 @@ static void usbhid_stop(struct hid_device *hid)
                return;
 
        clear_bit(HID_STARTED, &usbhid->iofl);
-       spin_lock_irq(&usbhid->inlock); /* Sync with error handler */
+       spin_lock_irq(&usbhid->lock);   /* Sync with error handler */
        set_bit(HID_DISCONNECTED, &usbhid->iofl);
-       spin_unlock_irq(&usbhid->inlock);
+       spin_unlock_irq(&usbhid->lock);
        usb_kill_urb(usbhid->urbin);
        usb_kill_urb(usbhid->urbout);
        usb_kill_urb(usbhid->urbctrl);
 
-       del_timer_sync(&usbhid->io_retry);
-       cancel_work_sync(&usbhid->reset_work);
+       hid_cancel_delayed_stuff(usbhid);
 
        if (hid->claimed & HID_CLAIMED_INPUT)
                hidinput_disconnect(hid);
@@ -935,12 +1067,28 @@ static void usbhid_stop(struct hid_device *hid)
        hid_free_buffers(hid_to_usb_dev(hid), hid);
 }
 
+static int usbhid_power(struct hid_device *hid, int lvl)
+{
+       int r = 0;
+
+       switch (lvl) {
+       case PM_HINT_FULLON:
+               r = usbhid_get_power(hid);
+               break;
+       case PM_HINT_NORMAL:
+               usbhid_put_power(hid);
+               break;
+       }
+       return r;
+}
+
 static struct hid_ll_driver usb_hid_driver = {
        .parse = usbhid_parse,
        .start = usbhid_start,
        .stop = usbhid_stop,
        .open = usbhid_open,
        .close = usbhid_close,
+       .power = usbhid_power,
        .hidinput_input_event = usb_hidinput_input_event,
 };
 
@@ -1049,19 +1197,126 @@ static void hid_disconnect(struct usb_interface *intf)
        kfree(usbhid);
 }
 
+static void hid_cancel_delayed_stuff(struct usbhid_device *usbhid)
+{
+       del_timer_sync(&usbhid->io_retry);
+       cancel_work_sync(&usbhid->restart_work);
+       cancel_work_sync(&usbhid->reset_work);
+}
+
+static void hid_cease_io(struct usbhid_device *usbhid)
+{
+       del_timer(&usbhid->io_retry);
+       usb_kill_urb(usbhid->urbin);
+       usb_kill_urb(usbhid->urbctrl);
+       usb_kill_urb(usbhid->urbout);
+}
+
+/* Treat USB reset pretty much the same as suspend/resume */
+static int hid_pre_reset(struct usb_interface *intf)
+{
+       struct hid_device *hid = usb_get_intfdata(intf);
+       struct usbhid_device *usbhid = hid->driver_data;
+
+       spin_lock_irq(&usbhid->lock);
+       set_bit(HID_RESET_PENDING, &usbhid->iofl);
+       spin_unlock_irq(&usbhid->lock);
+       cancel_work_sync(&usbhid->restart_work);
+       hid_cease_io(usbhid);
+
+       return 0;
+}
+
+/* Same routine used for post_reset and reset_resume */
+static int hid_post_reset(struct usb_interface *intf)
+{
+       struct usb_device *dev = interface_to_usbdev (intf);
+       struct hid_device *hid = usb_get_intfdata(intf);
+       struct usbhid_device *usbhid = hid->driver_data;
+       int status;
+       spin_lock_irq(&usbhid->lock);
+       clear_bit(HID_RESET_PENDING, &usbhid->iofl);
+       spin_unlock_irq(&usbhid->lock);
+       hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0);
+       /* FIXME: Any more reinitialization needed? */
+       status = hid_start_in(hid);
+       if (status < 0)
+               hid_io_error(hid);
+       usbhid_restart_queues(usbhid);
+
+       return 0;
+}
+
+int usbhid_get_power(struct hid_device *hid)
+{
+       struct usbhid_device *usbhid = hid->driver_data;
+       return usb_autopm_get_interface(usbhid->intf);
+}
+
+void usbhid_put_power(struct hid_device *hid)
+{
+       struct usbhid_device *usbhid = hid->driver_data;
+       usb_autopm_put_interface(usbhid->intf);
+}
+
+
+#ifdef CONFIG_PM
 static int hid_suspend(struct usb_interface *intf, pm_message_t message)
 {
-       struct hid_device *hid = usb_get_intfdata (intf);
+       struct hid_device *hid = usb_get_intfdata(intf);
        struct usbhid_device *usbhid = hid->driver_data;
+       struct usb_device *udev = interface_to_usbdev(intf);
+       int status;
 
-       if (!test_bit(HID_STARTED, &usbhid->iofl))
-               return 0;
+       if (udev->auto_pm) {
+               spin_lock_irq(&usbhid->lock);   /* Sync with error handler */
+               if (!test_bit(HID_RESET_PENDING, &usbhid->iofl)
+                   && !test_bit(HID_CLEAR_HALT, &usbhid->iofl)
+                   && !test_bit(HID_OUT_RUNNING, &usbhid->iofl)
+                   && !test_bit(HID_CTRL_RUNNING, &usbhid->iofl)
+                   && !test_bit(HID_KEYS_PRESSED, &usbhid->iofl)
+                   && (!usbhid->ledcount || ignoreled))
+               {
+                       set_bit(HID_REPORTED_IDLE, &usbhid->iofl);
+                       spin_unlock_irq(&usbhid->lock);
+               } else {
+                       usbhid_mark_busy(usbhid);
+                       spin_unlock_irq(&usbhid->lock);
+                       return -EBUSY;
+               }
 
-       spin_lock_irq(&usbhid->inlock); /* Sync with error handler */
-       set_bit(HID_SUSPENDED, &usbhid->iofl);
-       spin_unlock_irq(&usbhid->inlock);
-       del_timer_sync(&usbhid->io_retry);
-       usb_kill_urb(usbhid->urbin);
+       } else {
+               spin_lock_irq(&usbhid->lock);
+               set_bit(HID_REPORTED_IDLE, &usbhid->iofl);
+               spin_unlock_irq(&usbhid->lock);
+               if (usbhid_wait_io(hid) < 0)
+                       return -EIO;
+       }
+
+       if (!ignoreled && udev->auto_pm) {
+               spin_lock_irq(&usbhid->lock);
+               if (test_bit(HID_LED_ON, &usbhid->iofl)) {
+                       spin_unlock_irq(&usbhid->lock);
+                       usbhid_mark_busy(usbhid);
+                       return -EBUSY;
+               }
+               spin_unlock_irq(&usbhid->lock);
+       }
+
+       hid_cancel_delayed_stuff(usbhid);
+       hid_cease_io(usbhid);
+
+       if (udev->auto_pm && test_bit(HID_KEYS_PRESSED, &usbhid->iofl)) {
+               /* lost race against keypresses */
+               status = hid_start_in(hid);
+               if (status < 0)
+                       hid_io_error(hid);
+               usbhid_mark_busy(usbhid);
+               return -EBUSY;
+       }
        dev_dbg(&intf->dev, "suspend\n");
        return 0;
 }
@@ -1075,32 +1330,33 @@ static int hid_resume(struct usb_interface *intf)
        if (!test_bit(HID_STARTED, &usbhid->iofl))
                return 0;
 
-       clear_bit(HID_SUSPENDED, &usbhid->iofl);
+       clear_bit(HID_REPORTED_IDLE, &usbhid->iofl);
+       usbhid_mark_busy(usbhid);
+
+       if (test_bit(HID_CLEAR_HALT, &usbhid->iofl) ||
+           test_bit(HID_RESET_PENDING, &usbhid->iofl))
+               schedule_work(&usbhid->reset_work);
        usbhid->retry_delay = 0;
        status = hid_start_in(hid);
-       dev_dbg(&intf->dev, "resume status %d\n", status);
-       return status;
-}
+       if (status < 0)
+               hid_io_error(hid);
+       usbhid_restart_queues(usbhid);
 
-/* Treat USB reset pretty much the same as suspend/resume */
-static int hid_pre_reset(struct usb_interface *intf)
-{
-       /* FIXME: What if the interface is already suspended? */
-       hid_suspend(intf, PMSG_ON);
+       dev_dbg(&intf->dev, "resume status %d\n", status);
        return 0;
 }
 
-/* Same routine used for post_reset and reset_resume */
-static int hid_post_reset(struct usb_interface *intf)
+static int hid_reset_resume(struct usb_interface *intf)
 {
-       struct usb_device *dev = interface_to_usbdev (intf);
-
-       hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0);
-       /* FIXME: Any more reinitialization needed? */
+       struct hid_device *hid = usb_get_intfdata(intf);
+       struct usbhid_device *usbhid = hid->driver_data;
 
-       return hid_resume(intf);
+       clear_bit(HID_REPORTED_IDLE, &usbhid->iofl);
+       return hid_post_reset(intf);
 }
 
+#endif /* CONFIG_PM */
+
 static struct usb_device_id hid_usb_ids [] = {
        { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
                .bInterfaceClass = USB_INTERFACE_CLASS_HID },
@@ -1113,9 +1369,11 @@ static struct usb_driver hid_driver = {
        .name =         "usbhid",
        .probe =        hid_probe,
        .disconnect =   hid_disconnect,
+#ifdef CONFIG_PM
        .suspend =      hid_suspend,
        .resume =       hid_resume,
-       .reset_resume = hid_post_reset,
+       .reset_resume = hid_reset_resume,
+#endif
        .pre_reset =    hid_pre_reset,
        .post_reset =   hid_post_reset,
        .id_table =     hid_usb_ids,
@@ -1134,7 +1392,11 @@ static struct hid_driver hid_usb_driver = {
 
 static int __init hid_init(void)
 {
-       int retval;
+       int retval = -ENOMEM;
+
+       resumption_waker = create_freezeable_workqueue("usbhid_resumer");
+       if (!resumption_waker)
+               goto no_queue;
        retval = hid_register_driver(&hid_usb_driver);
        if (retval)
                goto hid_register_fail;
@@ -1158,6 +1420,8 @@ hiddev_init_fail:
 usbhid_quirks_init_fail:
        hid_unregister_driver(&hid_usb_driver);
 hid_register_fail:
+       destroy_workqueue(resumption_waker);
+no_queue:
        return retval;
 }
 
@@ -1167,6 +1431,7 @@ static void __exit hid_exit(void)
        hiddev_exit();
        usbhid_quirks_exit();
        hid_unregister_driver(&hid_usb_driver);
+       destroy_workqueue(resumption_waker);
 }
 
 module_init(hid_init);
index aa214170baf414c0b9e1459415dd537be32325a8..e9b436d2d94434c17fb1030d50528341edd71659 100644 (file)
@@ -246,10 +246,12 @@ static int hiddev_release(struct inode * inode, struct file * file)
        spin_unlock_irqrestore(&list->hiddev->list_lock, flags);
 
        if (!--list->hiddev->open) {
-               if (list->hiddev->exist)
+               if (list->hiddev->exist) {
                        usbhid_close(list->hiddev->hid);
-               else
+                       usbhid_put_power(list->hiddev->hid);
+               } else {
                        kfree(list->hiddev);
+               }
        }
 
        kfree(list);
@@ -300,6 +302,17 @@ static int hiddev_open(struct inode *inode, struct file *file)
        list_add_tail(&list->node, &hiddev_table[i]->list);
        spin_unlock_irq(&list->hiddev->list_lock);
 
+       if (!list->hiddev->open++)
+               if (list->hiddev->exist) {
+                       struct hid_device *hid = hiddev_table[i]->hid;
+                       res = usbhid_get_power(hid);
+                       if (res < 0) {
+                               res = -EIO;
+                               goto bail;
+                       }
+                       usbhid_open(hid);
+               }
+
        return 0;
 bail:
        file->private_data = NULL;
@@ -875,16 +888,21 @@ 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 */
+       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;
 }
index 9eb30564be9c0c8fba2d92ccb958ba3ed6c08913..08f505ca2e3db47426a2940fb2876a2e89ba9e14 100644 (file)
@@ -38,7 +38,10 @@ int usbhid_wait_io(struct hid_device* hid);
 void usbhid_close(struct hid_device *hid);
 int usbhid_open(struct hid_device *hid);
 void usbhid_init_reports(struct hid_device *hid);
-void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir);
+void usbhid_submit_report
+(struct hid_device *hid, struct hid_report *report, unsigned char dir);
+int usbhid_get_power(struct hid_device *hid);
+void usbhid_put_power(struct hid_device *hid);
 
 /* iofl flags */
 #define HID_CTRL_RUNNING       1
@@ -49,6 +52,9 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns
 #define HID_CLEAR_HALT         6
 #define HID_DISCONNECTED       7
 #define HID_STARTED            8
+#define HID_REPORTED_IDLE      9
+#define HID_KEYS_PRESSED       10
+#define HID_LED_ON             11
 
 /*
  * USB-specific HID struct, to be pointed to
@@ -66,7 +72,6 @@ struct usbhid_device {
        struct urb *urbin;                                              /* Input URB */
        char *inbuf;                                                    /* Input buffer */
        dma_addr_t inbuf_dma;                                           /* Input buffer dma */
-       spinlock_t inlock;                                              /* Input fifo spinlock */
 
        struct urb *urbctrl;                                            /* Control URB */
        struct usb_ctrlrequest *cr;                                     /* Control request struct */
@@ -75,21 +80,22 @@ struct usbhid_device {
        unsigned char ctrlhead, ctrltail;                               /* Control fifo head & tail */
        char *ctrlbuf;                                                  /* Control buffer */
        dma_addr_t ctrlbuf_dma;                                         /* Control buffer dma */
-       spinlock_t ctrllock;                                            /* Control fifo spinlock */
 
        struct urb *urbout;                                             /* Output URB */
        struct hid_output_fifo out[HID_CONTROL_FIFO_SIZE];              /* Output pipe fifo */
        unsigned char outhead, outtail;                                 /* Output pipe fifo head & tail */
        char *outbuf;                                                   /* Output buffer */
        dma_addr_t outbuf_dma;                                          /* Output buffer dma */
-       spinlock_t outlock;                                             /* Output fifo spinlock */
 
+       spinlock_t lock;                                                /* fifo spinlock */
        unsigned long iofl;                                             /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */
        struct timer_list io_retry;                                     /* Retry timer */
        unsigned long stop_retry;                                       /* Time to give up, in jiffies */
        unsigned int retry_delay;                                       /* Delay length in ms */
        struct work_struct reset_work;                                  /* Task context for resets */
+       struct work_struct restart_work;                                /* waking up for output to be done in a task */
        wait_queue_head_t wait;                                         /* For sleeping */
+       int ledcount;                                                   /* counting the number of active leds */
 };
 
 #define        hid_to_usb_dev(hid_dev) \
index fa8ee9cef7bec3e228f27304bb31e14d161c0673..a72876e435892cb3ce896e234e84084728434064 100644 (file)
@@ -270,6 +270,7 @@ struct hid_item {
 
 #define HID_QUIRK_INVERT                       0x00000001
 #define HID_QUIRK_NOTOUCH                      0x00000002
+#define HID_QUIRK_IGNORE                       0x00000004
 #define HID_QUIRK_NOGET                                0x00000008
 #define HID_QUIRK_BADPAD                       0x00000020
 #define HID_QUIRK_MULTI_INPUT                  0x00000040
@@ -603,12 +604,17 @@ struct hid_ll_driver {
        int (*open)(struct hid_device *hdev);
        void (*close)(struct hid_device *hdev);
 
+       int (*power)(struct hid_device *hdev, int level);
+
        int (*hidinput_input_event) (struct input_dev *idev, unsigned int type,
                        unsigned int code, int value);
 
        int (*parse)(struct hid_device *hdev);
 };
 
+#define        PM_HINT_FULLON  1<<5
+#define PM_HINT_NORMAL 1<<1
+
 /* Applications from HID Usage Tables 4/8/99 Version 1.1 */
 /* We ignore a few input applications that are not widely used */
 #define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001) || (a == 0x000d0002))
@@ -641,6 +647,7 @@ int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int
 void hid_output_report(struct hid_report *report, __u8 *data);
 struct hid_device *hid_allocate_device(void);
 int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size);
+int hid_check_keys_pressed(struct hid_device *hid);
 int hid_connect(struct hid_device *hid, unsigned int connect_mask);
 
 /**
@@ -791,21 +798,5 @@ dbg_hid(const char *fmt, ...)
                __FILE__ , ## arg)
 #endif /* HID_FF */
 
-#ifdef __KERNEL__
-#ifdef CONFIG_HID_COMPAT
-#define HID_COMPAT_LOAD_DRIVER(name)   \
-/* prototype to avoid sparse warning */        \
-extern void hid_compat_##name(void);   \
-void hid_compat_##name(void) { }       \
-EXPORT_SYMBOL(hid_compat_##name)
-#else
-#define HID_COMPAT_LOAD_DRIVER(name)
-#endif /* HID_COMPAT */
-#define HID_COMPAT_CALL_DRIVER(name)   do {    \
-       extern void hid_compat_##name(void);    \
-       hid_compat_##name();                    \
-} while (0)
-#endif /* __KERNEL__ */
-
 #endif