From: Takashi Iwai Subject: [PATCH v3] HID: multitouch: add support for Atmel 212c touchscreen Patch-mainline: Never, only for SLE11-SP3/SP4 References: bnc#793727,bnc#863946,bnc#924142 This patch enables hid-multitouch support for the new Atmel maxTouch device with the device ID 212c. This device supports two modes, mouse and digitizer, and both modes can be switched dynamically, and badly enough, the device ID remains same. Thus we need to decide which driver to bind also on the fly at boot time. The upstream code has a check of hid group by parsing the descriptor beforehand. Unfortunately, this will need lots of more changes (also the corresponding part in udev), hence not appropriate for SLE11. In this patch, instead of implementing the whole pre-parse, we do parse and check only Win8 compliant device for only the given devices. And skip the generic driver when a Win8 MT device has been detected and forcibly bind to hid-multitouch. Yes, this is pretty hackish and doesn't scale. But, it's safer and less intrusive. Signed-off-by: Takashi Iwai --- drivers/hid/hid-core.c | 106 +++++++++++++++++++++++++++++++++++++++- drivers/hid/hid-ids.h | 3 + drivers/hid/hid-multitouch.c | 5 + drivers/hid/usbhid/hid-quirks.c | 1 4 files changed, 113 insertions(+), 2 deletions(-) --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -638,6 +638,86 @@ static u8 *fetch_item(__u8 *start, __u8 return NULL; } +/* SLE11 specific */ +#define HID_QUIRK_MULTITOUCH 0x04000000 +#define HID_QUIRK_MULTITOUCH_WIN_8 0x08000000 + +static int hid_scan_main(struct hid_parser *parser, struct hid_item *item) +{ + struct hid_device *hid = parser->device; + __u32 data; + int i; + + data = item_udata(item); + + switch (item->tag) { + case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: + break; + case HID_MAIN_ITEM_TAG_END_COLLECTION: + break; + case HID_MAIN_ITEM_TAG_INPUT: + /* ignore constant inputs, they will be ignored by hid-input */ + if (data & HID_MAIN_ITEM_CONSTANT) + break; + for (i = 0; i < parser->local.usage_index; i++) + if (parser->local.usage[i] == HID_DG_CONTACTID) + hid->quirks |= HID_QUIRK_MULTITOUCH; + break; + case HID_MAIN_ITEM_TAG_OUTPUT: + break; + case HID_MAIN_ITEM_TAG_FEATURE: + for (i = 0; i < parser->local.usage_index; i++) + if (parser->local.usage[i] == 0xff0000c5 && + parser->global.report_count == 256 && + parser->global.report_size == 8) + hid->quirks |= HID_QUIRK_MULTITOUCH_WIN_8; + break; + } + + /* Reset the local parser environment */ + memset(&parser->local, 0, sizeof(parser->local)); + + return 0; +} + +/* + * Parse a report descriptor to check whether it's a Win-8 multitouch device + * Two hid->quirks bits are used for keeping the parsed flags where the + * upstream driver uses parser->scan_flags instead. + */ +static int hid_parse_multitouch_win8(struct hid_device *hid) +{ + struct hid_parser *parser; + struct hid_item item; + __u8 *start = hid->rdesc; + __u8 *end = start + hid->rsize; + static int (*dispatch_type[])(struct hid_parser *parser, + struct hid_item *item) = { + hid_scan_main, + hid_parser_global, + hid_parser_local, + hid_parser_reserved + }; + + parser = vzalloc(sizeof(struct hid_parser)); + if (!parser) + return -ENOMEM; + + parser->device = hid; + hid->quirks &=~ (HID_QUIRK_MULTITOUCH | HID_QUIRK_MULTITOUCH_WIN_8); + + while ((start = fetch_item(start, end, &item)) != NULL) + dispatch_type[item.type](parser, &item); + + if ((hid->quirks & HID_QUIRK_MULTITOUCH) && + (hid->quirks & HID_QUIRK_MULTITOUCH_WIN_8)) + hid_info(hid, "Detected Win8 compliant touchscreen\n"); + else + hid->quirks &=~ (HID_QUIRK_MULTITOUCH | HID_QUIRK_MULTITOUCH_WIN_8); + vfree(parser); + return 0; +} + /** * hid_parse_report - parse device report * @@ -664,7 +744,7 @@ int hid_parse_report(struct hid_device * hid_parser_reserved }; - if (device->driver->report_fixup) + if (device->driver && device->driver->report_fixup) start = device->driver->report_fixup(device, start, &size); device->rdesc = kmemdup(start, size, GFP_KERNEL); @@ -1691,12 +1771,19 @@ static int hid_bus_match(struct device * struct hid_driver *hdrv = container_of(drv, struct hid_driver, driver); struct hid_device *hdev = container_of(dev, struct hid_device, dev); + if ((hdev->quirks & HID_QUIRK_MULTITOUCH_WIN_8) && + !strcmp(hdrv->name, "hid-multitouch")) + return 1; + if (!hid_match_device(hdev, hdrv)) return 0; /* generic wants all that don't have specialized driver */ - if (!strncmp(hdrv->name, "generic-", 8)) + if (!strncmp(hdrv->name, "generic-", 8)) { + if (hdev->quirks & HID_QUIRK_MULTITOUCH_WIN_8) + return 0; return !hid_match_id(hdev, hid_have_special_driver); + } return 1; } @@ -1992,6 +2079,12 @@ static const struct hid_device_id hid_mo { } }; +/* devices that need dynamic win8 touchscreen detection (e.g. Atmel) */ +static const struct hid_device_id hid_multitouch_win8_list[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ATMEL, USB_DEVICE_ID_ATMEL_MXT_DIG2) }, + {} +}; + static bool hid_ignore(struct hid_device *hdev) { switch (hdev->vendor) { @@ -2044,6 +2137,15 @@ int hid_add_device(struct hid_device *hd dev_set_name(&hdev->dev, "%04X:%04X:%04X.%04X", hdev->bus, hdev->vendor, hdev->product, atomic_inc_return(&id)); + if (hid_match_id(hdev, hid_multitouch_win8_list)) { + ret = hid_parse(hdev); + if (ret < 0) + return ret; + ret = hid_parse_multitouch_win8(hdev); + if (ret < 0) + return ret; + } + hid_debug_register(hdev, dev_name(&hdev->dev)); ret = device_add(&hdev->dev); if (!ret) --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -149,6 +149,9 @@ #define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205 #define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208 +#define USB_VENDOR_ID_ATMEL 0x03eb +#define USB_DEVICE_ID_ATMEL_MXT_DIG2 0x212c + #define USB_VENDOR_ID_AVERMEDIA 0x07ca #define USB_DEVICE_ID_AVER_FM_MR800 0xb800 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -655,6 +655,11 @@ static const struct hid_device_id mt_dev HID_USB_DEVICE(USB_VENDOR_ID_ACTIONSTAR, USB_DEVICE_ID_ACTIONSTAR_1011) }, + /* Atmel panels */ + { .driver_data = MT_CLS_WIN_8, + HID_USB_DEVICE(USB_VENDOR_ID_ATMEL, + USB_DEVICE_ID_ATMEL_MXT_DIG2) }, + /* Cando panels */ { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, HID_USB_DEVICE(USB_VENDOR_ID_CANDO, --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -33,6 +33,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_ADVANCED_SILICON, USB_DEVICE_ID_AS_TS_36B1, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD }, + { USB_VENDOR_ID_ATMEL, USB_DEVICE_ID_ATMEL_MXT_DIG2, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_DWAV, USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER, HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET }, { USB_VENDOR_ID_MOJO, USB_DEVICE_ID_RETRO_ADAPTER, HID_QUIRK_MULTI_INPUT },