Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 10 Aug 2020 23:33:54 +0000 (16:33 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 10 Aug 2020 23:33:54 +0000 (16:33 -0700)
Pull HID updates from Jiri Kosina:

 - fix for some modern devices that return multi-byte battery report,
   from Grant Likely

 - fix for devices with Resolution Multiplier, from Peter Hutterer

 - device probing speed increase, from Dmitry Torokhov

 - ThinkPad 10 Ultrabook Keyboard support, from Hans de Goede

 - other small assorted fixes and device ID additions

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid:
  HID: quirks: add NOGET quirk for Logitech GROUP
  HID: Replace HTTP links with HTTPS ones
  HID: udraw-ps3: Replace HTTP links with HTTPS ones
  HID: mcp2221: Replace HTTP links with HTTPS ones
  HID: input: Fix devices that return multiple bytes in battery report
  HID: lenovo: Fix spurious F23 key press report during resume from suspend
  HID: lenovo: Add ThinkPad 10 Ultrabook Keyboard fn_lock support
  HID: lenovo: Add ThinkPad 10 Ultrabook Keyboard support
  HID: lenovo: Rename fn_lock sysfs attr handlers to make them generic
  HID: lenovo: Factor out generic parts of the LED code
  HID: lenovo: Merge tpkbd and cptkbd data structures
  HID: intel-ish-hid: Replace PCI_DEV_FLAGS_NO_D3 with pci_save_state
  HID: Wiimote: Treat the d-pad as an analogue stick
  HID: input: do not run GET_REPORT unless there's a Resolution Multiplier
  HID: usbhid: remove redundant assignment to variable retval
  HID: usbhid: do not sleep when opening device

14 files changed:
drivers/hid/Kconfig
drivers/hid/hid-cp2112.c
drivers/hid/hid-ids.h
drivers/hid/hid-input.c
drivers/hid/hid-lenovo.c
drivers/hid/hid-mcp2221.c
drivers/hid/hid-quirks.c
drivers/hid/hid-udraw-ps3.c
drivers/hid/hid-wiimote-core.c
drivers/hid/hid-wiimote-modules.c
drivers/hid/hid-wiimote.h
drivers/hid/intel-ish-hid/ipc/pci-ish.c
drivers/hid/usbhid/hid-core.c
drivers/hid/usbhid/usbhid.h

index 45e87dc..05315b4 100644 (file)
@@ -20,7 +20,7 @@ config HID
          removed from the HID bus by the transport-layer drivers, such as
          usbhid (USB_HID) and hidp (BT_HIDP).
 
-         For docs and specs, see http://www.usb.org/developers/hidpage/
+         For docs and specs, see https://www.usb.org/developers/hidpage/
 
          If unsure, say Y.
 
index db1b55d..f64517b 100644 (file)
@@ -11,7 +11,7 @@
  * host communicates with the CP2112 via raw HID reports.
  *
  * Data Sheet:
- *   http://www.silabs.com/Support%20Documents/TechnicalDocs/CP2112.pdf
+ *   https://www.silabs.com/Support%20Documents/TechnicalDocs/CP2112.pdf
  * Programming Interface Specification:
  *   https://www.silabs.com/documents/public/application-notes/an495-cp2112-interface-specification.pdf
  */
index 6f370e0..6221888 100644 (file)
 #define USB_DEVICE_ID_LENOVO_CUSBKBD   0x6047
 #define USB_DEVICE_ID_LENOVO_CBTKBD    0x6048
 #define USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL       0x6049
+#define USB_DEVICE_ID_LENOVO_TP10UBKBD 0x6062
 #define USB_DEVICE_ID_LENOVO_TPPRODOCK 0x6067
 #define USB_DEVICE_ID_LENOVO_X1_COVER  0x6085
 #define USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_608D     0x608d
 #define USB_DEVICE_ID_LOGITECH_G27_WHEEL       0xc29b
 #define USB_DEVICE_ID_LOGITECH_WII_WHEEL       0xc29c
 #define USB_DEVICE_ID_LOGITECH_ELITE_KBD       0xc30a
+#define USB_DEVICE_ID_LOGITECH_GROUP_AUDIO     0x0882
 #define USB_DEVICE_ID_S510_RECEIVER    0xc50c
 #define USB_DEVICE_ID_S510_RECEIVER_2  0xc517
 #define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500  0xc512
index dea9cc6..b8eabf2 100644 (file)
@@ -350,13 +350,13 @@ static int hidinput_query_battery_capacity(struct hid_device *dev)
        u8 *buf;
        int ret;
 
-       buf = kmalloc(2, GFP_KERNEL);
+       buf = kmalloc(4, GFP_KERNEL);
        if (!buf)
                return -ENOMEM;
 
-       ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 2,
+       ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 4,
                                 dev->battery_report_type, HID_REQ_GET_REPORT);
