Re: [PATCH] HID: Add driver for synaptics keybard with broken rdesc

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Jan 27, 2015 at 8:13 AM, Benjamin Tissoires
<benjamin.tissoires@xxxxxxxxx> wrote:
> On Tue, Jan 27, 2015 at 10:10 AM, Simon Wörner <linux@xxxxxxxxxxxxxxxx> wrote:
>> Thanks for the feedback, this is my first commit to the linux kernel so
>> I don't know much about how the input / hid is working.
>>
>> I will add the changelog to the patch email, just need to find out how
>> this works.
>>
>> Am 27.01.15 um 15:16 schrieb Benjamin Tissoires:
>>> On Fri, Jan 23, 2015 at 2:03 PM, Simon Wörner <linux@xxxxxxxxxxxxxxxx> wrote:
>>>> From: Simon Wörner <mail@xxxxxxxxxxxxxxxx>
>>>>
>>>> Signed-off-by: Simon Wörner <mail@xxxxxxxxxxxxxxxx>
>>>> ---
>>>>  drivers/hid/Kconfig         |   6 ++
>>>>  drivers/hid/Makefile        |   1 +
>>>>  drivers/hid/hid-ids.h       |   1 +
>>>>  drivers/hid/hid-synaptics.c | 190 ++++++++++++++++++++++++++++++++++++++++++++
>>>>  4 files changed, 198 insertions(+)
>>>>  create mode 100644 drivers/hid/hid-synaptics.c

This isn't actually a Synaptics keyboard. The product id matches a
touchpad so it appears that this is another situation where a vendor
is using our touchpad's VID and PID to describe a composite USB device
in a dock, even though our touchpad is only one of the devices in the
dock. This is similar to what happened on the Dell Venue Pro 11. I
think Synaptics should be removed from the name since it will be
confusing should Synaptics release a keyboard.

>>>
>>> I am pretty sure you are missing a change in hid-core.c to add your
>>> device to hid_have_special_driver.
>>>
>>
>> So I need to add this?
>>
>>> static const struct hid_device_id hid_have_special_driver[] = {
>>>       ...
>>>       { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_VENDOR_ID_SYNAPTICS_KBD) },
>>>       ...
>
> yep, seems fine.

Adding the VID and PID to the hid_have_special_driver will cause the
touchpad to stop working since it will prevent hid-multitouch from
binding to it. Maybe in hid-core.c add a new hid group and a new scan
flag for HID_GD_KEYBOARD and look for it in hid_scan_report()? But,
this would also match the keyboard in the Dell Venue Pro 11 and
potentially others, so the driver would need to check to see if the
report descriptor needs this fix or not.

