HID: lenovo: add support for Lenovo ThinkPad Keyboard Pro unit
authorBenjamin Tissoires <benjamin.tissoires@redhat.com>
Fri, 1 May 2015 20:22:45 +0000 (16:22 -0400)
committerJiri Kosina <jkosina@suse.cz>
Thu, 7 May 2015 08:28:11 +0000 (10:28 +0200)
This dock is used with the Thinkpad Helix 2 but suffers from an error
in the report descriptor where an usage max is 65535.

Add a report fixup for it and make the keyboard working.

Tested-by: Jonathan Oppenheim <lejono@gmail.com>
Tested-by: John Reid <owlman.lists@gmail.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
drivers/hid/hid-core.c
drivers/hid/hid-ids.h
drivers/hid/hid-lenovo.c

index 722a925795a2886557d9b86ed9befdc4c53b5514..c2baf8c7c6bbdce590a28e2a5dde51178e69cebc 100644 (file)
@@ -1851,6 +1851,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CUSBKBD) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CBTKBD) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPPRODOCK) },
 #endif
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) },
index 41f167e4d75fdeec20d795b566fdeba1c642497f..1649436b969477d7992b7f4d007fae93e7ef6366 100644 (file)
 #define USB_DEVICE_ID_LENOVO_TPKBD     0x6009
 #define USB_DEVICE_ID_LENOVO_CUSBKBD   0x6047
 #define USB_DEVICE_ID_LENOVO_CBTKBD    0x6048
+#define USB_DEVICE_ID_LENOVO_TPPRODOCK 0x6067
 
 #define USB_VENDOR_ID_LG               0x1fd2
 #define USB_DEVICE_ID_LG_MULTITOUCH    0x0064
index 78608d646007fbb61b2e3e65e8c25224e5af0e6c..4e291d5ad645a55b1e769e0e14eba6866e3179fa 100644 (file)
@@ -43,6 +43,35 @@ struct lenovo_drvdata_cptkbd {
 
 #define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
 
+static const __u8 lenovo_pro_dock_need_fixup_collection[] = {
+       0x05, 0x88,             /* Usage Page (Vendor Usage Page 0x88)  */
+       0x09, 0x01,             /* Usage (Vendor Usage 0x01)            */
+       0xa1, 0x01,             /* Collection (Application)             */
+       0x85, 0x04,             /*  Report ID (4)                       */
+       0x19, 0x00,             /*  Usage Minimum (0)                   */
+       0x2a, 0xff, 0xff,       /*  Usage Maximum (65535)               */
+};
+
+static __u8 *lenovo_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+               unsigned int *rsize)
+{
+       switch (hdev->product) {
+       case USB_DEVICE_ID_LENOVO_TPPRODOCK:
+               /* the fixups that need to be done:
+                *   - get a reasonable usage max for the vendor collection
+                *     0x8801 from the report ID 4
+                */
+               if (*rsize >= 153 &&
+                   memcmp(&rdesc[140], lenovo_pro_dock_need_fixup_collection,
+                         sizeof(lenovo_pro_dock_need_fixup_collection)) == 0) {
+                       rdesc[151] = 0x01;
+                       rdesc[152] = 0x00;
+               }
+               break;
+       }
+       return rdesc;
+}
+
 static int lenovo_input_mapping_tpkbd(struct hid_device *hdev,
                struct hid_input *hi, struct hid_field *field,
                struct hid_usage *usage, unsigned long **bit, int *max)
@@ -784,6 +813,7 @@ static const struct hid_device_id lenovo_devices[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CUSBKBD) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CBTKBD) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPPRODOCK) },
        { }
 };
 
@@ -797,6 +827,7 @@ static struct hid_driver lenovo_driver = {
        .probe = lenovo_probe,
        .remove = lenovo_remove,
        .raw_event = lenovo_raw_event,
+       .report_fixup = lenovo_report_fixup,
 };
 module_hid_driver(lenovo_driver);