-       if (ret != 2) {
+       if (ret < 2) {
                kfree(buf);
                return -ENODATA;
        }
@@ -1560,21 +1560,12 @@ static bool __hidinput_change_resolution_multipliers(struct hid_device *hid,
 {
        struct hid_usage *usage;
        bool update_needed = false;
+       bool get_report_completed = false;
        int i, j;
 
        if (report->maxfield == 0)
                return false;
 
-       /*
-        * If we have more than one feature within this report we
-        * need to fill in the bits from the others before we can
-        * overwrite the ones for the Resolution Multiplier.
-        */
-       if (report->maxfield > 1) {
-               hid_hw_request(hid, report, HID_REQ_GET_REPORT);
-               hid_hw_wait(hid);
-       }
-
        for (i = 0; i < report->maxfield; i++) {
                __s32 value = use_logical_max ?
                              report->field[i]->logical_maximum :
@@ -1593,6 +1584,25 @@ static bool __hidinput_change_resolution_multipliers(struct hid_device *hid,
                        if (usage->hid != HID_GD_RESOLUTION_MULTIPLIER)
                                continue;
 
+                       /*
+                        * If we have more than one feature within this
+                        * report we need to fill in the bits from the
+                        * others before we can overwrite the ones for the
+                        * Resolution Multiplier.
+                        *
+                        * But if we're not allowed to read from the device,
+                        * we just bail. Such a device should not exist
+                        * anyway.
+                        */
+                       if (!get_report_completed && report->maxfield > 1) {
+                               if (hid->quirks & HID_QUIRK_NO_INIT_REPORTS)
+                                       return update_needed;
+
+                               hid_hw_request(hid, report, HID_REQ_GET_REPORT);
+                               hid_hw_wait(hid);
+                               get_report_completed = true;
+                       }
+
                        report->field[i]->value[j] = value;
                        update_needed = true;
                }
index 96fa2a2..c6c8e20 100644 (file)
 #include <linux/hid.h>
 #include <linux/input.h>
 #include <linux/leds.h>
+#include <linux/workqueue.h>
 
 #include "hid-ids.h"
 
-struct lenovo_drvdata_tpkbd {
+struct lenovo_drvdata {
+       u8 led_report[3]; /* Must be first for proper alignment */
        int led_state;
+       struct mutex led_report_mutex;
        struct led_classdev led_mute;
        struct led_classdev led_micmute;
+       struct work_struct fn_lock_sync_work;
+       struct hid_device *hdev;
        int press_to_select;
        int dragging;
        int release_to_select;
        int select_right;
        int sensitivity;
        int press_speed;
-};
-
-struct lenovo_drvdata_cptkbd {
        u8 middlebutton_state; /* 0:Up, 1:Down (undecided), 2:Scrolling */
        bool fn_lock;
-       int sensitivity;
 };
 
 #define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
 
+#define TP10UBKBD_LED_OUTPUT_REPORT    9
+
+#define TP10UBKBD_FN_LOCK_LED          0x54
+#define TP10UBKBD_MUTE_LED             0x64
+#define TP10UBKBD_MICMUTE_LED          0x74
+
+#define TP10UBKBD_LED_OFF              1
+#define TP10UBKBD_LED_ON               2
+
+static void lenovo_led_set_tp10ubkbd(struct hid_device *hdev, u8 led_code,
+                                    enum led_brightness value)
+{
+       struct lenovo_drvdata *data = hid_get_drvdata(hdev);
+       int ret;
+
+       mutex_lock(&data->led_report_mutex);
+
+       data->led_report[0] = TP10UBKBD_LED_OUTPUT_REPORT;
+       data->led_report[1] = led_code;
+       data->led_report[2] = value ? TP10UBKBD_LED_ON : TP10UBKBD_LED_OFF;
+       ret = hid_hw_raw_request(hdev, data->led_report[0], data->led_report, 3,
+                                HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
+       if (ret)
+               hid_err(hdev, "Set LED output report error: %d\n", ret);
+
+       mutex_unlock(&data->led_report_mutex);
+}
+
+static void lenovo_tp10ubkbd_sync_fn_lock(struct work_struct *work)
+{
+       struct lenovo_drvdata *data =
+               container_of(work, struct lenovo_drvdata, fn_lock_sync_work);
+
+       lenovo_led_set_tp10ubkbd(data->hdev, TP10UBKBD_FN_LOCK_LED,
+                                data->fn_lock);
+}
+
 static const __u8 lenovo_pro_dock_need_fixup_collection[] = {
        0x05, 0x88,             /* Usage Page (Vendor Usage Page 0x88)  */
        0x09, 0x01,             /* Usage (Vendor Usage 0x01)            */
@@ -179,6 +217,44 @@ static int lenovo_input_mapping_scrollpoint(struct hid_device *hdev,
        return 0;
 }
 
+static int lenovo_input_mapping_tp10_ultrabook_kbd(struct hid_device *hdev,
+               struct hid_input *hi, struct hid_field *field,
+               struct hid_usage *usage, unsigned long **bit, int *max)
+{
+       /*
+        * The ThinkPad 10 Ultrabook Keyboard uses 0x000c0001 usage for
+        * a bunch of keys which have no standard consumer page code.
+        */
+       if (usage->hid == 0x000c0001) {
+               switch (usage->usage_index) {
+               case 8: /* Fn-Esc: Fn-lock toggle */
+                       map_key_clear(KEY_FN_ESC);
+                       return 1;
+               case 9: /* Fn-F4: Mic mute */
+                       map_key_clear(KEY_MICMUTE);
+                       return 1;
+               case 10: /* Fn-F7: Control panel */
+                       map_key_clear(KEY_CONFIG);
+                       return 1;
+               case 11: /* Fn-F8: Search (magnifier glass) */
+                       map_key_clear(KEY_SEARCH);
+                       return 1;
+               case 12: /* Fn-F10: Open My computer (6 boxes) */
+                       map_key_clear(KEY_FILE);
+                       return 1;
+               }
+       }
+
+       /*
+        * The Ultrabook Keyboard sends a spurious F23 key-press when resuming
+        * from suspend and it does not actually have a F23 key, ignore it.
+        */
+       if (usage->hid == 0x00070072)
+               return -1;
+
+       return 0;
+}
+
 static int lenovo_input_mapping(struct hid_device *hdev,
                struct hid_input *hi, struct hid_field *field,
                struct hid_usage *usage, unsigned long **bit, int *max)
@@ -199,6 +275,9 @@ static int lenovo_input_mapping(struct hid_device *hdev,
        case USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL:
                return lenovo_input_mapping_scrollpoint(hdev, hi, field,
                                                        usage, bit, max);
+       case USB_DEVICE_ID_LENOVO_TP10UBKBD:
+               return lenovo_input_mapping_tp10_ultrabook_kbd(hdev, hi, field,
+                                                              usage, bit, max);
        default:
                return 0;
        }
@@ -242,7 +321,7 @@ static int lenovo_send_cmd_cptkbd(struct hid_device *hdev,
 static void lenovo_features_set_cptkbd(struct hid_device *hdev)
 {
        int ret;
-       struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
+       struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
 
        ret = lenovo_send_cmd_cptkbd(hdev, 0x05, cptkbd_data->fn_lock);
        if (ret)
@@ -253,23 +332,23 @@ static void lenovo_features_set_cptkbd(struct hid_device *hdev)
                hid_err(hdev, "Sensitivity setting failed: %d\n", ret);
 }
 
-static ssize_t attr_fn_lock_show_cptkbd(struct device *dev,
+static ssize_t attr_fn_lock_show(struct device *dev,
                struct device_attribute *attr,
                char *buf)
 {
        struct hid_device *hdev = to_hid_device(dev);
-       struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
+       struct lenovo_drvdata *data = hid_get_drvdata(hdev);
 
-       return snprintf(buf, PAGE_SIZE, "%u\n", cptkbd_data->fn_lock);
+       return snprintf(buf, PAGE_SIZE, "%u\n", data->fn_lock);
 }
 
-static ssize_t attr_fn_lock_store_cptkbd(struct device *dev,
+static ssize_t attr_fn_lock_store(struct device *dev,
                struct device_attribute *attr,
                const char *buf,
                size_t count)
 {
        struct hid_device *hdev = to_hid_device(dev);
-       struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
+       struct lenovo_drvdata *data = hid_get_drvdata(hdev);
        int value;
 
        if (kstrtoint(buf, 10, &value))
@@ -277,8 +356,17 @@ static ssize_t attr_fn_lock_store_cptkbd(struct device *dev,
        if (value < 0 || value > 1)
                return -EINVAL;
 
-       cptkbd_data->fn_lock = !!value;
-       lenovo_features_set_cptkbd(hdev);
+       data->fn_lock = !!value;
+
+       switch (hdev->product) {
+       case USB_DEVICE_ID_LENOVO_CUSBKBD:
+       case USB_DEVICE_ID_LENOVO_CBTKBD:
+               lenovo_features_set_cptkbd(hdev);
+               break;
+       case USB_DEVICE_ID_LENOVO_TP10UBKBD:
+               lenovo_led_set_tp10ubkbd(hdev, TP10UBKBD_FN_LOCK_LED, value);
+               break;
+       }
 
        return count;
 }
@@ -288,7 +376,7 @@ static ssize_t attr_sensitivity_show_cptkbd(struct device *dev,
                char *buf)
 {
        struct hid_device *hdev = to_hid_device(dev);
-       struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
+       struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
 
        return snprintf(buf, PAGE_SIZE, "%u\n",
                cptkbd_data->sensitivity);
@@ -300,7 +388,7 @@ static ssize_t attr_sensitivity_store_cptkbd(struct device *dev,
                size_t count)
 {
        struct hid_device *hdev = to_hid_device(dev);
-       struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
+       struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
        int value;
 
        if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
@@ -313,10 +401,10 @@ static ssize_t attr_sensitivity_store_cptkbd(struct device *dev,
 }
 
 
-static struct device_attribute dev_attr_fn_lock_cptkbd =
+static struct device_attribute dev_attr_fn_lock =
        __ATTR(fn_lock, S_IWUSR | S_IRUGO,
-                       attr_fn_lock_show_cptkbd,
-                       attr_fn_lock_store_cptkbd);
+                       attr_fn_lock_show,
+                       attr_fn_lock_store);
 
 static struct device_attribute dev_attr_sensitivity_cptkbd =
        __ATTR(sensitivity, S_IWUSR | S_IRUGO,
@@ -325,7 +413,7 @@ static struct device_attribute dev_attr_sensitivity_cptkbd =
 
 
 static struct attribute *lenovo_attributes_cptkbd[] = {
-       &dev_attr_fn_lock_cptkbd.attr,
+       &dev_attr_fn_lock.attr,
        &dev_attr_sensitivity_cptkbd.attr,
        NULL
 };
@@ -354,10 +442,28 @@ static int lenovo_raw_event(struct hid_device *hdev,
        return 0;
 }
 
+static int lenovo_event_tp10ubkbd(struct hid_device *hdev,
+               struct hid_field *field, struct hid_usage *usage, __s32 value)
+{
+       struct lenovo_drvdata *data = hid_get_drvdata(hdev);
+
+       if (usage->type == EV_KEY && usage->code == KEY_FN_ESC && value == 1) {
+               /*
+                * The user has toggled the Fn-lock state. Toggle our own
+                * cached value of it and sync our value to the keyboard to
+                * ensure things are in sync (the sycning should be a no-op).
+                */
+               data->fn_lock = !data->fn_lock;
+               schedule_work(&data->fn_lock_sync_work);
+       }
+
+       return 0;
+}
+
 static int lenovo_event_cptkbd(struct hid_device *hdev,
                struct hid_field *field, struct hid_usage *usage, __s32 value)
 {
-       struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
+       struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
 
        /* "wheel" scroll events */
        if (usage->type == EV_REL && (usage->code == REL_WHEEL ||
@@ -396,6 +502,8 @@ static int lenovo_event(struct hid_device *hdev, struct hid_field *field,
        case USB_DEVICE_ID_LENOVO_CUSBKBD:
        case USB_DEVICE_ID_LENOVO_CBTKBD:
                return lenovo_event_cptkbd(hdev, field, usage, value);
+       case USB_DEVICE_ID_LENOVO_TP10UBKBD:
+               return lenovo_event_tp10ubkbd(hdev, field, usage, value);
        default:
                return 0;
        }
@@ -404,7 +512,7 @@ static int lenovo_event(struct hid_device *hdev, struct hid_field *field,
 static int lenovo_features_set_tpkbd(struct hid_device *hdev)
 {
        struct hid_report *report;
-       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+       struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
 
        report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4];
 
@@ -425,7 +533,7 @@ static ssize_t attr_press_to_select_show_tpkbd(struct device *dev,
                char *buf)
 {
        struct hid_device *hdev = to_hid_device(dev);
-       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+       struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
 
        return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select);
 }
@@ -436,7 +544,7 @@ static ssize_t attr_press_to_select_store_tpkbd(struct device *dev,
                size_t count)
 {
        struct hid_device *hdev = to_hid_device(dev);
-       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+       struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
        int value;
 
        if (kstrtoint(buf, 10, &value))
@@ -455,7 +563,7 @@ static ssize_t attr_dragging_show_tpkbd(struct device *dev,
                char *buf)
 {
        struct hid_device *hdev = to_hid_device(dev);
-       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+       struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
 
        return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging);
 }
@@ -466,7 +574,7 @@ static ssize_t attr_dragging_store_tpkbd(struct device *dev,
                size_t count)
 {
        struct hid_device *hdev = to_hid_device(dev);
-       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+       struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
        int value;
 
        if (kstrtoint(buf, 10, &value))
@@ -485,7 +593,7 @@ static ssize_t attr_release_to_select_show_tpkbd(struct device *dev,
                char *buf)
 {
        struct hid_device *hdev = to_hid_device(dev);
-       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+       struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
 
        return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select);
 }
@@ -496,7 +604,7 @@ static ssize_t attr_release_to_select_store_tpkbd(struct device *dev,
                size_t count)
 {
        struct hid_device *hdev = to_hid_device(dev);
-       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+       struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
        int value;
 
        if (kstrtoint(buf, 10, &value))
@@ -515,7 +623,7 @@ static ssize_t attr_select_right_show_tpkbd(struct device *dev,
                char *buf)
 {
        struct hid_device *hdev = to_hid_device(dev);
-       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+       struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
 
        return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right);
 }
@@ -526,7 +634,7 @@ static ssize_t attr_select_right_store_tpkbd(struct device *dev,
                size_t count)
 {
        struct hid_device *hdev = to_hid_device(dev);
-       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+       struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
        int value;
 
        if (kstrtoint(buf, 10, &value))
@@ -545,7 +653,7 @@ static ssize_t attr_sensitivity_show_tpkbd(struct device *dev,
                char *buf)
 {
        struct hid_device *hdev = to_hid_device(dev);
-       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+       struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
 
        return snprintf(buf, PAGE_SIZE, "%u\n",
                data_pointer->sensitivity);
@@ -557,7 +665,7 @@ static ssize_t attr_sensitivity_store_tpkbd(struct device *dev,
                size_t count)
 {
        struct hid_device *hdev = to_hid_device(dev);
-       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+       struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
        int value;
 
        if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
@@ -574,7 +682,7 @@ static ssize_t attr_press_speed_show_tpkbd(struct device *dev,
                char *buf)
 {
        struct hid_device *hdev = to_hid_device(dev);
-       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+       struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
 
        return snprintf(buf, PAGE_SIZE, "%u\n",
                data_pointer->press_speed);
@@ -586,7 +694,7 @@ static ssize_t attr_press_speed_store_tpkbd(struct device *dev,
                size_t count)
 {
        struct hid_device *hdev = to_hid_device(dev);
-       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+       struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
        int value;
 
        if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
@@ -642,12 +750,23 @@ static const struct attribute_group lenovo_attr_group_tpkbd = {
        .attrs = lenovo_attributes_tpkbd,
 };
 
-static enum led_brightness lenovo_led_brightness_get_tpkbd(
+static void lenovo_led_set_tpkbd(struct hid_device *hdev)
+{
+       struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
+       struct hid_report *report;
+
+       report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3];
+       report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1;
+       report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1;
+       hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
+}
+
+static enum led_brightness lenovo_led_brightness_get(
                        struct led_classdev *led_cdev)
 {
        struct device *dev = led_cdev->dev->parent;
        struct hid_device *hdev = to_hid_device(dev);
-       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+       struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
        int led_nr = 0;
 
        if (led_cdev == &data_pointer->led_micmute)
@@ -658,13 +777,13 @@ static enum led_brightness lenovo_led_brightness_get_tpkbd(
                                : LED_OFF;
 }
 
-static void lenovo_led_brightness_set_tpkbd(struct led_classdev *led_cdev,
+static void lenovo_led_brightness_set(struct led_classdev *led_cdev,
                        enum led_brightness value)
 {
        struct device *dev = led_cdev->dev->parent;
        struct hid_device *hdev = to_hid_device(dev);
-       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
-       struct hid_report *report;
+       struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
+       u8 tp10ubkbd_led[] = { TP10UBKBD_MUTE_LED, TP10UBKBD_MICMUTE_LED };
        int led_nr = 0;
 
        if (led_cdev == &data_pointer->led_micmute)
@@ -675,21 +794,58 @@ static void lenovo_led_brightness_set_tpkbd(struct led_classdev *led_cdev,
        else
                data_pointer->led_state |= 1 << led_nr;
 
-       report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3];
-       report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1;
-       report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1;
-       hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
+       switch (hdev->product) {
+       case USB_DEVICE_ID_LENOVO_TPKBD:
+               lenovo_led_set_tpkbd(hdev);
+               break;
+       case USB_DEVICE_ID_LENOVO_TP10UBKBD:
+               lenovo_led_set_tp10ubkbd(hdev, tp10ubkbd_led[led_nr], value);
+               break;
+       }
 }
 
-static int lenovo_probe_tpkbd(struct hid_device *hdev)
+static int lenovo_register_leds(struct hid_device *hdev)
 {
-       struct device *dev = &hdev->dev;
-       struct lenovo_drvdata_tpkbd *data_pointer;
-       size_t name_sz = strlen(dev_name(dev)) + 16;
-       char *name_mute, *name_micmute;
-       int i;
+       struct lenovo_drvdata *data = hid_get_drvdata(hdev);
+       size_t name_sz = strlen(dev_name(&hdev->dev)) + 16;
+       char *name_mute, *name_micm;
        int ret;
 
+       name_mute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
+       name_micm = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
+       if (name_mute == NULL || name_micm == NULL) {
+               hid_err(hdev, "Could not allocate memory for led data\n");
+               return -ENOMEM;
+       }
+       snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(&hdev->dev));
+       snprintf(name_micm, name_sz, "%s:amber:micmute", dev_name(&hdev->dev));
+
+       data->led_mute.name = name_mute;
+       data->led_mute.brightness_get = lenovo_led_brightness_get;
+       data->led_mute.brightness_set = lenovo_led_brightness_set;
+       data->led_mute.dev = &hdev->dev;
+       ret = led_classdev_register(&hdev->dev, &data->led_mute);
+       if (ret < 0)
+               return ret;
+
+       data->led_micmute.name = name_micm;
+       data->led_micmute.brightness_get = lenovo_led_brightness_get;
+       data->led_micmute.brightness_set = lenovo_led_brightness_set;
+       data->led_micmute.dev = &hdev->dev;
+       ret = led_classdev_register(&hdev->dev, &data->led_micmute);
+       if (ret < 0) {
+               led_classdev_unregister(&data->led_mute);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int lenovo_probe_tpkbd(struct hid_device *hdev)
+{
+       struct lenovo_drvdata *data_pointer;
+       int i, ret;
+
        /*
         * Only register extra settings against subdevice where input_mapping
         * set drvdata to 1, i.e. the trackpoint.
@@ -712,7 +868,7 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev)
                hid_warn(hdev, "Could not create sysfs group: %d\n", ret);
 
        data_pointer = devm_kzalloc(&hdev->dev,
-                                   sizeof(struct lenovo_drvdata_tpkbd),
+                                   sizeof(struct lenovo_drvdata),
                                    GFP_KERNEL);
        if (data_pointer == NULL) {
                hid_err(hdev, "Could not allocate memory for driver data\n");
@@ -724,37 +880,11 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev)
        data_pointer->sensitivity = 0xa0;
        data_pointer->press_speed = 0x38;
 
-       name_mute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
-       name_micmute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
-       if (name_mute == NULL || name_micmute == NULL) {
-               hid_err(hdev, "Could not allocate memory for led data\n");
-               ret = -ENOMEM;
-               goto err;
-       }
-       snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(dev));
-       snprintf(name_micmute, name_sz, "%s:amber:micmute", dev_name(dev));
-
        hid_set_drvdata(hdev, data_pointer);
 
-       data_pointer->led_mute.name = name_mute;
-       data_pointer->led_mute.brightness_get = lenovo_led_brightness_get_tpkbd;
-       data_pointer->led_mute.brightness_set = lenovo_led_brightness_set_tpkbd;
-       data_pointer->led_mute.dev = dev;
-       ret = led_classdev_register(dev, &data_pointer->led_mute);
-       if (ret < 0)
-               goto err;
-
-       data_pointer->led_micmute.name = name_micmute;
-       data_pointer->led_micmute.brightness_get =
-               lenovo_led_brightness_get_tpkbd;
-       data_pointer->led_micmute.brightness_set =
-               lenovo_led_brightness_set_tpkbd;
-       data_pointer->led_micmute.dev = dev;
-       ret = led_classdev_register(dev, &data_pointer->led_micmute);
-       if (ret < 0) {
-               led_classdev_unregister(&data_pointer->led_mute);
+       ret = lenovo_register_leds(hdev);
+       if (ret)
                goto err;
-       }
 
        lenovo_features_set_tpkbd(hdev);
 
@@ -767,7 +897,7 @@ err:
 static int lenovo_probe_cptkbd(struct hid_device *hdev)
 {
        int ret;
-       struct lenovo_drvdata_cptkbd *cptkbd_data;
+       struct lenovo_drvdata *cptkbd_data;
 
        /* All the custom action happens on the USBMOUSE device for USB */
        if (hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD
@@ -811,6 +941,57 @@ static int lenovo_probe_cptkbd(struct hid_device *hdev)
        return 0;
 }
 
+static struct attribute *lenovo_attributes_tp10ubkbd[] = {
+       &dev_attr_fn_lock.attr,
+       NULL
+};
+
+static const struct attribute_group lenovo_attr_group_tp10ubkbd = {
+       .attrs = lenovo_attributes_tp10ubkbd,
+};
+
+static int lenovo_probe_tp10ubkbd(struct hid_device *hdev)
+{
+       struct lenovo_drvdata *data;
+       int ret;
+
+       /* All the custom action happens on the USBMOUSE device for USB */
+       if (hdev->type != HID_TYPE_USBMOUSE)
+               return 0;
+
+       data = devm_kzalloc(&hdev->dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       mutex_init(&data->led_report_mutex);
+       INIT_WORK(&data->fn_lock_sync_work, lenovo_tp10ubkbd_sync_fn_lock);
+       data->hdev = hdev;
+
+       hid_set_drvdata(hdev, data);
+
+       /*
+        * The Thinkpad 10 ultrabook USB kbd dock's Fn-lock defaults to on.
+        * We cannot read the state, only set it, so we force it to on here
+        * (which should be a no-op) to make sure that our state matches the
+        * keyboard's FN-lock state. This is the same as what Windows does.
+        */
+       data->fn_lock = true;
+       lenovo_led_set_tp10ubkbd(hdev, TP10UBKBD_FN_LOCK_LED, data->fn_lock);
+
+       ret = sysfs_create_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd);
+       if (ret)
+               return ret;
+
+       ret = lenovo_register_leds(hdev);
+       if (ret)
+               goto err;
+
+       return 0;
+err:
+       sysfs_remove_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd);
+       return ret;
+}
+
 static int lenovo_probe(struct hid_device *hdev,
                const struct hid_device_id *id)
 {
@@ -836,6 +1017,9 @@ static int lenovo_probe(struct hid_device *hdev,
        case USB_DEVICE_ID_LENOVO_CBTKBD:
                ret = lenovo_probe_cptkbd(hdev);
                break;
+       case USB_DEVICE_ID_LENOVO_TP10UBKBD:
+               ret = lenovo_probe_tp10ubkbd(hdev);
+               break;
        default:
                ret = 0;
                break;
@@ -852,7 +1036,7 @@ err:
 
 static void lenovo_remove_tpkbd(struct hid_device *hdev)
 {
-       struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
+       struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
 
        /*
         * Only the trackpoint half of the keyboard has drvdata and stuff that
@@ -874,6 +1058,20 @@ static void lenovo_remove_cptkbd(struct hid_device *hdev)
                        &lenovo_attr_group_cptkbd);
 }
 
+static void lenovo_remove_tp10ubkbd(struct hid_device *hdev)
+{
+       struct lenovo_drvdata *data = hid_get_drvdata(hdev);
+
+       if (data == NULL)
+               return;
+
+       led_classdev_unregister(&data->led_micmute);
+       led_classdev_unregister(&data->led_mute);
+
+       sysfs_remove_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd);
+       cancel_work_sync(&data->fn_lock_sync_work);
+}
+
 static void lenovo_remove(struct hid_device *hdev)
 {
        switch (hdev->product) {
@@ -884,6 +1082,9 @@ static void lenovo_remove(struct hid_device *hdev)
        case USB_DEVICE_ID_LENOVO_CBTKBD:
                lenovo_remove_cptkbd(hdev);
                break;
+       case USB_DEVICE_ID_LENOVO_TP10UBKBD:
+               lenovo_remove_tp10ubkbd(hdev);
+               break;
        }
 
        hid_hw_stop(hdev);
@@ -920,6 +1121,7 @@ static const struct hid_device_id lenovo_devices[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL) },
        { HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL_PRO) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TP10UBKBD) },
        { }
 };
 
index e1b93ce..0d27ccb 100644 (file)
@@ -4,7 +4,7 @@
  *
  * Copyright (c) 2020, Rishi Gupta <gupt21@gmail.com>
  *
- * Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/20005565B.pdf
+ * Datasheet: https://ww1.microchip.com/downloads/en/DeviceDoc/20005565B.pdf
  */
 
 #include <linux/module.h>
index 934fc0a..c242150 100644 (file)
@@ -179,6 +179,7 @@ static const struct hid_device_id hid_quirks[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD2, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS), HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
        { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD), HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
        { HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_XIN_MO_DUAL_ARCADE), HID_QUIRK_MULTI_INPUT },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_GROUP_AUDIO), HID_QUIRK_NOGET },
 
        { 0 }
 };
index b0fbd11..b2e17ef 100644 (file)
@@ -16,7 +16,7 @@ MODULE_LICENSE("GPL");
 
 /*
  * Protocol information from:
- * http://brandonw.net/udraw/
+ * https://brandonw.net/udraw/
  * and the source code of:
  * https://vvvv.org/contribution/udraw-hid
  */
index 92874db..679e142 100644 (file)
@@ -1870,6 +1870,11 @@ static const struct hid_device_id wiimote_hid_devices[] = {
                                USB_DEVICE_ID_NINTENDO_WIIMOTE2) },
        { }
 };
+
+bool wiimote_dpad_as_analog = false;
+module_param_named(dpad_as_analog, wiimote_dpad_as_analog, bool, 0644);
+MODULE_PARM_DESC(dpad_as_analog, "Use D-Pad as main analog input");
+
 MODULE_DEVICE_TABLE(hid, wiimote_hid_devices);
 
 static struct hid_driver wiimote_hid_driver = {
index 2c39253..213c58b 100644 (file)
@@ -1088,12 +1088,28 @@ static void wiimod_classic_in_ext(struct wiimote_data *wdata, const __u8 *ext)
         * is the same as before.
         */
 
+       static const s8 digital_to_analog[3] = {0x20, 0, -0x20};
+
        if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
-               lx = ext[0] & 0x3e;
-               ly = ext[1] & 0x3e;
+               if (wiimote_dpad_as_analog) {
+                       lx = digital_to_analog[1 - !(ext[4] & 0x80)
+                               + !(ext[1] & 0x01)];
+                       ly = digital_to_analog[1 - !(ext[4] & 0x40)
+                               + !(ext[0] & 0x01)];
+               } else {
+                       lx = (ext[0] & 0x3e) - 0x20;
+                       ly = (ext[1] & 0x3e) - 0x20;
+               }
        } else {
-               lx = ext[0] & 0x3f;
-               ly = ext[1] & 0x3f;
+               if (wiimote_dpad_as_analog) {
+                       lx = digital_to_analog[1 - !(ext[4] & 0x80)
+                               + !(ext[5] & 0x02)];
+                       ly = digital_to_analog[1 - !(ext[4] & 0x40)
+                               + !(ext[5] & 0x01)];
+               } else {
+                       lx = (ext[0] & 0x3f) - 0x20;
+                       ly = (ext[1] & 0x3f) - 0x20;
+               }
        }
 
        rx = (ext[0] >> 3) & 0x18;
@@ -1110,19 +1126,13 @@ static void wiimod_classic_in_ext(struct wiimote_data *wdata, const __u8 *ext)
        rt <<= 1;
        lt <<= 1;
 
-       input_report_abs(wdata->extension.input, ABS_HAT1X, lx - 0x20);
-       input_report_abs(wdata->extension.input, ABS_HAT1Y, ly - 0x20);
+       input_report_abs(wdata->extension.input, ABS_HAT1X, lx);
+       input_report_abs(wdata->extension.input, ABS_HAT1Y, ly);
        input_report_abs(wdata->extension.input, ABS_HAT2X, rx - 0x20);
        input_report_abs(wdata->extension.input, ABS_HAT2Y, ry - 0x20);
        input_report_abs(wdata->extension.input, ABS_HAT3X, rt);
        input_report_abs(wdata->extension.input, ABS_HAT3Y, lt);
 
-       input_report_key(wdata->extension.input,
-                        wiimod_classic_map[WIIMOD_CLASSIC_KEY_RIGHT],
-                        !(ext[4] & 0x80));
-       input_report_key(wdata->extension.input,
-                        wiimod_classic_map[WIIMOD_CLASSIC_KEY_DOWN],
-                        !(ext[4] & 0x40));
        input_report_key(wdata->extension.input,
                         wiimod_classic_map[WIIMOD_CLASSIC_KEY_LT],
                         !(ext[4] & 0x20));
@@ -1157,20 +1167,29 @@ static void wiimod_classic_in_ext(struct wiimote_data *wdata, const __u8 *ext)
                         wiimod_classic_map[WIIMOD_CLASSIC_KEY_ZR],
                         !(ext[5] & 0x04));
 
-       if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
-               input_report_key(wdata->extension.input,
-                        wiimod_classic_map[WIIMOD_CLASSIC_KEY_LEFT],
-                        !(ext[1] & 0x01));
-               input_report_key(wdata->extension.input,
-                        wiimod_classic_map[WIIMOD_CLASSIC_KEY_UP],
-                        !(ext[0] & 0x01));
-       } else {
+       if (!wiimote_dpad_as_analog) {
                input_report_key(wdata->extension.input,
-                        wiimod_classic_map[WIIMOD_CLASSIC_KEY_LEFT],
-                        !(ext[5] & 0x02));
+                                wiimod_classic_map[WIIMOD_CLASSIC_KEY_RIGHT],
+                                !(ext[4] & 0x80));
                input_report_key(wdata->extension.input,
-                        wiimod_classic_map[WIIMOD_CLASSIC_KEY_UP],
-                        !(ext[5] & 0x01));
+                                wiimod_classic_map[WIIMOD_CLASSIC_KEY_DOWN],
+                                !(ext[4] & 0x40));
+
+               if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
+                       input_report_key(wdata->extension.input,
+                                wiimod_classic_map[WIIMOD_CLASSIC_KEY_LEFT],
+                                !(ext[1] & 0x01));
+                       input_report_key(wdata->extension.input,
+                                wiimod_classic_map[WIIMOD_CLASSIC_KEY_UP],
+                                !(ext[0] & 0x01));
+               } else {
+                       input_report_key(wdata->extension.input,
+                                wiimod_classic_map[WIIMOD_CLASSIC_KEY_LEFT],
+                                !(ext[5] & 0x02));
+                       input_report_key(wdata->extension.input,
+                                wiimod_classic_map[WIIMOD_CLASSIC_KEY_UP],
+                                !(ext[5] & 0x01));
+               }
        }
 
        input_sync(wdata->extension.input);
index b2a26a0..ad4ff83 100644 (file)
@@ -162,6 +162,8 @@ struct wiimote_data {
        struct work_struct init_worker;
 };
 
+extern bool wiimote_dpad_as_analog;
+
 /* wiimote modules */
 
 enum wiimod_module {
index f491d8b..c6d48a8 100644 (file)
@@ -106,6 +106,11 @@ static inline bool ish_should_enter_d0i3(struct pci_dev *pdev)
        return !pm_suspend_via_firmware() || pdev->device == CHV_DEVICE_ID;
 }
 
+static inline bool ish_should_leave_d0i3(struct pci_dev *pdev)
+{
+       return !pm_resume_via_firmware() || pdev->device == CHV_DEVICE_ID;
+}
+
 /**
  * ish_probe() - PCI driver probe callback
  * @pdev:      pci device
@@ -215,9 +220,7 @@ static void __maybe_unused ish_resume_handler(struct work_struct *work)
        struct ishtp_device *dev = pci_get_drvdata(pdev);
        int ret;
 
-       /* Check the NO_D3 flag to distinguish the resume paths */
-       if (pdev->dev_flags & PCI_DEV_FLAGS_NO_D3) {
-               pdev->dev_flags &= ~PCI_DEV_FLAGS_NO_D3;
+       if (ish_should_leave_d0i3(pdev) && !dev->suspend_flag) {
                disable_irq_wake(pdev->irq);
 
                ishtp_send_resume(dev);
@@ -281,8 +284,11 @@ static int __maybe_unused ish_suspend(struct device *device)
                         */
                        ish_disable_dma(dev);
                } else {
-                       /* Set the NO_D3 flag, the ISH would enter D0i3 */
-                       pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3;
+                       /*
+                        * Save state so PCI core will keep the device at D0,
+                        * the ISH would enter D0i3
+                        */
+                       pci_save_state(pdev);
 
                        enable_irq_wake(pdev->irq);
                }
index 17a638f..492dd64 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/wait.h>
 #include <linux/workqueue.h>
 #include <linux/string.h>
+#include <linux/timekeeping.h>
 
 #include <linux/usb.h>
 
@@ -95,6 +96,18 @@ static int hid_start_in(struct hid_device *hid)
                                set_bit(HID_NO_BANDWIDTH, &usbhid->iofl);
                } else {
                        clear_bit(HID_NO_BANDWIDTH, &usbhid->iofl);
+
+                       if (test_bit(HID_RESUME_RUNNING, &usbhid->iofl)) {
+                               /*
+                                * In case events are generated while nobody was
+                                * listening, some are released when the device
+                                * is re-opened. Wait 50 msec for the queue to
+                                * empty before allowing events to go through
+                                * hid.
+                                */
+                               usbhid->input_start_time =
+                                       ktime_add_ms(ktime_get_coarse(), 50);
+                       }
                }
        }
        spin_unlock_irqrestore(&usbhid->lock, flags);
@@ -280,20 +293,23 @@ static void hid_irq_in(struct urb *urb)
                if (!test_bit(HID_OPENED, &usbhid->iofl))
                        break;
                usbhid_mark_busy(usbhid);
-               if (!test_bit(HID_RESUME_RUNNING, &usbhid->iofl)) {
-                       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);
+               if (test_bit(HID_RESUME_RUNNING, &usbhid->iofl)) {
+                       if (ktime_before(ktime_get_coarse(),
+                                        usbhid->input_start_time))
+                               break;
+                       clear_bit(HID_RESUME_RUNNING, &usbhid->iofl);
                }
+               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);
@@ -720,17 +736,6 @@ static int usbhid_open(struct hid_device *hid)
 
        usb_autopm_put_interface(usbhid->intf);
 
-       /*
-        * In case events are generated while nobody was listening,
-        * some are released when the device is re-opened.
-        * Wait 50 msec for the queue to empty before allowing events
-        * to go through hid.
-        */
-       if (res == 0)
-               msleep(50);
-
-       clear_bit(HID_RESUME_RUNNING, &usbhid->iofl);
-
  Done:
        mutex_unlock(&usbhid->mutex);
        return res;
@@ -1667,7 +1672,7 @@ struct usb_interface *usbhid_find_interface(int minor)
 
 static int __init hid_init(void)
 {
-       int retval = -ENOMEM;
+       int retval;
 
        retval = hid_quirks_init(quirks_param, BUS_USB, MAX_USBHID_BOOT_QUIRKS);
        if (retval)
index 75fe85d..c6ad684 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <linux/types.h>
 #include <linux/slab.h>
+#include <linux/ktime.h>
 #include <linux/list.h>
 #include <linux/mutex.h>
 #include <linux/timer.h>
@@ -83,6 +84,7 @@ struct usbhid_device {
        struct mutex mutex;                                             /* start/stop/open/close */
        spinlock_t lock;                                                /* fifo spinlock */
        unsigned long iofl;                                             /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */
+       ktime_t input_start_time;                                       /* When to start handling input */
        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 */