>>
>> do I also need to add
>>
>>> #if IS_ENABLED(HID_SYNAPTICS)
>>> ...
>>> #endif
>>
>> or will hid-core handle that for me?
>
> no, the rule here is that we just blacklist the device, and if the
> module is not compiled, then... too bad :)
>
>>
>>> Other than that (and the 2comments Jiri made), I am not very happy
>>> with adding a hid-synaptics while we already have a hid-rmi for
>>> synaptics devices. However, the keyboard must not follow the rmi spec,
>>> so this might be just fine.
>>>
>>
>> I have seen the rmi module and haven't found anything they have in
>> common. The USB device of the keyboard has also attached a mouse /
>> trackpad, but it also doens't make use of the rmi module.
>>
>> I don't see any reason for putting both together, e.g. hid-holtek-* has
>> also two modules for keyboard rdesc fix and mouse rdesc fix.
>>
>> Maybe hid-synaptics-kbd would better fit?
>
> no hid-synaptics will be fine.
>
>>
>>> Few more comments:
>>>
>>>>
>>>> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
>>>> index dfdc269..9f4124e 100644
>>>> --- a/drivers/hid/Kconfig
>>>> +++ b/drivers/hid/Kconfig
>>>> @@ -708,6 +708,12 @@ config HID_SUNPLUS
>>>>         ---help---
>>>>         Support for Sunplus wireless desktop.
>>>>
>>>> +config HID_SYNAPTICS
>>>> +       tristate "Synaptics keyboard support"
>>>> +       depends on HID
>>>> +       ---help---
>>>> +       Support for Synaptics keyboard with invalid rdesc
>>>> +
>>>>  config HID_RMI
>>>>         tristate "Synaptics RMI4 device support"
>>>>         depends on HID
>>>> diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
>>>> index debd15b..01bda39 100644
>>>> --- a/drivers/hid/Makefile
>>>> +++ b/drivers/hid/Makefile
>>>> @@ -109,6 +109,7 @@ obj-$(CONFIG_HID_SONY)              += hid-sony.o
>>>>  obj-$(CONFIG_HID_SPEEDLINK)    += hid-speedlink.o
>>>>  obj-$(CONFIG_HID_STEELSERIES)  += hid-steelseries.o
>>>>  obj-$(CONFIG_HID_SUNPLUS)      += hid-sunplus.o
>>>> +obj-$(CONFIG_HID_SYNAPTICS)    += hid-synaptics.o
>>>>  obj-$(CONFIG_HID_GREENASIA)    += hid-gaff.o
>>>>  obj-$(CONFIG_HID_THINGM)       += hid-thingm.o
>>>>  obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o
>>>> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
>>>> index 9243359..976ab39 100644
>>>> --- a/drivers/hid/hid-ids.h
>>>> +++ b/drivers/hid/hid-ids.h
>>>> @@ -873,6 +873,7 @@
>>>>  #define USB_DEVICE_ID_SYNAPTICS_LTS2   0x1d10
>>>>  #define USB_DEVICE_ID_SYNAPTICS_HD     0x0ac3
>>>>  #define USB_DEVICE_ID_SYNAPTICS_QUAD_HD        0x1ac3
>>>> +#define USB_VENDOR_ID_SYNAPTICS_KBD    0x2968
>>>>  #define USB_DEVICE_ID_SYNAPTICS_TP_V103        0x5710
>>>>
>>>>  #define USB_VENDOR_ID_TEXAS_INSTRUMENTS        0x2047
>>>> diff --git a/drivers/hid/hid-synaptics.c b/drivers/hid/hid-synaptics.c
>>>> new file mode 100644
>>>> index 0000000..3dab405
>>>> --- /dev/null
>>>> +++ b/drivers/hid/hid-synaptics.c
>>>> @@ -0,0 +1,190 @@
>>>> +/*
>>>> + *  HID driver for synaptics devices
>>>> + *
>>>> + *  Copyright (c) 2015 Simon Wörner
>>>> + */
>>>> +
>>>> +/*
>>>> + * 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 <linux/usb.h>
>>>
>>> Please avoid using usb.h in HID drivers. HID should be transport agnostic.
>>>
>>
>> Okay.
>>
>>>> +
>>>> +#include "hid-ids.h"
>>>> +
>>>> +/* Synaptics keyboards (USB ID 06cb:2968) e.g. in Acer SW5-012
>>>> + * have the following issue:
>>>> + * - The report descriptor specifies an excessively large number of consumer
>>>> + *   usages (2^15), which is more than HID_MAX_USAGES. This prevents proper
>>>> + *   parsing of the report descriptor.
>>>> + *
>>>> + * The replacement descriptor below fixes the number of consumer usages.
>>>> + */
>>>> +
>>>> +static __u8 synaptics_kbd_rdesc_fixed[] = {
>>>> +       /* Application Collection */
>>>> +       0x06, 0x85, 0xFF,       /* (GLOBAL) USAGE_PAGE (Vendor-defined)       */
>>>> +       0x09, 0x95,             /* (LOCAL)  USAGE (0xFF850095)                */
>>>> +       0xA1, 0x01,             /* (MAIN)   COLLECTION (Application)          */
>>>> +       0x85, 0x5A,             /*   (GLOBAL) REPORT_ID (0x5A (90) 'Z')       */
>>>> +       0x09, 0x01,             /*   (LOCAL)  USAGE (0xFF850001)              */
>>>> +       0x26, 0xFF, 0x00,       /*   (GLOBAL) LOGICAL_MAXIMUM 0x00FF (255)    */
>>>> +       0x75, 0x08,             /*   (GLOBAL) REPORT_SIZE 0x08 (8)            */
>>>> +       0x95, 0x10,             /*   (GLOBAL) REPORT_COUNT 0x10 (16)          */
>>>> +       0xB1, 0x00,             /*   (MAIN)   FEATURE 0x00                    */
>>>> +       0xC0,                   /* (MAIN)   END_COLLECTION (Application)      */
>>>> +
>>>> +       /* Application Collection */
>>>> +       0x05, 0x01,             /* (GLOBAL) USAGE_PAGE (Desktop)              */
>>>> +       0x09, 0x06,             /* (LOCAL)  USAGE 0x06 (Keyboard)             */
>>>> +       0xA1, 0x01,             /* (MAIN)   COLLECTION (Application)          */
>>>> +       0x85, 0x01,             /*   (GLOBAL) REPORT_ID 0x01 (1)              */
>>>> +       0x75, 0x01,             /*   (GLOBAL) REPORT_SIZE 0x01 (1)            */
>>>> +       0x95, 0x08,             /*   (GLOBAL) REPORT_COUNT 0x08 (8)           */
>>>> +       0x05, 0x07,             /*   (GLOBAL) USAGE_PAGE 0x07 (Keyboard)      */
>>>> +       0x19, 0xE0,             /*   (LOCAL)  USAGE_MINIMUM 0xE0              */
>>>> +       0x29, 0xE7,             /*   (LOCAL)  USAGE_MAXIMUM 0xE7              */
>>>> +       0x25, 0x01,             /*   (GLOBAL) LOGICAL_MAXIMUM 0x01 (1)        */
>>>> +       0x81, 0x02,             /*   (MAIN)   INPUT 0x02                      */
>>>> +       0x95, 0x01,             /*   (GLOBAL) REPORT_COUNT 0x01 (1)           */
>>>> +       0x75, 0x08,             /*   (GLOBAL) REPORT_SIZE 0x08 (8)            */
>>>> +       0x81, 0x03,             /*   (MAIN)   INPUT 0x03                      */
>>>> +       0x95, 0x05,             /*   (GLOBAL) REPORT_COUNT 0x05 (5)           */
>>>> +       0x75, 0x01,             /*   (GLOBAL) REPORT_SIZE 0x01 (1)            */
>>>> +       0x05, 0x08,             /*   (GLOBAL) USAGE_PAGE 0x08 (LED Indicator) */
>>>> +       0x19, 0x01,             /*   (LOCAL)  USAGE_MINIMUM 0x01              */
>>>> +       0x29, 0x05,             /*   (LOCAL)  USAGE_MAXIMUM 0x05              */
>>>> +       0x91, 0x02,             /*   (MAIN)   OUTPUT 0x02                     */
>>>> +       0x95, 0x01,             /*   (GLOBAL) REPORT_COUNT 0x01 (1)           */
>>>> +       0x75, 0x03,             /*   (GLOBAL) REPORT_SIZE 0x03 (3)            */
>>>> +       0x91, 0x03,             /*   (MAIN)   OUTPUT 0x03                     */
>>>> +       0x95, 0x06,             /*   (GLOBAL) REPORT_COUNT 0x06 (6)           */
>>>> +       0x75, 0x08,             /*   (GLOBAL) REPORT_SIZE 0x08 (8)            */
>>>> +       0x26, 0xFF, 0x00,       /*   (GLOBAL) LOGICAL_MAXIMUM 0x00FF (255)    */
>>>> +       0x05, 0x07,             /*   (GLOBAL) USAGE_PAGE 0x0007 (Keyboard)    */
>>>> +       0x19, 0x00,             /*   (LOCAL)  USAGE_MINIMUM 0x00              */
>>>> +       0x2A, 0xFF, 0x00,       /*   (LOCAL)  USAGE_MAXIMUM 0xFF              */
>>>> +       0x81, 0x00,             /*   (MAIN)   INPUT 0x00                      */
>>>> +       0xC0,                   /* (MAIN)   END_COLLECTION (Application)      */
>>>> +
>>>> +       /* Application Collection */
>>>> +       0x05, 0x0C,             /* (GLOBAL) USAGE_PAGE (Consumer)             */
>>>> +       0x09, 0x01,             /* (LOCAL)  USAGE 0x01 (Consumer Control)     */
>>>> +       0xA1, 0x01,             /* (MAIN)   COLLECTION (Application)          */
>>>> +       0x85, 0x02,             /*   (GLOBAL) REPORT_ID 0x02 (2)              */
>>>> +       0x19, 0x00,             /*   (LOCAL)  USAGE_MINIMUM 0x00              */
>>>> +       0x2A, 0x3C, 0x02,       /*   (LOCAL)  USAGE_MAXIMUM 0x023C            */
>>>> +       0x26, 0x3C, 0x02,       /*   (GLOBAL) LOGICAL_MAXIMUM 0x023C (572)    */
>>>> +       0x75, 0x10,             /*   (GLOBAL) REPORT_SIZE 0x10 (16)           */
>>>> +       0x95, 0x01,             /*   (GLOBAL) REPORT_COUNT 0x01 (1)           */
>>>> +       0x81, 0x00,             /*   (MAIN)   INPUT 0x00                      */
>>>> +       0xC0,                   /* (MAIN)   END_COLLECTION (Application)      */
>>>> +
>>>> +       /* Application Collection */
>>>> +       0x05, 0x01,             /* (GLOBAL) USAGE_PAGE (Desktop)              */
>>>> +       0x09, 0x0C,             /* (LOCAL)  USAGE (Wireless Radio Controls)   */
>>>> +       0xA1, 0x01,             /* (MAIN)   COLLECTION (Application)          */
>>>> +       0x85, 0x03,             /*   (GLOBAL) REPORT_ID 0x03 (3)              */
>>>> +       0x25, 0x01,             /*   (GLOBAL) LOGICAL_MAXIMUM 0x01 (1)        */
>>>> +       0x09, 0xC6,             /*   (LOCAL)  USAGE 0xC6                      */
>>>> +       0x75, 0x01,             /*   (GLOBAL) REPORT_SIZE 0x01 (1)            */
>>>> +       0x81, 0x06,             /*   (MAIN)   INPUT 0x06                      */
>>>> +       0x75, 0x07,             /*   (GLOBAL) REPORT_SIZE 0x07 (7)            */
>>>> +       0x81, 0x03,             /*   (MAIN)   INPUT 0x03                      */
>>>> +       0xC0,                   /* (MAIN)   END_COLLECTION (Application)      */
>>>> +
>>>> +       /* Application Collection */
>>>> +       0x05, 0x88,             /* (GLOBAL) USAGE_PAGE (0x88)                 */
>>>> +       0x09, 0x01,             /* (LOCAL)  USAGE (0x01)                      */
>>>> +       0xA1, 0x01,             /* (MAIN)   COLLECTION (Application)          */
>>>> +       0x85, 0x04,             /*   (GLOBAL) REPORT_ID 0x04 (4)              */
>>>> +       0x19, 0x00,             /*   (LOCAL)  USAGE_MINIMUM 0x00              */
>>>> +       0x2A, 0xFF, 0x00,       /*   (LOCAL)  USAGE_MAXIMUM 0x00FF            */
>>>> +       0x26, 0xFF, 0x00,       /*   (GLOBAL) LOGICAL_MAXIMUM 0x00FF          */
>>>> +       0x75, 0x08,             /*   (GLOBAL) REPORT_SIZE 0x08 (8)            */
>>>> +       0x95, 0x02,             /*   (GLOBAL) REPORT_COUNT 0x02 (2)           */
>>>> +       0x81, 0x02,             /*   (MAIN)   INPUT 0x02                      */
>>>> +       0xC0,                   /* (MAIN)   END_COLLECTION (Application)      */
>>>> +
>>>> +       /* Application Collection */
>>>> +       0x05, 0x01,             /* (GLOBAL) USAGE_PAGE (Desktop)              */
>>>> +       0x09, 0x80,             /* (LOCAL)  USAGE (System Control)            */
>>>> +       0xA1, 0x01,             /* (MAIN)   COLLECTION (Application)          */
>>>> +       0x85, 0x05,             /*   (GLOBAL) REPORT_ID 0x05 (5)              */
>>>> +       0x19, 0x81,             /*   (LOCAL)  USAGE_MINIMUM 0x81              */
>>>> +       0x29, 0x83,             /*   (LOCAL)  USAGE_MAXIMUM 0x83              */
>>>> +       0x25, 0x01,             /*   (GLOBAL) LOGICAL_MAXIMUM 0x01 (1)        */
>>>> +       0x95, 0x08,             /*   (GLOBAL) REPORT_COUNT 0x08 (8)           */
>>>> +       0x75, 0x01,             /*   (GLOBAL) REPORT_SIZE 0x01 (1)            */
>>>> +       0x81, 0x02,             /*   (MAIN)   INPUT 0x02                      */
>>>> +       0xC0,                   /* (MAIN)   END_COLLECTION (Application)      */
>>>> +};
>>>> +
>>>> +static __u8 *synaptics_kbd_report_fixup(struct hid_device *hdev, __u8 *rdesc,
>>>> +               unsigned int *rsize)
>>>> +{
>>>> +       struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
>>>> +
>>>> +       if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
>>>
>>> Please don't. This is too fragile and we can not use uhid to replay
>>> and test such devices.
>>> Make a check on the size of the original descriptor, or check which
>>> bits you are replacing to know which interface you want to change the
>>> report descriptor.
>>> You can also check on the HID device .type which may be different
>>> among the various interfaces.
>>>
>>
>> Is a size check safe enough? Maybe synaptics starts to change the rdesc.
>>
>> I'm just replacing two bytes:
>>
>>> 0x2A, 0xFF, 0x00,       /*   (LOCAL)  USAGE_MAXIMUM 0x00FF
>>> 0x26, 0xFF, 0x00,       /*   (GLOBAL) LOGICAL_MAXIMUM 0x00FF
>>
>> used to be
>>
>>> 0x2A, 0xFF, 0xFF,       /*   (LOCAL)  USAGE_MAXIMUM 0xFFFF
>>> 0x26, 0xFF, 0xFF,       /*   (GLOBAL) LOGICAL_MAXIMUM 0xFFFF
>>
>> and I removed some unneeded USAGE_MINIMUM which are already set to 0
>> globally (which doesn't make any difference).
>>
>> The smallest solution would be a byte copy over it, but I think it
>> wouldn't be readable later if there occur other issue or if synaptics
>> starts selling devices with changed rdesc and same device id.
>>
>> I have seen rdesc fixes with full copy and byte copy in other modules,
>> are there any suggestions when to use what?
>
> there is no rule. In your case, I would prefer a byte copy because the
> change is small enough and this would prevent any copyright issues.
> If possible, reuse the same way of fixing that
> kye_consumer_control_fixup() in hid-kye.c
>
> If it is clearly documented, there will not be further mistakes.
>
>>
>>
>>>> +               rdesc = synaptics_kbd_rdesc_fixed;
>>>> +               *rsize = sizeof(synaptics_kbd_rdesc_fixed);
>>>> +       }
>>>> +       return rdesc;
>>>> +}
>>>> +
>>>> +static int synaptics_probe(struct hid_device *hdev,
>>>> +               const struct hid_device_id *id)
>>>> +{
>>>> +       int ret;
>>>> +
>>>> +       ret = hid_parse(hdev);
>>>> +       if (ret) {
>>>> +               hid_err(hdev, "parse failed\n");
>>>> +               goto err_free;
>>>> +       }
>>>> +
>>>> +       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
>>>> +       if (ret) {
>>>> +               hid_err(hdev, "hw start failed\n");
>>>> +               goto err_free;
>>>> +       }
>>>> +
>>>> +       if (ret < 0)
>>>> +               goto err_stop;
>>>> +
>>>> +       return 0;
>>>> +err_stop:
>>>> +       hid_hw_stop(hdev);
>>>> +err_free:
>>>> +       return ret;
>>>> +}
>>
>> I looked around how other modules handle this and didn't came up with a
>> better name for the label, but this obsolete if I remove the
>> probe/remove (see below).
>>
>>>
>>> I am pretty sure there is no need for a probe here. Hid-core will use
>>> the generic one and you should be fine.
>>>
>>
>> So when I remove the function(s) and reference(s) in hid_driver for the
>> probe and the remove hid_core will handle that for me?
>
> Yeah, your driver does not allocated anything, so probe and remove
> should not be used. Hid-core will take care of everything.
>
> Cheers,
> Benjamin
>
>>
>>>> +
>>>> +static void synaptics_remove(struct hid_device *hdev)
>>>> +{
>>>> +       hid_hw_stop(hdev);
>>>> +       kfree(hid_get_drvdata(hdev));
>>>> +}
>>>
>>> Same here, not mandatory.
>>>
>>> Cheers,
>>> Benjamin
>>>
>>>> +
>>>> +static const struct hid_device_id synaptics_devices[] = {
>>>> +       { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS,
>>>> +               USB_VENDOR_ID_SYNAPTICS_KBD) },
>>>> +       { }
>>>> +};
>>>> +MODULE_DEVICE_TABLE(hid, synaptics_devices);
>>>> +
>>>> +static struct hid_driver synaptics_driver = {
>>>> +       .name = "synaptics",
>>>> +       .id_table = synaptics_devices,
>>>> +       .report_fixup = synaptics_kbd_report_fixup,
>>>> +       .probe = synaptics_probe,
>>>> +       .remove = synaptics_remove,
>>>> +};
>>>> +module_hid_driver(synaptics_driver);
>>>> +
>>>> +MODULE_LICENSE("GPL");
>>>> --
>>>> 2.2.2
>>>>
>>>> --
>>>> To unsubscribe from this list: send the line "unsubscribe linux-input" in
>>>> the body of a message to majordomo@xxxxxxxxxxxxxxx
>>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux