diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-23 04:21:48 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-23 04:21:48 +0200 |
commit | 3c2c4b73aa79e4a1b601710b59e092441175f4bb (patch) | |
tree | fbd08c3e18517871b25fe9c886caae423947d578 /drivers | |
parent | Merge branch 'x86-apic-for-linus' of git://git.kernel.org/pub/scm/linux/kerne... (diff) | |
parent | Merge branches 'upstream-fixes', 'wacom' and 'waltop' into for-linus (diff) | |
download | linux-3c2c4b73aa79e4a1b601710b59e092441175f4bb.tar.xz linux-3c2c4b73aa79e4a1b601710b59e092441175f4bb.zip |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
Pull HID subsystem updates from Jiri Kosina:
"Apart from various driver updates and added support for a number of
new devices (mostly multitouch ones, but not limited to), there is one
change that is worth pointing out explicitly: creation of HID device
groups and proper autoloading of hid-multitouch, implemented by Henrik
Rydberg."
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (50 commits)
HID: wacom: fix build breakage without CONFIG_LEDS_CLASS
HID: waltop: Extend barrel button fix
HID: hyperv: Set the hid drvdata correctly
HID: wacom: Unify speed setting
HID: wacom: Add speed setting for Intuos4 WL
HID: wacom: Move Graphire raport header check.
HID: uclogic: Add support for UC-Logic TWHL850
HID: explain the signed/unsigned handling in hid_add_field()
HID: handle logical min/max signedness properly in parser
HID: logitech: read all 32 bits of report type bitfield
HID: wacom: Add LED selector control for Wacom Intuos4 WL
HID: hid-multitouch: fix wrong protocol detection
HID: wiimote: Fix IR data parser
HID: wacom: Add tilt reporting for Intuos4 WL
HID: multitouch: MT interface matching for Baanto
HID: hid-multitouch: Only match MT interfaces
HID: Create a common generic driver
HID: hid-multitouch: Switch to device groups
HID: Create a generic device group
HID: Allow bus wildcard matching
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/hid/Kconfig | 34 | ||||
-rw-r--r-- | drivers/hid/Makefile | 3 | ||||
-rw-r--r-- | drivers/hid/hid-apple.c | 5 | ||||
-rw-r--r-- | drivers/hid/hid-aureal.c | 54 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 316 | ||||
-rw-r--r-- | drivers/hid/hid-generic.c | 53 | ||||
-rw-r--r-- | drivers/hid/hid-hyperv.c | 15 | ||||
-rw-r--r-- | drivers/hid/hid-ids.h | 8 | ||||
-rw-r--r-- | drivers/hid/hid-input.c | 36 | ||||
-rw-r--r-- | drivers/hid/hid-lg.c | 55 | ||||
-rw-r--r-- | drivers/hid/hid-lg.h | 5 | ||||
-rw-r--r-- | drivers/hid/hid-lg4ff.c | 258 | ||||
-rw-r--r-- | drivers/hid/hid-logitech-dj.c | 76 | ||||
-rw-r--r-- | drivers/hid/hid-multitouch.c | 233 | ||||
-rw-r--r-- | drivers/hid/hid-uclogic.c | 141 | ||||
-rw-r--r-- | drivers/hid/hid-wacom.c | 302 | ||||
-rw-r--r-- | drivers/hid/hid-waltop.c | 230 | ||||
-rw-r--r-- | drivers/hid/hid-wiimote-core.c | 16 | ||||
-rw-r--r-- | drivers/hid/hidraw.c | 19 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-core.c | 81 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-quirks.c | 1 | ||||
-rw-r--r-- | drivers/hid/usbhid/hiddev.c | 9 | ||||
-rw-r--r-- | drivers/hid/usbhid/usbhid.h | 1 |
23 files changed, 1432 insertions, 519 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index ffddcba32af6..e9c68fedfcff 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -32,9 +32,13 @@ config HID If unsure, say Y. config HID_BATTERY_STRENGTH - bool + bool "Battery level reporting for HID devices" depends on HID && POWER_SUPPLY && HID = POWER_SUPPLY default n + ---help--- + This option adds support of reporting battery strength (for HID devices + that support this feature) through power_supply class so that userspace + tools, such as upower, can display it. config HIDRAW bool "/dev/hidraw raw HID device support" @@ -60,6 +64,18 @@ source "drivers/hid/usbhid/Kconfig" menu "Special HID drivers" depends on HID +config HID_GENERIC + tristate "Generic HID driver" + depends on HID + default y + ---help--- + Support for generic HID devices. + + To compile this driver as a module, choose M here: the module + will be called hid-generic. + + If unsure, say Y. + config HID_A4TECH tristate "A4 tech mice" if EXPERT depends on USB_HID @@ -92,6 +108,12 @@ config HID_APPLE Say Y here if you want support for keyboards of Apple iBooks, PowerBooks, MacBooks, MacBook Pros and Apple Aluminum. +config HID_AUREAL + tristate "Aureal" + depends on USB_HID + ---help--- + Support for Aureal Cy se W-01RN Remote Controller and other Aureal derived remotes. + config HID_BELKIN tristate "Belkin Flip KVM and Wireless keyboard" if EXPERT depends on USB_HID @@ -595,16 +617,10 @@ config THRUSTMASTER_FF config HID_WACOM tristate "Wacom Bluetooth devices support" depends on BT_HIDP - ---help--- - Support for Wacom Graphire Bluetooth tablet. - -config HID_WACOM_POWER_SUPPLY - bool "Wacom Bluetooth devices power supply status support" - depends on HID_WACOM + depends on LEDS_CLASS select POWER_SUPPLY ---help--- - Say Y here if you want to enable power supply status monitoring for - Wacom Bluetooth devices. + Support for Wacom Graphire Bluetooth and Intuos4 WL tablets. config HID_WIIMOTE tristate "Nintendo Wii Remote support" diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 22f1d16cd79c..ca6cc9f0485c 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -9,6 +9,8 @@ endif obj-$(CONFIG_HID) += hid.o +obj-$(CONFIG_HID_GENERIC) += hid-generic.o + hid-$(CONFIG_HIDRAW) += hidraw.o hid-logitech-y := hid-lg.o @@ -36,6 +38,7 @@ endif obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o obj-$(CONFIG_HID_ACRUX) += hid-axff.o obj-$(CONFIG_HID_APPLE) += hid-apple.o +obj-$(CONFIG_HID_AUREAL) += hid-aureal.o obj-$(CONFIG_HID_BELKIN) += hid-belkin.o obj-$(CONFIG_HID_CHERRY) += hid-cherry.o obj-$(CONFIG_HID_CHICONY) += hid-chicony.o diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 299d23871122..fa10f847f7db 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -234,7 +234,7 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, } } - if (iso_layout) { + if (iso_layout) { if (asc->quirks & APPLE_ISO_KEYBOARD) { trans = apple_find_translation(apple_iso_keyboard, usage->code); if (trans) { @@ -458,6 +458,9 @@ static const struct hid_device_id apple_devices[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI), + .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI), diff --git a/drivers/hid/hid-aureal.c b/drivers/hid/hid-aureal.c new file mode 100644 index 000000000000..ba64b041b8bf --- /dev/null +++ b/drivers/hid/hid-aureal.c @@ -0,0 +1,54 @@ +/* + * HID driver for Aureal Cy se W-01RN USB_V3.1 devices + * + * Copyright (c) 2010 Franco Catrin <fcatrin@gmail.com> + * Copyright (c) 2010 Ben Cropley <bcropley@internode.on.net> + * + * Based on HID sunplus driver by + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> + * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc + * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + * Copyright (c) 2008 Jiri Slaby + */ +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +static __u8 *aureal_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + if (*rsize >= 54 && rdesc[52] == 0x25 && rdesc[53] == 0x01) { + dev_info(&hdev->dev, "fixing Aureal Cy se W-01RN USB_V3.1 report descriptor.\n"); + rdesc[53] = 0x65; + } return rdesc; +} + +static const struct hid_device_id aureal_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_AUREAL, USB_DEVICE_ID_AUREAL_W01RN) }, + { } +}; +MODULE_DEVICE_TABLE(hid, aureal_devices); + +static struct hid_driver aureal_driver = { + .name = "aureal", + .id_table = aureal_devices, + .report_fixup = aureal_report_fixup, +}; + +static int __init aureal_init(void) +{ + return hid_register_driver(&aureal_driver); +} + +static void __exit aureal_exit(void) +{ + hid_unregister_driver(&aureal_driver); +} + +module_init(aureal_init); +module_exit(aureal_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 4da66b4b977c..8e3a6b261477 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -230,9 +230,16 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign return -1; } - if (parser->global.logical_maximum < parser->global.logical_minimum) { - hid_err(parser->device, "logical range invalid %d %d\n", - parser->global.logical_minimum, parser->global.logical_maximum); + /* Handle both signed and unsigned cases properly */ + if ((parser->global.logical_minimum < 0 && + parser->global.logical_maximum < + parser->global.logical_minimum) || + (parser->global.logical_minimum >= 0 && + (__u32)parser->global.logical_maximum < + (__u32)parser->global.logical_minimum)) { + dbg_hid("logical range invalid 0x%x 0x%x\n", + parser->global.logical_minimum, + parser->global.logical_maximum); return -1; } @@ -546,12 +553,11 @@ static void hid_free_report(struct hid_report *report) } /* - * Free a device structure, all reports, and all fields. + * Close report. This function returns the device + * state to the point prior to hid_open_report(). */ - -static void hid_device_release(struct device *dev) +static void hid_close_report(struct hid_device *device) { - struct hid_device *device = container_of(dev, struct hid_device, dev); unsigned i, j; for (i = 0; i < HID_REPORT_TYPES; i++) { @@ -562,11 +568,34 @@ static void hid_device_release(struct device *dev) if (report) hid_free_report(report); } + memset(report_enum, 0, sizeof(*report_enum)); + INIT_LIST_HEAD(&report_enum->report_list); } kfree(device->rdesc); + device->rdesc = NULL; + device->rsize = 0; + kfree(device->collection); - kfree(device); + device->collection = NULL; + device->collection_size = 0; + device->maxcollection = 0; + device->maxapplication = 0; + + device->status &= ~HID_STAT_PARSED; +} + +/* + * Free a device structure, all reports, and all fields. + */ + +static void hid_device_release(struct device *dev) +{ + struct hid_device *hid = container_of(dev, struct hid_device, dev); + + hid_close_report(hid); + kfree(hid->dev_rdesc); + kfree(hid); } /* @@ -636,6 +665,60 @@ static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item) return NULL; } +static void hid_scan_usage(struct hid_device *hid, u32 usage) +{ + if (usage == HID_DG_CONTACTID) + hid->group = HID_GROUP_MULTITOUCH; +} + +/* + * Scan a report descriptor before the device is added to the bus. + * Sets device groups and other properties that determine what driver + * to load. + */ +static int hid_scan_report(struct hid_device *hid) +{ + unsigned int page = 0, delim = 0; + __u8 *start = hid->dev_rdesc; + __u8 *end = start + hid->dev_rsize; + unsigned int u, u_min = 0, u_max = 0; + struct hid_item item; + + hid->group = HID_GROUP_GENERIC; + while ((start = fetch_item(start, end, &item)) != NULL) { + if (item.format != HID_ITEM_FORMAT_SHORT) + return -EINVAL; + if (item.type == HID_ITEM_TYPE_GLOBAL) { + if (item.tag == HID_GLOBAL_ITEM_TAG_USAGE_PAGE) + page = item_udata(&item) << 16; + } else if (item.type == HID_ITEM_TYPE_LOCAL) { + if (delim > 1) + break; + u = item_udata(&item); + if (item.size <= 2) + u += page; + switch (item.tag) { + case HID_LOCAL_ITEM_TAG_DELIMITER: + delim += !!u; + break; + case HID_LOCAL_ITEM_TAG_USAGE: + hid_scan_usage(hid, u); + break; + case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: + u_min = u; + break; + case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM: + u_max = u; + for (u = u_min; u <= u_max; u++) + hid_scan_usage(hid, u); + break; + } + } + } + + return 0; +} + /** * hid_parse_report - parse device report * @@ -643,15 +726,37 @@ static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item) * @start: report start * @size: report size * + * Allocate the device report as read by the bus driver. This function should + * only be called from parse() in ll drivers. + */ +int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size) +{ + hid->dev_rdesc = kmemdup(start, size, GFP_KERNEL); + if (!hid->dev_rdesc) + return -ENOMEM; + hid->dev_rsize = size; + return 0; +} +EXPORT_SYMBOL_GPL(hid_parse_report); + +/** + * hid_open_report - open a driver-specific device report + * + * @device: hid device + * * Parse a report description into a hid_device structure. Reports are * enumerated, fields are attached to these reports. * 0 returned on success, otherwise nonzero error value. + * + * This function (or the equivalent hid_parse() macro) should only be + * called from probe() in drivers, before starting the device. */ -int hid_parse_report(struct hid_device *device, __u8 *start, - unsigned size) +int hid_open_report(struct hid_device *device) { struct hid_parser *parser; struct hid_item item; + unsigned int size; + __u8 *start; __u8 *end; int ret; static int (*dispatch_type[])(struct hid_parser *parser, @@ -662,6 +767,14 @@ int hid_parse_report(struct hid_device *device, __u8 *start, hid_parser_reserved }; + if (WARN_ON(device->status & HID_STAT_PARSED)) + return -EBUSY; + + start = device->dev_rdesc; + if (WARN_ON(!start)) + return -ENODEV; + size = device->dev_rsize; + if (device->driver->report_fixup) start = device->driver->report_fixup(device, start, &size); @@ -679,6 +792,15 @@ int hid_parse_report(struct hid_device *device, __u8 *start, parser->device = device; end = start + size; + + device->collection = kcalloc(HID_DEFAULT_NUM_COLLECTIONS, + sizeof(struct hid_collection), GFP_KERNEL); + if (!device->collection) { + ret = -ENOMEM; + goto err; + } + device->collection_size = HID_DEFAULT_NUM_COLLECTIONS; + ret = -EINVAL; while ((start = fetch_item(start, end, &item)) != NULL) { @@ -704,6 +826,7 @@ int hid_parse_report(struct hid_device *device, __u8 *start, goto err; } vfree(parser); + device->status |= HID_STAT_PARSED; return 0; } } @@ -711,9 +834,10 @@ int hid_parse_report(struct hid_device *device, __u8 *start, hid_err(device, "item fetching failed at offset %d\n", (int)(end - start)); err: vfree(parser); + hid_close_report(device); return ret; } -EXPORT_SYMBOL_GPL(hid_parse_report); +EXPORT_SYMBOL_GPL(hid_open_report); /* * Convert a signed n-bit integer to signed 32-bit integer. Common @@ -1032,7 +1156,7 @@ static struct hid_report *hid_get_report(struct hid_report_enum *report_enum, return report; } -void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, +int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, int interrupt) { struct hid_report_enum *report_enum = hid->report_enum + type; @@ -1040,10 +1164,11 @@ void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, unsigned int a; int rsize, csize = size; u8 *cdata = data; + int ret = 0; report = hid_get_report(report_enum, data); if (!report) - return; + goto out; if (report_enum->numbered) { cdata++; @@ -1063,14 +1188,19 @@ void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event) hid->hiddev_report_event(hid, report); - if (hid->claimed & HID_CLAIMED_HIDRAW) - hidraw_report_event(hid, data, size); + if (hid->claimed & HID_CLAIMED_HIDRAW) { + ret = hidraw_report_event(hid, data, size); + if (ret) + goto out; + } for (a = 0; a < report->maxfield; a++) hid_input_field(hid, report->field[a], cdata, interrupt); if (hid->claimed & HID_CLAIMED_INPUT) hidinput_report_event(hid, report); +out: + return ret; } EXPORT_SYMBOL_GPL(hid_report_raw_event); @@ -1147,7 +1277,7 @@ nomem: } } - hid_report_raw_event(hid, type, data, size, interrupt); + ret = hid_report_raw_event(hid, type, data, size, interrupt); unlock: up(&hid->driver_lock); @@ -1158,7 +1288,8 @@ EXPORT_SYMBOL_GPL(hid_input_report); static bool hid_match_one_id(struct hid_device *hdev, const struct hid_device_id *id) { - return id->bus == hdev->bus && + return (id->bus == HID_BUS_ANY || id->bus == hdev->bus) && + (id->group == HID_GROUP_ANY || id->group == hdev->group) && (id->vendor == HID_ANY_ID || id->vendor == hdev->vendor) && (id->product == HID_ANY_ID || id->product == hdev->product); } @@ -1234,10 +1365,6 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask) if ((connect_mask & HID_CONNECT_HIDINPUT) && !hidinput_connect(hdev, connect_mask & HID_CONNECT_HIDINPUT_FORCE)) hdev->claimed |= HID_CLAIMED_INPUT; - if (hdev->quirks & HID_QUIRK_MULTITOUCH) { - /* this device should be handled by hid-multitouch, skip it */ - return -ENODEV; - } if ((connect_mask & HID_CONNECT_HIDDEV) && hdev->hiddev_connect && !hdev->hiddev_connect(hdev, @@ -1314,13 +1441,10 @@ EXPORT_SYMBOL_GPL(hid_disconnect); /* a list of devices for which there is a specialized driver on HID bus */ static const struct hid_device_id hid_have_special_driver[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) }, - { HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M2256) }, { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) }, { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) }, { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_RP_649) }, { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) }, - { HID_USB_DEVICE(USB_VENDOR_ID_ACTIONSTAR, USB_DEVICE_ID_ACTIONSTAR_1011) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) }, @@ -1385,60 +1509,33 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, - { HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_T91MT) }, - { HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUSTEK_MULTITOUCH_YFO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AUREAL, USB_DEVICE_ID_AUREAL_W01RN) }, { HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM) }, { HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE_2) }, - { HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH) }, - { HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_MULTI_TOUCH) }, - { HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_MULTI_TOUCH_10_1) }, - { HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_MULTI_TOUCH_11_6) }, - { HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_MULTI_TOUCH_15_6) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION_SOLAR) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS2) }, - { HID_USB_DEVICE(USB_VENDOR_ID_CHUNGHWAT, USB_DEVICE_ID_CHUNGHWAT_MULTITOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_PRODIKEYS_PCMIDI) }, - { HID_USB_DEVICE(USB_VENDOR_ID_CVTOUCH, USB_DEVICE_ID_CVTOUCH_SCREEN) }, { 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_BARCODE_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) }, - { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_TRUETOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) }, { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480D) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_725E) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302) }, - { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, - { HID_USB_DEVICE(USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2515) }, { HID_USB_DEVICE(USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II) }, { HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) }, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_FRUCTEL, USB_DEVICE_ID_GAMETEL_MT_MODE) }, { 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) }, - { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS) }, - { HID_USB_DEVICE(USB_VENDOR_ID_GOODTOUCH, USB_DEVICE_ID_GOODTOUCH_000f) }, { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003) }, { 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_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) }, - { HID_USB_DEVICE(USB_VENDOR_ID_HANVON, USB_DEVICE_ID_HANVON_MULTITOUCH) }, - { HID_USB_DEVICE(USB_VENDOR_ID_HANVON_ALT, USB_DEVICE_ID_HANVON_ALT_MULTITOUCH) }, - { HID_USB_DEVICE(USB_VENDOR_ID_IDEACOM, USB_DEVICE_ID_IDEACOM_IDC6650) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) }, - { HID_USB_DEVICE(USB_VENDOR_ID_ILITEK, USB_DEVICE_ID_ILITEK_MULTITOUCH) }, - { HID_USB_DEVICE(USB_VENDOR_ID_IRTOUCHSYSTEMS, USB_DEVICE_ID_IRTOUCH_INFRARED_USB) }, { HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) }, { HID_USB_DEVICE(USB_VENDOR_ID_KEYTOUCH, USB_DEVICE_ID_KEYTOUCH_IEC) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) }, @@ -1447,7 +1544,6 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) }, { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000 ) }, - { HID_USB_DEVICE(USB_VENDOR_ID_LG, USB_DEVICE_ID_LG_MULTITOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) }, @@ -1480,8 +1576,6 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) }, - { HID_USB_DEVICE(USB_VENDOR_ID_LUMIO, USB_DEVICE_ID_CRYSTALTOUCH) }, - { HID_USB_DEVICE(USB_VENDOR_ID_LUMIO, USB_DEVICE_ID_CRYSTALTOUCH_DUAL) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_COMFORT_MOUSE_4500) }, @@ -1513,15 +1607,8 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_18) }, { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_PKB1700) }, { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) }, - { HID_USB_DEVICE(USB_VENDOR_ID_PANASONIC, USB_DEVICE_ID_PANABOARD_UBT780) }, - { HID_USB_DEVICE(USB_VENDOR_ID_PANASONIC, USB_DEVICE_ID_PANABOARD_UBT880) }, - { HID_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_PCI) }, { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, - { HID_USB_DEVICE(USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN) }, - { HID_USB_DEVICE(USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1) }, - { HID_USB_DEVICE(USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2) }, { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) }, - { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) }, @@ -1538,9 +1625,6 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) }, - { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM, USB_DEVICE_ID_MTP) }, - { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM, USB_DEVICE_ID_MTP_STM) }, - { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_SITRONIX, USB_DEVICE_ID_MTP_SITRONIX) }, { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) }, @@ -1554,16 +1638,13 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_TIVO, USB_DEVICE_ID_TIVO_SLIDE) }, { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) }, { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED2, USB_DEVICE_ID_TOPSEED2_RF_COMBO) }, - { HID_USB_DEVICE(USB_VENDOR_ID_TOUCH_INTL, USB_DEVICE_ID_TOUCH_INTL_MULTI_TOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_TWINHAN, USB_DEVICE_ID_TWINHAN_IR_REMOTE) }, - { HID_USB_DEVICE(USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UNITEC, USB_DEVICE_ID_UNITEC_USB_TOUCH_0709) }, - { HID_USB_DEVICE(USB_VENDOR_ID_UNITEC, USB_DEVICE_ID_UNITEC_USB_TOUCH_0A19) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SUPER_JOY_BOX_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD) }, @@ -1578,16 +1659,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_PID_0038) }, { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH) }, - { HID_USB_DEVICE(USB_VENDOR_ID_XAT, USB_DEVICE_ID_XAT_CSR) }, - { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_SPX) }, - { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_MPX) }, - { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_CSR) }, - { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_SPX1) }, - { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_MPX1) }, - { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_CSR1) }, - { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_SPX2) }, - { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_MPX2) }, - { HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_CSR2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET) }, { HID_USB_DEVICE(USB_VENDOR_ID_X_TENSIONS, USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE) }, { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) }, { HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) }, @@ -1631,6 +1703,7 @@ static ssize_t store_new_id(struct device_driver *drv, const char *buf, return -ENOMEM; dynid->id.bus = bus; + dynid->id.group = HID_GROUP_ANY; dynid->id.vendor = vendor; dynid->id.product = product; dynid->id.driver_data = driver_data; @@ -1679,18 +1752,7 @@ static int hid_bus_match(struct device *dev, struct device_driver *drv) 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) && - !strncmp(hdrv->name, "hid-multitouch", 14)) - 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) && !hid_ignore_special_drivers) - return !hid_match_id(hdev, hid_have_special_driver); - - return 1; + return hid_match_device(hdev, hdrv) != NULL; } static int hid_device_probe(struct device *dev) @@ -1707,23 +1769,22 @@ static int hid_device_probe(struct device *dev) if (!hdev->driver) { id = hid_match_device(hdev, hdrv); if (id == NULL) { - if (!((hdev->quirks & HID_QUIRK_MULTITOUCH) && - !strncmp(hdrv->name, "hid-multitouch", 14))) { - ret = -ENODEV; - goto unlock; - } + ret = -ENODEV; + goto unlock; } hdev->driver = hdrv; if (hdrv->probe) { ret = hdrv->probe(hdev, id); } else { /* default probe */ - ret = hid_parse(hdev); + ret = hid_open_report(hdev); if (!ret) ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); } - if (ret) + if (ret) { + hid_close_report(hdev); hdev->driver = NULL; + } } unlock: up(&hdev->driver_lock); @@ -1744,6 +1805,7 @@ static int hid_device_remove(struct device *dev) hdrv->remove(hdev); else /* default remove */ hid_hw_stop(hdev); + hid_close_report(hdev); hdev->driver = NULL; } @@ -1751,6 +1813,23 @@ static int hid_device_remove(struct device *dev) return 0; } +static ssize_t modalias_show(struct device *dev, struct device_attribute *a, + char *buf) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + int len; + + len = snprintf(buf, PAGE_SIZE, "hid:b%04Xg%04Xv%08Xp%08X\n", + hdev->bus, hdev->group, hdev->vendor, hdev->product); + + return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; +} + +static struct device_attribute hid_dev_attrs[] = { + __ATTR_RO(modalias), + __ATTR_NULL, +}; + static int hid_uevent(struct device *dev, struct kobj_uevent_env *env) { struct hid_device *hdev = container_of(dev, struct hid_device, dev); @@ -1768,8 +1847,8 @@ static int hid_uevent(struct device *dev, struct kobj_uevent_env *env) if (add_uevent_var(env, "HID_UNIQ=%s", hdev->uniq)) return -ENOMEM; - if (add_uevent_var(env, "MODALIAS=hid:b%04Xv%08Xp%08X", - hdev->bus, hdev->vendor, hdev->product)) + if (add_uevent_var(env, "MODALIAS=hid:b%04Xg%04Xv%08Xp%08X", + hdev->bus, hdev->group, hdev->vendor, hdev->product)) return -ENOMEM; return 0; @@ -1777,6 +1856,7 @@ static int hid_uevent(struct device *dev, struct kobj_uevent_env *env) static struct bus_type hid_bus_type = { .name = "hid", + .dev_attrs = hid_dev_attrs, .match = hid_bus_match, .probe = hid_device_probe, .remove = hid_device_remove, @@ -2075,6 +2155,26 @@ int hid_add_device(struct hid_device *hdev) && (hid_ignore(hdev) || (hdev->quirks & HID_QUIRK_IGNORE))) return -ENODEV; + /* + * Read the device report descriptor once and use as template + * for the driver-specific modifications. + */ + ret = hdev->ll_driver->parse(hdev); + if (ret) + return ret; + if (!hdev->dev_rdesc) + return -ENODEV; + + /* + * Scan generic devices for group information + */ + if (hid_ignore_special_drivers || + !hid_match_id(hdev, hid_have_special_driver)) { + ret = hid_scan_report(hdev); + if (ret) + hid_warn(hdev, "bad device descriptor (%d)\n", ret); + } + /* XXX hack, any other cleaner solution after the driver core * is converted to allow more than 20 bytes as the device name? */ dev_set_name(&hdev->dev, "%04X:%04X:%04X.%04X", hdev->bus, @@ -2103,7 +2203,6 @@ EXPORT_SYMBOL_GPL(hid_add_device); struct hid_device *hid_allocate_device(void) { struct hid_device *hdev; - unsigned int i; int ret = -ENOMEM; hdev = kzalloc(sizeof(*hdev), GFP_KERNEL); @@ -2114,23 +2213,13 @@ struct hid_device *hid_allocate_device(void) hdev->dev.release = hid_device_release; hdev->dev.bus = &hid_bus_type; - hdev->collection = kcalloc(HID_DEFAULT_NUM_COLLECTIONS, - sizeof(struct hid_collection), GFP_KERNEL); - if (hdev->collection == NULL) - goto err; - hdev->collection_size = HID_DEFAULT_NUM_COLLECTIONS; - - for (i = 0; i < HID_REPORT_TYPES; i++) - INIT_LIST_HEAD(&hdev->report_enum[i].report_list); + hid_close_report(hdev); init_waitqueue_head(&hdev->debug_wait); INIT_LIST_HEAD(&hdev->debug_list); sema_init(&hdev->driver_lock, 1); return hdev; -err: - put_device(&hdev->dev); - return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(hid_allocate_device); @@ -2141,6 +2230,9 @@ static void hid_remove_device(struct hid_device *hdev) hid_debug_unregister(hdev); hdev->status &= ~HID_STAT_ADDED; } + kfree(hdev->dev_rdesc); + hdev->dev_rdesc = NULL; + hdev->dev_rsize = 0; } /** diff --git a/drivers/hid/hid-generic.c b/drivers/hid/hid-generic.c new file mode 100644 index 000000000000..a8b3148e03a2 --- /dev/null +++ b/drivers/hid/hid-generic.c @@ -0,0 +1,53 @@ +/* + * HID support for Linux + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> + * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc + * Copyright (c) 2007-2008 Oliver Neukum + * Copyright (c) 2006-2012 Jiri Kosina + * Copyright (c) 2012 Henrik Rydberg + */ + +/* + * 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/module.h> +#include <linux/slab.h> +#include <linux/kernel.h> +#include <asm/unaligned.h> +#include <asm/byteorder.h> + +#include <linux/hid.h> + +static const struct hid_device_id hid_table[] = { + { HID_DEVICE(HID_BUS_ANY, HID_GROUP_GENERIC, HID_ANY_ID, HID_ANY_ID) }, + { } +}; +MODULE_DEVICE_TABLE(hid, hid_table); + +static struct hid_driver hid_generic = { + .name = "hid-generic", + .id_table = hid_table, +}; + +static int __init hid_init(void) +{ + return hid_register_driver(&hid_generic); +} + +static void __exit hid_exit(void) +{ + hid_unregister_driver(&hid_generic); +} + +module_init(hid_init); +module_exit(hid_exit); + +MODULE_AUTHOR("Henrik Rydberg"); +MODULE_DESCRIPTION("HID generic driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c index 406632472c1b..3d62781b8993 100644 --- a/drivers/hid/hid-hyperv.c +++ b/drivers/hid/hid-hyperv.c @@ -430,6 +430,15 @@ cleanup: return ret; } +static int mousevsc_hid_parse(struct hid_device *hid) +{ + struct hv_device *dev = hid_get_drvdata(hid); + struct mousevsc_dev *input_dev = hv_get_drvdata(dev); + + return hid_parse_report(hid, input_dev->report_desc, + input_dev->report_desc_size); +} + static int mousevsc_hid_open(struct hid_device *hid) { return 0; @@ -449,6 +458,7 @@ static void mousevsc_hid_stop(struct hid_device *hid) } static struct hid_ll_driver mousevsc_ll_driver = { + .parse = mousevsc_hid_parse, .open = mousevsc_hid_open, .close = mousevsc_hid_close, .start = mousevsc_hid_start, @@ -506,13 +516,14 @@ static int mousevsc_probe(struct hv_device *device, sprintf(hid_dev->name, "%s", "Microsoft Vmbus HID-compliant Mouse"); + hid_set_drvdata(hid_dev, device); + ret = hid_add_device(hid_dev); if (ret) goto probe_err1; - ret = hid_parse_report(hid_dev, input_dev->report_desc, - input_dev->report_desc_size); + ret = hid_parse(hid_dev); if (ret) { hid_err(hid_dev, "parse failed\n"); goto probe_err2; diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index e39aecb1f9f2..9373f535dfe9 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -154,9 +154,15 @@ #define USB_DEVICE_ID_ATMEL_MULTITOUCH 0x211c #define USB_DEVICE_ID_ATMEL_MXT_DIGITIZER 0x2118 +#define USB_VENDOR_ID_AUREAL 0x0755 +#define USB_DEVICE_ID_AUREAL_W01RN 0x2626 + #define USB_VENDOR_ID_AVERMEDIA 0x07ca #define USB_DEVICE_ID_AVER_FM_MR800 0xb800 +#define USB_VENDOR_ID_BAANTO 0x2453 +#define USB_DEVICE_ID_BAANTO_MT_190W2 0x0100 + #define USB_VENDOR_ID_BELKIN 0x050d #define USB_DEVICE_ID_FLIP_KVM 0x3201 @@ -726,6 +732,7 @@ #define USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U 0x0004 #define USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U 0x0005 #define USB_DEVICE_ID_UCLOGIC_TABLET_WP1062 0x0064 +#define USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850 0x0522 #define USB_VENDOR_ID_UNITEC 0x227d #define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709 0x0709 @@ -749,6 +756,7 @@ #define USB_DEVICE_ID_WALTOP_PID_0038 0x0038 #define USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH 0x0501 #define USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH 0x0500 +#define USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET 0x0502 #define USB_VENDOR_ID_WISEGROUP 0x0925 #define USB_DEVICE_ID_SMARTJOY_PLUS 0x0005 diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 002781c5a616..132b0019365e 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -225,7 +225,10 @@ static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code) * Verify and convert units. * See HID specification v1.11 6.2.2.7 Global Items for unit decoding */ - if (code == ABS_X || code == ABS_Y || code == ABS_Z) { + switch (code) { + case ABS_X: + case ABS_Y: + case ABS_Z: if (field->unit == 0x11) { /* If centimeters */ /* Convert to millimeters */ unit_exponent += 1; @@ -239,7 +242,13 @@ static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code) } else { return 0; } - } else if (code == ABS_RX || code == ABS_RY || code == ABS_RZ) { + break; + + case ABS_RX: + case ABS_RY: + case ABS_RZ: + case ABS_TILT_X: + case ABS_TILT_Y: if (field->unit == 0x14) { /* If degrees */ /* Convert to radians */ prev = logical_extents; @@ -250,7 +259,9 @@ static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code) } else if (field->unit != 0x12) { /* If not radians */ return 0; } - } else { + break; + + default: return 0; } @@ -623,6 +634,14 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel map_key_clear(BTN_TOOL_RUBBER); break; + case 0x3d: /* X Tilt */ + map_abs_clear(ABS_TILT_X); + break; + + case 0x3e: /* Y Tilt */ + map_abs_clear(ABS_TILT_Y); + break; + case 0x33: /* Touch */ case 0x42: /* TipSwitch */ case 0x43: /* TipSwitch2 */ @@ -638,10 +657,6 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel map_key_clear(BTN_STYLUS2); break; - case 0x51: /* ContactID */ - device->quirks |= HID_QUIRK_MULTITOUCH; - goto unknown; - default: goto unknown; } break; @@ -1208,13 +1223,6 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) } } - if (hid->quirks & HID_QUIRK_MULTITOUCH) { - /* generic hid does not know how to handle multitouch devices */ - if (hidinput) - goto out_cleanup; - goto out_unwind; - } - if (hidinput && input_register_device(hidinput->input)) goto out_cleanup; diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index e7a7bd1eb34a..fc37ed6b108c 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -109,23 +109,23 @@ static __u8 dfp_rdesc_fixed[] = { static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { - unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev); - if ((quirks & LG_RDESC) && *rsize >= 90 && rdesc[83] == 0x26 && + if ((drv_data->quirks & LG_RDESC) && *rsize >= 90 && rdesc[83] == 0x26 && rdesc[84] == 0x8c && rdesc[85] == 0x02) { hid_info(hdev, "fixing up Logitech keyboard report descriptor\n"); rdesc[84] = rdesc[89] = 0x4d; rdesc[85] = rdesc[90] = 0x10; } - if ((quirks & LG_RDESC_REL_ABS) && *rsize >= 50 && + if ((drv_data->quirks & LG_RDESC_REL_ABS) && *rsize >= 50 && rdesc[32] == 0x81 && rdesc[33] == 0x06 && rdesc[49] == 0x81 && rdesc[50] == 0x06) { hid_info(hdev, "fixing up rel/abs in Logitech report descriptor\n"); rdesc[33] = rdesc[50] = 0x02; } - if ((quirks & LG_FF4) && *rsize >= 101 && + if ((drv_data->quirks & LG_FF4) && *rsize >= 101 && rdesc[41] == 0x95 && rdesc[42] == 0x0B && rdesc[47] == 0x05 && rdesc[48] == 0x09) { hid_info(hdev, "fixing up Logitech Speed Force Wireless button descriptor\n"); @@ -278,7 +278,7 @@ static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi, 0, 0, 0, 0, 0,183,184,185,186,187, 188,189,190,191,192,193,194, 0, 0, 0 }; - unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev); unsigned int hid = usage->hid; if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER && @@ -289,7 +289,7 @@ static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi, lg_dinovo_mapping(hi, usage, bit, max)) return 1; - if ((quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max)) + if ((drv_data->quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max)) return 1; if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON) @@ -299,11 +299,11 @@ static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi, /* Special handling for Logitech Cordless Desktop */ if (field->application == HID_GD_MOUSE) { - if ((quirks & LG_IGNORE_DOUBLED_WHEEL) && + if ((drv_data->quirks & LG_IGNORE_DOUBLED_WHEEL) && (hid == 7 || hid == 8)) return -1; } else { - if ((quirks & LG_EXPANDED_KEYMAP) && + if ((drv_data->quirks & LG_EXPANDED_KEYMAP) && hid < ARRAY_SIZE(e_keymap) && e_keymap[hid] != 0) { hid_map_usage(hi, usage, bit, max, EV_KEY, @@ -319,13 +319,13 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) { - unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev); - if ((quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY && + if ((drv_data->quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY && (field->flags & HID_MAIN_ITEM_RELATIVE)) field->flags &= ~HID_MAIN_ITEM_RELATIVE; - if ((quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY || + if ((drv_data->quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY || usage->type == EV_REL || usage->type == EV_ABS)) clear_bit(usage->code, *bit); @@ -335,9 +335,9 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi, static int lg_event(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value) { - unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); + struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev); - if ((quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) { + if ((drv_data->quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) { input_event(field->hidinput->input, usage->type, usage->code, -value); return 1; @@ -348,13 +348,20 @@ static int lg_event(struct hid_device *hdev, struct hid_field *field, static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) { - unsigned long quirks = id->driver_data; unsigned int connect_mask = HID_CONNECT_DEFAULT; + struct lg_drv_data *drv_data; int ret; - hid_set_drvdata(hdev, (void *)quirks); + drv_data = kzalloc(sizeof(struct lg_drv_data), GFP_KERNEL); + if (!drv_data) { + hid_err(hdev, "Insufficient memory, cannot allocate driver data\n"); + return -ENOMEM; + } + drv_data->quirks = id->driver_data; + + hid_set_drvdata(hdev, (void *)drv_data); - if (quirks & LG_NOGET) + if (drv_data->quirks & LG_NOGET) hdev->quirks |= HID_QUIRK_NOGET; ret = hid_parse(hdev); @@ -363,7 +370,7 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_free; } - if (quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4)) + if (drv_data->quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4)) connect_mask &= ~HID_CONNECT_FF; ret = hid_hw_start(hdev, connect_mask); @@ -392,27 +399,29 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) } } - if (quirks & LG_FF) + if (drv_data->quirks & LG_FF) lgff_init(hdev); - if (quirks & LG_FF2) + if (drv_data->quirks & LG_FF2) lg2ff_init(hdev); - if (quirks & LG_FF3) + if (drv_data->quirks & LG_FF3) lg3ff_init(hdev); - if (quirks & LG_FF4) + if (drv_data->quirks & LG_FF4) lg4ff_init(hdev); return 0; err_free: + kfree(drv_data); return ret; } static void lg_remove(struct hid_device *hdev) { - unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); - if(quirks & LG_FF4) + struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev); + if (drv_data->quirks & LG_FF4) lg4ff_deinit(hdev); hid_hw_stop(hdev); + kfree(drv_data); } static const struct hid_device_id lg_devices[] = { diff --git a/drivers/hid/hid-lg.h b/drivers/hid/hid-lg.h index 4b097286dc78..d64cf8d2751e 100644 --- a/drivers/hid/hid-lg.h +++ b/drivers/hid/hid-lg.h @@ -1,6 +1,11 @@ #ifndef __HID_LG_H #define __HID_LG_H +struct lg_drv_data { + unsigned long quirks; + void *device_props; /* Device specific properties */ +}; + #ifdef CONFIG_LOGITECH_FF int lgff_init(struct hid_device *hdev); #else diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index 6ecc9e220440..f3390ee6105c 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -1,7 +1,8 @@ /* - * Force feedback support for Logitech Speed Force Wireless + * Force feedback support for Logitech Gaming Wheels * - * http://wiibrew.org/wiki/Logitech_USB_steering_wheel + * Including G27, G25, DFP, DFGT, FFEX, Momo, Momo2 & + * Speed Force Wireless (WiiWheel) * * Copyright (c) 2010 Simon Wood <simon@mungewell.org> */ @@ -51,20 +52,18 @@ static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *at static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IRWXO, lg4ff_range_show, lg4ff_range_store); -static bool list_inited; - struct lg4ff_device_entry { - char *device_id; /* Use name in respective kobject structure's address as the ID */ __u16 range; __u16 min_range; __u16 max_range; - __u8 leds; +#ifdef CONFIG_LEDS_CLASS + __u8 led_state; + struct led_classdev *led[5]; +#endif struct list_head list; void (*set_range)(struct hid_device *hid, u16 range); }; -static struct lg4ff_device_entry device_list; - static const signed short lg4ff_wheel_effects[] = { FF_CONSTANT, FF_AUTOCENTER, @@ -285,18 +284,20 @@ static void hid_lg4ff_switch_native(struct hid_device *hid, const struct lg4ff_n /* Read current range and display it in terminal */ static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct lg4ff_device_entry *uninitialized_var(entry); - struct list_head *h; struct hid_device *hid = to_hid_device(dev); + struct lg4ff_device_entry *entry; + struct lg_drv_data *drv_data; size_t count; - list_for_each(h, &device_list.list) { - entry = list_entry(h, struct lg4ff_device_entry, list); - if (strcmp(entry->device_id, (&hid->dev)->kobj.name) == 0) - break; + drv_data = hid_get_drvdata(hid); + if (!drv_data) { + hid_err(hid, "Private driver data not found!\n"); + return 0; } - if (h == &device_list.list) { - dbg_hid("Device not found!"); + + entry = drv_data->device_props; + if (!entry) { + hid_err(hid, "Device properties not found!\n"); return 0; } @@ -308,19 +309,21 @@ static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *att * according to the type of the wheel */ static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct lg4ff_device_entry *uninitialized_var(entry); - struct list_head *h; struct hid_device *hid = to_hid_device(dev); + struct lg4ff_device_entry *entry; + struct lg_drv_data *drv_data; __u16 range = simple_strtoul(buf, NULL, 10); - list_for_each(h, &device_list.list) { - entry = list_entry(h, struct lg4ff_device_entry, list); - if (strcmp(entry->device_id, (&hid->dev)->kobj.name) == 0) - break; + drv_data = hid_get_drvdata(hid); + if (!drv_data) { + hid_err(hid, "Private driver data not found!\n"); + return 0; } - if (h == &device_list.list) { - dbg_hid("Device not found!"); - return count; + + entry = drv_data->device_props; + if (!entry) { + hid_err(hid, "Device properties not found!\n"); + return 0; } if (range == 0) @@ -336,6 +339,88 @@ static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *at return count; } +#ifdef CONFIG_LEDS_CLASS +static void lg4ff_set_leds(struct hid_device *hid, __u8 leds) +{ + struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct hid_report *report = list_entry(report_list->next, struct hid_report, list); + + report->field[0]->value[0] = 0xf8; + report->field[0]->value[1] = 0x12; + report->field[0]->value[2] = leds; + report->field[0]->value[3] = 0x00; + report->field[0]->value[4] = 0x00; + report->field[0]->value[5] = 0x00; + report->field[0]->value[6] = 0x00; + usbhid_submit_report(hid, report, USB_DIR_OUT); +} + +static void lg4ff_led_set_brightness(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct device *dev = led_cdev->dev->parent; + struct hid_device *hid = container_of(dev, struct hid_device, dev); + struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hid); + struct lg4ff_device_entry *entry; + int i, state = 0; + + if (!drv_data) { + hid_err(hid, "Device data not found."); + return; + } + + entry = (struct lg4ff_device_entry *)drv_data->device_props; + + if (!entry) { + hid_err(hid, "Device properties not found."); + return; + } + + for (i = 0; i < 5; i++) { + if (led_cdev != entry->led[i]) + continue; + state = (entry->led_state >> i) & 1; + if (value == LED_OFF && state) { + entry->led_state &= ~(1 << i); + lg4ff_set_leds(hid, entry->led_state); + } else if (value != LED_OFF && !state) { + entry->led_state |= 1 << i; + lg4ff_set_leds(hid, entry->led_state); + } + break; + } +} + +static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cdev) +{ + struct device *dev = led_cdev->dev->parent; + struct hid_device *hid = container_of(dev, struct hid_device, dev); + struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hid); + struct lg4ff_device_entry *entry; + int i, value = 0; + + if (!drv_data) { + hid_err(hid, "Device data not found."); + return LED_OFF; + } + + entry = (struct lg4ff_device_entry *)drv_data->device_props; + + if (!entry) { + hid_err(hid, "Device properties not found."); + return LED_OFF; + } + + for (i = 0; i < 5; i++) + if (led_cdev == entry->led[i]) { + value = (entry->led_state >> i) & 1; + break; + } + + return value ? LED_FULL : LED_OFF; +} +#endif + int lg4ff_init(struct hid_device *hid) { struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); @@ -344,6 +429,7 @@ int lg4ff_init(struct hid_device *hid) struct hid_report *report; struct hid_field *field; struct lg4ff_device_entry *entry; + struct lg_drv_data *drv_data; struct usb_device_descriptor *udesc; int error, i, j; __u16 bcdDevice, rev_maj, rev_min; @@ -423,28 +509,24 @@ int lg4ff_init(struct hid_device *hid) dev->ff->set_autocenter(dev, 0); } - /* Initialize device_list if this is the first device to handle by lg4ff */ - if (!list_inited) { - INIT_LIST_HEAD(&device_list.list); - list_inited = 1; + /* Get private driver data */ + drv_data = hid_get_drvdata(hid); + if (!drv_data) { + hid_err(hid, "Cannot add device, private driver data not allocated\n"); + return -1; } - /* Add the device to device_list */ + /* Initialize device properties */ entry = kzalloc(sizeof(struct lg4ff_device_entry), GFP_KERNEL); if (!entry) { - hid_err(hid, "Cannot add device, insufficient memory.\n"); - return -ENOMEM; - } - entry->device_id = kstrdup((&hid->dev)->kobj.name, GFP_KERNEL); - if (!entry->device_id) { - hid_err(hid, "Cannot set device_id, insufficient memory.\n"); - kfree(entry); + hid_err(hid, "Cannot add device, insufficient memory to allocate device properties.\n"); return -ENOMEM; } + drv_data->device_props = entry; + entry->min_range = lg4ff_devices[i].min_range; entry->max_range = lg4ff_devices[i].max_range; entry->set_range = lg4ff_devices[i].set_range; - list_add(&entry->list, &device_list.list); /* Create sysfs interface */ error = device_create_file(&hid->dev, &dev_attr_range); @@ -457,32 +539,100 @@ int lg4ff_init(struct hid_device *hid) if (entry->set_range != NULL) entry->set_range(hid, entry->range); - hid_info(hid, "Force feedback for Logitech Speed Force Wireless by Simon Wood <simon@mungewell.org>\n"); +#ifdef CONFIG_LEDS_CLASS + /* register led subsystem - G27 only */ + entry->led_state = 0; + for (j = 0; j < 5; j++) + entry->led[j] = NULL; + + if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_G27_WHEEL) { + struct led_classdev *led; + size_t name_sz; + char *name; + + lg4ff_set_leds(hid, 0); + + name_sz = strlen(dev_name(&hid->dev)) + 8; + + for (j = 0; j < 5; j++) { + led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL); + if (!led) { + hid_err(hid, "can't allocate memory for LED %d\n", j); + goto err; + } + + name = (void *)(&led[1]); + snprintf(name, name_sz, "%s::RPM%d", dev_name(&hid->dev), j+1); + led->name = name; + led->brightness = 0; + led->max_brightness = 1; + led->brightness_get = lg4ff_led_get_brightness; + led->brightness_set = lg4ff_led_set_brightness; + + entry->led[j] = led; + error = led_classdev_register(&hid->dev, led); + + if (error) { + hid_err(hid, "failed to register LED %d. Aborting.\n", j); +err: + /* Deregister LEDs (if any) */ + for (j = 0; j < 5; j++) { + led = entry->led[j]; + entry->led[j] = NULL; + if (!led) + continue; + led_classdev_unregister(led); + kfree(led); + } + goto out; /* Let the driver continue without LEDs */ + } + } + } +out: +#endif + hid_info(hid, "Force feedback support for Logitech Gaming Wheels\n"); return 0; } int lg4ff_deinit(struct hid_device *hid) { - bool found = 0; struct lg4ff_device_entry *entry; - struct list_head *h, *g; - list_for_each_safe(h, g, &device_list.list) { - entry = list_entry(h, struct lg4ff_device_entry, list); - if (strcmp(entry->device_id, (&hid->dev)->kobj.name) == 0) { - list_del(h); - kfree(entry->device_id); - kfree(entry); - found = 1; - break; - } - } + struct lg_drv_data *drv_data; + + device_remove_file(&hid->dev, &dev_attr_range); - if (!found) { - dbg_hid("Device entry not found!\n"); + drv_data = hid_get_drvdata(hid); + if (!drv_data) { + hid_err(hid, "Error while deinitializing device, no private driver data.\n"); + return -1; + } + entry = drv_data->device_props; + if (!entry) { + hid_err(hid, "Error while deinitializing device, no device properties data.\n"); return -1; } - device_remove_file(&hid->dev, &dev_attr_range); +#ifdef CONFIG_LEDS_CLASS + { + int j; + struct led_classdev *led; + + /* Deregister LEDs (if any) */ + for (j = 0; j < 5; j++) { + + led = entry->led[j]; + entry->led[j] = NULL; + if (!led) + continue; + led_classdev_unregister(led); + kfree(led); + } + } +#endif + + /* Deallocate memory */ + kfree(entry); + dbg_hid("Device successfully unregistered\n"); return 0; } diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 2b56efcbdf61..5e8a7ed42344 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -26,6 +26,7 @@ #include <linux/hid.h> #include <linux/module.h> #include <linux/usb.h> +#include <asm/unaligned.h> #include "usbhid/usbhid.h" #include "hid-ids.h" #include "hid-logitech-dj.h" @@ -155,6 +156,14 @@ static const char media_descriptor[] = { /* Maximum size of all defined hid reports in bytes (including report id) */ #define MAX_REPORT_SIZE 8 +/* Make sure all descriptors are present here */ +#define MAX_RDESC_SIZE \ + (sizeof(kbd_descriptor) + \ + sizeof(mse_descriptor) + \ + sizeof(consumer_descriptor) + \ + sizeof(syscontrol_descriptor) + \ + sizeof(media_descriptor)) + /* Number of possible hid report types that can be created by this driver. * * Right now, RF report types have the same report types (or report id's) @@ -265,8 +274,8 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev, goto dj_device_allocate_fail; } - dj_dev->reports_supported = le32_to_cpu( - dj_report->report_params[DEVICE_PAIRED_RF_REPORT_TYPE]); + dj_dev->reports_supported = get_unaligned_le32( + dj_report->report_params + DEVICE_PAIRED_RF_REPORT_TYPE); dj_dev->hdev = dj_hiddev; dj_dev->dj_receiver_dev = djrcv_dev; dj_dev->device_index = dj_report->device_index; @@ -473,9 +482,17 @@ static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf, return 0; } +static void rdcat(char **rdesc, unsigned int *rsize, const char *data, unsigned int size) +{ + memcpy(*rdesc + *rsize, data, size); + *rsize += size; +} + static int logi_dj_ll_parse(struct hid_device *hid) { struct dj_device *djdev = hid->driver_data; + unsigned int rsize = 0; + char *rdesc; int retval; dbg_hid("%s\n", __func__); @@ -483,70 +500,38 @@ static int logi_dj_ll_parse(struct hid_device *hid) djdev->hdev->version = 0x0111; djdev->hdev->country = 0x00; + rdesc = kmalloc(MAX_RDESC_SIZE, GFP_KERNEL); + if (!rdesc) + return -ENOMEM; + if (djdev->reports_supported & STD_KEYBOARD) { dbg_hid("%s: sending a kbd descriptor, reports_supported: %x\n", __func__, djdev->reports_supported); - retval = hid_parse_report(hid, - (u8 *) kbd_descriptor, - sizeof(kbd_descriptor)); - if (retval) { - dbg_hid("%s: sending a kbd descriptor, hid_parse failed" - " error: %d\n", __func__, retval); - return retval; - } + rdcat(&rdesc, &rsize, kbd_descriptor, sizeof(kbd_descriptor)); } if (djdev->reports_supported & STD_MOUSE) { dbg_hid("%s: sending a mouse descriptor, reports_supported: " "%x\n", __func__, djdev->reports_supported); - retval = hid_parse_report(hid, - (u8 *) mse_descriptor, - sizeof(mse_descriptor)); - if (retval) { - dbg_hid("%s: sending a mouse descriptor, hid_parse " - "failed error: %d\n", __func__, retval); - return retval; - } + rdcat(&rdesc, &rsize, mse_descriptor, sizeof(mse_descriptor)); } if (djdev->reports_supported & MULTIMEDIA) { dbg_hid("%s: sending a multimedia report descriptor: %x\n", __func__, djdev->reports_supported); - retval = hid_parse_report(hid, - (u8 *) consumer_descriptor, - sizeof(consumer_descriptor)); - if (retval) { - dbg_hid("%s: sending a consumer_descriptor, hid_parse " - "failed error: %d\n", __func__, retval); - return retval; - } + rdcat(&rdesc, &rsize, consumer_descriptor, sizeof(consumer_descriptor)); } if (djdev->reports_supported & POWER_KEYS) { dbg_hid("%s: sending a power keys report descriptor: %x\n", __func__, djdev->reports_supported); - retval = hid_parse_report(hid, - (u8 *) syscontrol_descriptor, - sizeof(syscontrol_descriptor)); - if (retval) { - dbg_hid("%s: sending a syscontrol_descriptor, " - "hid_parse failed error: %d\n", - __func__, retval); - return retval; - } + rdcat(&rdesc, &rsize, syscontrol_descriptor, sizeof(syscontrol_descriptor)); } if (djdev->reports_supported & MEDIA_CENTER) { dbg_hid("%s: sending a media center report descriptor: %x\n", __func__, djdev->reports_supported); - retval = hid_parse_report(hid, - (u8 *) media_descriptor, - sizeof(media_descriptor)); - if (retval) { - dbg_hid("%s: sending a media_descriptor, hid_parse " - "failed error: %d\n", __func__, retval); - return retval; - } + rdcat(&rdesc, &rsize, media_descriptor, sizeof(media_descriptor)); } if (djdev->reports_supported & KBD_LEDS) { @@ -554,7 +539,10 @@ static int logi_dj_ll_parse(struct hid_device *hid) __func__, djdev->reports_supported); } - return 0; + retval = hid_parse_report(hid, rdesc, rsize); + kfree(rdesc); + + return retval; } static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type, diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 1d5b94167b52..6e3332a99976 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -70,9 +70,16 @@ struct mt_class { bool is_indirect; /* true for touchpads */ }; +struct mt_fields { + unsigned usages[HID_MAX_FIELDS]; + unsigned int length; +}; + struct mt_device { struct mt_slot curdata; /* placeholder of incoming data */ struct mt_class mtclass; /* our mt device class */ + struct mt_fields *fields; /* temporary placeholder for storing the + multitouch fields */ unsigned last_field_index; /* last field index of the report */ unsigned last_slot_field; /* the last field of a slot */ __s8 inputmode; /* InputMode HID feature, -1 if non-existent */ @@ -110,6 +117,9 @@ struct mt_device { #define MT_DEFAULT_MAXCONTACT 10 +#define MT_USB_DEVICE(v, p) HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH, v, p) +#define MT_BT_DEVICE(v, p) HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_MULTITOUCH, v, p) + /* * these device-dependent functions determine what slot corresponds * to a valid contact that was just read. @@ -275,11 +285,15 @@ static void set_abs(struct input_dev *input, unsigned int code, input_set_abs_params(input, code, fmin, fmax, fuzz, 0); } -static void set_last_slot_field(struct hid_usage *usage, struct mt_device *td, +static void mt_store_field(struct hid_usage *usage, struct mt_device *td, struct hid_input *hi) { - if (!test_bit(usage->hid, hi->input->absbit)) - td->last_slot_field = usage->hid; + struct mt_fields *f = td->fields; + + if (f->length >= HID_MAX_FIELDS) + return; + + f->usages[f->length++] = usage->hid; } static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, @@ -330,7 +344,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, cls->sn_move); /* touchscreen emulation */ set_abs(hi->input, ABS_X, field, cls->sn_move); - set_last_slot_field(usage, td, hi); + mt_store_field(usage, td, hi); td->last_field_index = field->index; return 1; case HID_GD_Y: @@ -340,7 +354,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, cls->sn_move); /* touchscreen emulation */ set_abs(hi->input, ABS_Y, field, cls->sn_move); - set_last_slot_field(usage, td, hi); + mt_store_field(usage, td, hi); td->last_field_index = field->index; return 1; } @@ -349,24 +363,24 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_UP_DIGITIZER: switch (usage->hid) { case HID_DG_INRANGE: - set_last_slot_field(usage, td, hi); + mt_store_field(usage, td, hi); td->last_field_index = field->index; return 1; case HID_DG_CONFIDENCE: - set_last_slot_field(usage, td, hi); + mt_store_field(usage, td, hi); td->last_field_index = field->index; return 1; case HID_DG_TIPSWITCH: hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); input_set_capability(hi->input, EV_KEY, BTN_TOUCH); - set_last_slot_field(usage, td, hi); + mt_store_field(usage, td, hi); td->last_field_index = field->index; return 1; case HID_DG_CONTACTID: if (!td->maxcontacts) td->maxcontacts = MT_DEFAULT_MAXCONTACT; input_mt_init_slots(hi->input, td->maxcontacts); - td->last_slot_field = usage->hid; + mt_store_field(usage, td, hi); td->last_field_index = field->index; td->touches_by_report++; return 1; @@ -375,7 +389,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, EV_ABS, ABS_MT_TOUCH_MAJOR); set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field, cls->sn_width); - set_last_slot_field(usage, td, hi); + mt_store_field(usage, td, hi); td->last_field_index = field->index; return 1; case HID_DG_HEIGHT: @@ -385,7 +399,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, cls->sn_height); input_set_abs_params(hi->input, ABS_MT_ORIENTATION, 0, 1, 0, 0); - set_last_slot_field(usage, td, hi); + mt_store_field(usage, td, hi); td->last_field_index = field->index; return 1; case HID_DG_TIPPRESSURE: @@ -396,7 +410,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, /* touchscreen emulation */ set_abs(hi->input, ABS_PRESSURE, field, cls->sn_pressure); - set_last_slot_field(usage, td, hi); + mt_store_field(usage, td, hi); td->last_field_index = field->index; return 1; case HID_DG_CONTACTCOUNT: @@ -635,6 +649,31 @@ static void mt_set_maxcontacts(struct hid_device *hdev) } } +static void mt_post_parse_default_settings(struct mt_device *td) +{ + __s32 quirks = td->mtclass.quirks; + + /* unknown serial device needs special quirks */ + if (td->touches_by_report == 1) { + quirks |= MT_QUIRK_ALWAYS_VALID; + quirks &= ~MT_QUIRK_NOT_SEEN_MEANS_UP; + quirks &= ~MT_QUIRK_VALID_IS_INRANGE; + quirks &= ~MT_QUIRK_VALID_IS_CONFIDENCE; + } + + td->mtclass.quirks = quirks; +} + +static void mt_post_parse(struct mt_device *td) +{ + struct mt_fields *f = td->fields; + + if (td->touches_by_report > 0) { + int field_count_per_touch = f->length / td->touches_by_report; + td->last_slot_field = f->usages[field_count_per_touch - 1]; + } +} + static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret, i; @@ -654,7 +693,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) * that emit events over several HID messages. */ hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC; - hdev->quirks &= ~HID_QUIRK_MULTITOUCH; td = kzalloc(sizeof(struct mt_device), GFP_KERNEL); if (!td) { @@ -666,6 +704,13 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) td->maxcontact_report_id = -1; hid_set_drvdata(hdev, td); + td->fields = kzalloc(sizeof(struct mt_fields), GFP_KERNEL); + if (!td->fields) { + dev_err(&hdev->dev, "cannot allocate multitouch fields data\n"); + ret = -ENOMEM; + goto fail; + } + ret = hid_parse(hdev); if (ret != 0) goto fail; @@ -674,14 +719,10 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) if (ret) goto fail; - if (!id && td->touches_by_report == 1) { - /* the device has been sent by hid-generic */ - mtclass = &td->mtclass; - mtclass->quirks |= MT_QUIRK_ALWAYS_VALID; - mtclass->quirks &= ~MT_QUIRK_NOT_SEEN_MEANS_UP; - mtclass->quirks &= ~MT_QUIRK_VALID_IS_INRANGE; - mtclass->quirks &= ~MT_QUIRK_VALID_IS_CONFIDENCE; - } + mt_post_parse(td); + + if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID) + mt_post_parse_default_settings(td); td->slots = kzalloc(td->maxcontacts * sizeof(struct mt_slot), GFP_KERNEL); @@ -697,9 +738,13 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) mt_set_maxcontacts(hdev); mt_set_input_mode(hdev); + kfree(td->fields); + td->fields = NULL; + return 0; fail: + kfree(td->fields); kfree(td); return ret; } @@ -727,50 +772,54 @@ static const struct hid_device_id mt_devices[] = { /* 3M panels */ { .driver_data = MT_CLS_3M, - HID_USB_DEVICE(USB_VENDOR_ID_3M, + MT_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) }, { .driver_data = MT_CLS_3M, - HID_USB_DEVICE(USB_VENDOR_ID_3M, + MT_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M2256) }, { .driver_data = MT_CLS_3M, - HID_USB_DEVICE(USB_VENDOR_ID_3M, + MT_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M3266) }, /* ActionStar panels */ { .driver_data = MT_CLS_DEFAULT, - HID_USB_DEVICE(USB_VENDOR_ID_ACTIONSTAR, + MT_USB_DEVICE(USB_VENDOR_ID_ACTIONSTAR, USB_DEVICE_ID_ACTIONSTAR_1011) }, /* Atmel panels */ { .driver_data = MT_CLS_SERIAL, - HID_USB_DEVICE(USB_VENDOR_ID_ATMEL, + MT_USB_DEVICE(USB_VENDOR_ID_ATMEL, USB_DEVICE_ID_ATMEL_MULTITOUCH) }, { .driver_data = MT_CLS_SERIAL, - HID_USB_DEVICE(USB_VENDOR_ID_ATMEL, + MT_USB_DEVICE(USB_VENDOR_ID_ATMEL, USB_DEVICE_ID_ATMEL_MXT_DIGITIZER) }, + /* Baanto multitouch devices */ + { .driver_data = MT_CLS_DEFAULT, + MT_USB_DEVICE(USB_VENDOR_ID_BAANTO, + USB_DEVICE_ID_BAANTO_MT_190W2) }, /* Cando panels */ { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, - HID_USB_DEVICE(USB_VENDOR_ID_CANDO, + MT_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_MULTI_TOUCH) }, { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, - HID_USB_DEVICE(USB_VENDOR_ID_CANDO, + MT_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_MULTI_TOUCH_10_1) }, { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, - HID_USB_DEVICE(USB_VENDOR_ID_CANDO, + MT_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_MULTI_TOUCH_11_6) }, { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, - HID_USB_DEVICE(USB_VENDOR_ID_CANDO, + MT_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_MULTI_TOUCH_15_6) }, /* Chunghwa Telecom touch panels */ { .driver_data = MT_CLS_DEFAULT, - HID_USB_DEVICE(USB_VENDOR_ID_CHUNGHWAT, + MT_USB_DEVICE(USB_VENDOR_ID_CHUNGHWAT, USB_DEVICE_ID_CHUNGHWAT_MULTITOUCH) }, /* CVTouch panels */ { .driver_data = MT_CLS_DEFAULT, - HID_USB_DEVICE(USB_VENDOR_ID_CVTOUCH, + MT_USB_DEVICE(USB_VENDOR_ID_CVTOUCH, USB_DEVICE_ID_CVTOUCH_SCREEN) }, /* Cypress panel */ @@ -780,225 +829,227 @@ static const struct hid_device_id mt_devices[] = { /* eGalax devices (resistive) */ { .driver_data = MT_CLS_EGALAX, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480D) }, { .driver_data = MT_CLS_EGALAX, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E) }, /* eGalax devices (capacitive) */ { .driver_data = MT_CLS_EGALAX, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C) }, { .driver_data = MT_CLS_EGALAX_SERIAL, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7207) }, { .driver_data = MT_CLS_EGALAX_SERIAL, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_725E) }, { .driver_data = MT_CLS_EGALAX_SERIAL, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224) }, { .driver_data = MT_CLS_EGALAX_SERIAL, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_722A) }, { .driver_data = MT_CLS_EGALAX, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B) }, { .driver_data = MT_CLS_EGALAX_SERIAL, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7262) }, { .driver_data = MT_CLS_EGALAX, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1) }, { .driver_data = MT_CLS_EGALAX_SERIAL, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72AA) }, { .driver_data = MT_CLS_EGALAX, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA) }, { .driver_data = MT_CLS_EGALAX, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302) }, { .driver_data = MT_CLS_EGALAX_SERIAL, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7349) }, { .driver_data = MT_CLS_EGALAX_SERIAL, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) }, /* Elo TouchSystems IntelliTouch Plus panel */ { .driver_data = MT_CLS_DUAL_NSMU_CONTACTID, - HID_USB_DEVICE(USB_VENDOR_ID_ELO, + MT_USB_DEVICE(USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2515) }, /* GeneralTouch panel */ { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, - HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, + MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS) }, /* Gametel game controller */ { .driver_data = MT_CLS_DEFAULT, - HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_FRUCTEL, + MT_BT_DEVICE(USB_VENDOR_ID_FRUCTEL, USB_DEVICE_ID_GAMETEL_MT_MODE) }, /* GoodTouch panels */ { .driver_data = MT_CLS_DEFAULT, - HID_USB_DEVICE(USB_VENDOR_ID_GOODTOUCH, + MT_USB_DEVICE(USB_VENDOR_ID_GOODTOUCH, USB_DEVICE_ID_GOODTOUCH_000f) }, /* Hanvon panels */ { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, - HID_USB_DEVICE(USB_VENDOR_ID_HANVON_ALT, + MT_USB_DEVICE(USB_VENDOR_ID_HANVON_ALT, USB_DEVICE_ID_HANVON_ALT_MULTITOUCH) }, /* Ideacom panel */ { .driver_data = MT_CLS_SERIAL, - HID_USB_DEVICE(USB_VENDOR_ID_IDEACOM, + MT_USB_DEVICE(USB_VENDOR_ID_IDEACOM, USB_DEVICE_ID_IDEACOM_IDC6650) }, { .driver_data = MT_CLS_SERIAL, - HID_USB_DEVICE(USB_VENDOR_ID_IDEACOM, + MT_USB_DEVICE(USB_VENDOR_ID_IDEACOM, USB_DEVICE_ID_IDEACOM_IDC6651) }, /* Ilitek dual touch panel */ { .driver_data = MT_CLS_DEFAULT, - HID_USB_DEVICE(USB_VENDOR_ID_ILITEK, + MT_USB_DEVICE(USB_VENDOR_ID_ILITEK, USB_DEVICE_ID_ILITEK_MULTITOUCH) }, /* IRTOUCH panels */ { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, - HID_USB_DEVICE(USB_VENDOR_ID_IRTOUCHSYSTEMS, + MT_USB_DEVICE(USB_VENDOR_ID_IRTOUCHSYSTEMS, USB_DEVICE_ID_IRTOUCH_INFRARED_USB) }, /* LG Display panels */ { .driver_data = MT_CLS_DEFAULT, - HID_USB_DEVICE(USB_VENDOR_ID_LG, + MT_USB_DEVICE(USB_VENDOR_ID_LG, USB_DEVICE_ID_LG_MULTITOUCH) }, /* Lumio panels */ { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, - HID_USB_DEVICE(USB_VENDOR_ID_LUMIO, + MT_USB_DEVICE(USB_VENDOR_ID_LUMIO, USB_DEVICE_ID_CRYSTALTOUCH) }, { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, - HID_USB_DEVICE(USB_VENDOR_ID_LUMIO, + MT_USB_DEVICE(USB_VENDOR_ID_LUMIO, USB_DEVICE_ID_CRYSTALTOUCH_DUAL) }, /* MosArt panels */ { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, - HID_USB_DEVICE(USB_VENDOR_ID_ASUS, + MT_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_T91MT)}, { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, - HID_USB_DEVICE(USB_VENDOR_ID_ASUS, + MT_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUSTEK_MULTITOUCH_YFO) }, { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, - HID_USB_DEVICE(USB_VENDOR_ID_TURBOX, + MT_USB_DEVICE(USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART) }, /* Panasonic panels */ { .driver_data = MT_CLS_PANASONIC, - HID_USB_DEVICE(USB_VENDOR_ID_PANASONIC, + MT_USB_DEVICE(USB_VENDOR_ID_PANASONIC, USB_DEVICE_ID_PANABOARD_UBT780) }, { .driver_data = MT_CLS_PANASONIC, - HID_USB_DEVICE(USB_VENDOR_ID_PANASONIC, + MT_USB_DEVICE(USB_VENDOR_ID_PANASONIC, USB_DEVICE_ID_PANABOARD_UBT880) }, /* PenMount panels */ { .driver_data = MT_CLS_CONFIDENCE, - HID_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, + MT_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_PCI) }, /* PixArt optical touch screen */ { .driver_data = MT_CLS_INRANGE_CONTACTNUMBER, - HID_USB_DEVICE(USB_VENDOR_ID_PIXART, + MT_USB_DEVICE(USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN) }, { .driver_data = MT_CLS_INRANGE_CONTACTNUMBER, - HID_USB_DEVICE(USB_VENDOR_ID_PIXART, + MT_USB_DEVICE(USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1) }, { .driver_data = MT_CLS_INRANGE_CONTACTNUMBER, - HID_USB_DEVICE(USB_VENDOR_ID_PIXART, + MT_USB_DEVICE(USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2) }, /* PixCir-based panels */ { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, - HID_USB_DEVICE(USB_VENDOR_ID_HANVON, + MT_USB_DEVICE(USB_VENDOR_ID_HANVON, USB_DEVICE_ID_HANVON_MULTITOUCH) }, { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, - HID_USB_DEVICE(USB_VENDOR_ID_CANDO, + MT_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH) }, /* Quanta-based panels */ { .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID, - HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, + MT_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, { .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID, - HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, + MT_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001) }, { .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID, - HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, + MT_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008) }, /* Stantum panels */ { .driver_data = MT_CLS_CONFIDENCE, - HID_USB_DEVICE(USB_VENDOR_ID_STANTUM, + MT_USB_DEVICE(USB_VENDOR_ID_STANTUM, USB_DEVICE_ID_MTP)}, { .driver_data = MT_CLS_CONFIDENCE, - HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM, + MT_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM, USB_DEVICE_ID_MTP_STM)}, { .driver_data = MT_CLS_CONFIDENCE, - HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_SITRONIX, + MT_USB_DEVICE(USB_VENDOR_ID_STANTUM_SITRONIX, USB_DEVICE_ID_MTP_SITRONIX)}, /* TopSeed panels */ { .driver_data = MT_CLS_TOPSEED, - HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED2, + MT_USB_DEVICE(USB_VENDOR_ID_TOPSEED2, USB_DEVICE_ID_TOPSEED2_PERIPAD_701) }, /* Touch International panels */ { .driver_data = MT_CLS_DEFAULT, - HID_USB_DEVICE(USB_VENDOR_ID_TOUCH_INTL, + MT_USB_DEVICE(USB_VENDOR_ID_TOUCH_INTL, USB_DEVICE_ID_TOUCH_INTL_MULTI_TOUCH) }, /* Unitec panels */ { .driver_data = MT_CLS_DEFAULT, - HID_USB_DEVICE(USB_VENDOR_ID_UNITEC, + MT_USB_DEVICE(USB_VENDOR_ID_UNITEC, USB_DEVICE_ID_UNITEC_USB_TOUCH_0709) }, { .driver_data = MT_CLS_DEFAULT, - HID_USB_DEVICE(USB_VENDOR_ID_UNITEC, + MT_USB_DEVICE(USB_VENDOR_ID_UNITEC, USB_DEVICE_ID_UNITEC_USB_TOUCH_0A19) }, /* XAT */ { .driver_data = MT_CLS_DEFAULT, - HID_USB_DEVICE(USB_VENDOR_ID_XAT, + MT_USB_DEVICE(USB_VENDOR_ID_XAT, USB_DEVICE_ID_XAT_CSR) }, /* Xiroku */ { .driver_data = MT_CLS_DEFAULT, - HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_SPX) }, { .driver_data = MT_CLS_DEFAULT, - HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_MPX) }, { .driver_data = MT_CLS_DEFAULT, - HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_CSR) }, { .driver_data = MT_CLS_DEFAULT, - HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_SPX1) }, { .driver_data = MT_CLS_DEFAULT, - HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_MPX1) }, { .driver_data = MT_CLS_DEFAULT, - HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_CSR1) }, { .driver_data = MT_CLS_DEFAULT, - HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_SPX2) }, { .driver_data = MT_CLS_DEFAULT, - HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_MPX2) }, { .driver_data = MT_CLS_DEFAULT, - HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, + MT_USB_DEVICE(USB_VENDOR_ID_XIROKU, USB_DEVICE_ID_XIROKU_CSR2) }, + /* Generic MT device */ + { HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH, HID_ANY_ID, HID_ANY_ID) }, { } }; MODULE_DEVICE_TABLE(hid, mt_devices); diff --git a/drivers/hid/hid-uclogic.c b/drivers/hid/hid-uclogic.c index 1f1128910337..3aba02be1f26 100644 --- a/drivers/hid/hid-uclogic.c +++ b/drivers/hid/hid-uclogic.c @@ -14,6 +14,7 @@ #include <linux/device.h> #include <linux/hid.h> #include <linux/module.h> +#include <linux/usb.h> #include "hid-ids.h" @@ -352,9 +353,125 @@ static __u8 pf1209_rdesc_fixed[] = { 0xC0 /* End Collection */ }; +/* + * See TWHL850 description, device and HID report descriptors at + * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Wireless_Tablet_TWHL850 + */ + +/* Size of the original descriptors of TWHL850 tablet */ +#define TWHL850_RDESC_ORIG_SIZE0 182 +#define TWHL850_RDESC_ORIG_SIZE1 161 +#define TWHL850_RDESC_ORIG_SIZE2 92 + +/* Fixed PID 0522 tablet report descriptor, interface 0 (stylus) */ +static __u8 twhl850_rdesc_fixed0[] = { + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x02, /* Usage (Pen), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x09, /* Report ID (9), */ + 0x09, 0x20, /* Usage (Stylus), */ + 0xA0, /* Collection (Physical), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x09, 0x44, /* Usage (Barrel Switch), */ + 0x09, 0x46, /* Usage (Tablet Pick), */ + 0x81, 0x02, /* Input (Variable), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x95, 0x01, /* Report Count (1), */ + 0x09, 0x32, /* Usage (In Range), */ + 0x81, 0x02, /* Input (Variable), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x75, 0x10, /* Report Size (16), */ + 0xA4, /* Push, */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x65, 0x13, /* Unit (Inch), */ + 0x55, 0xFD, /* Unit Exponent (-3), */ + 0x34, /* Physical Minimum (0), */ + 0x09, 0x30, /* Usage (X), */ + 0x46, 0x40, 0x1F, /* Physical Maximum (8000), */ + 0x26, 0x00, 0x7D, /* Logical Maximum (32000), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (Y), */ + 0x46, 0x88, 0x13, /* Physical Maximum (5000), */ + 0x26, 0x20, 0x4E, /* Logical Maximum (20000), */ + 0x81, 0x02, /* Input (Variable), */ + 0xB4, /* Pop, */ + 0x09, 0x30, /* Usage (Tip Pressure), */ + 0x26, 0xFF, 0x03, /* Logical Maximum (1023), */ + 0x81, 0x02, /* Input (Variable), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; + +/* Fixed PID 0522 tablet report descriptor, interface 1 (mouse) */ +static __u8 twhl850_rdesc_fixed1[] = { + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x02, /* Usage (Mouse), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x01, /* Report ID (1), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xA0, /* Collection (Physical), */ + 0x05, 0x09, /* Usage Page (Button), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x19, 0x01, /* Usage Minimum (01h), */ + 0x29, 0x03, /* Usage Maximum (03h), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x05, /* Report Count (5), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x30, /* Usage (X), */ + 0x09, 0x31, /* Usage (Y), */ + 0x16, 0x00, 0x80, /* Logical Minimum (-32768), */ + 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x02, /* Report Count (2), */ + 0x81, 0x06, /* Input (Variable, Relative), */ + 0x09, 0x38, /* Usage (Wheel), */ + 0x15, 0xFF, /* Logical Minimum (-1), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x95, 0x01, /* Report Count (1), */ + 0x75, 0x08, /* Report Size (8), */ + 0x81, 0x06, /* Input (Variable, Relative), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; + +/* Fixed PID 0522 tablet report descriptor, interface 2 (frame buttons) */ +static __u8 twhl850_rdesc_fixed2[] = { + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x06, /* Usage (Keyboard), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x03, /* Report ID (3), */ + 0x05, 0x07, /* Usage Page (Keyboard), */ + 0x14, /* Logical Minimum (0), */ + 0x19, 0xE0, /* Usage Minimum (KB Leftcontrol), */ + 0x29, 0xE7, /* Usage Maximum (KB Right GUI), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x08, /* Report Count (8), */ + 0x81, 0x02, /* Input (Variable), */ + 0x18, /* Usage Minimum (None), */ + 0x29, 0xFF, /* Usage Maximum (FFh), */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x06, /* Report Count (6), */ + 0x80, /* Input, */ + 0xC0 /* End Collection */ +}; + static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { + struct usb_interface *iface = to_usb_interface(hdev->dev.parent); + __u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber; + switch (hdev->product) { case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209: if (*rsize == PF1209_RDESC_ORIG_SIZE) { @@ -386,6 +503,28 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, *rsize = sizeof(wp1062_rdesc_fixed); } break; + case USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850: + switch (iface_num) { + case 0: + if (*rsize == TWHL850_RDESC_ORIG_SIZE0) { + rdesc = twhl850_rdesc_fixed0; + *rsize = sizeof(twhl850_rdesc_fixed0); + } + break; + case 1: + if (*rsize == TWHL850_RDESC_ORIG_SIZE1) { + rdesc = twhl850_rdesc_fixed1; + *rsize = sizeof(twhl850_rdesc_fixed1); + } + break; + case 2: + if (*rsize == TWHL850_RDESC_ORIG_SIZE2) { + rdesc = twhl850_rdesc_fixed2; + *rsize = sizeof(twhl850_rdesc_fixed2); + } + break; + } + break; } return rdesc; @@ -402,6 +541,8 @@ static const struct hid_device_id uclogic_devices[] = { USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, + USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, { } }; MODULE_DEVICE_TABLE(hid, uclogic_devices); diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c index 067e2963314c..fe23a1eb586b 100644 --- a/drivers/hid/hid-wacom.c +++ b/drivers/hid/hid-wacom.c @@ -24,15 +24,16 @@ #include <linux/device.h> #include <linux/hid.h> #include <linux/module.h> +#include <linux/leds.h> #include <linux/slab.h> -#ifdef CONFIG_HID_WACOM_POWER_SUPPLY #include <linux/power_supply.h> -#endif #include "hid-ids.h" #define PAD_DEVICE_ID 0x0F +#define WAC_CMD_LED_CONTROL 0x20 + struct wacom_data { __u16 tool; __u16 butstate; @@ -41,16 +42,20 @@ struct wacom_data { __u32 id; __u32 serial; unsigned char high_speed; -#ifdef CONFIG_HID_WACOM_POWER_SUPPLY - int battery_capacity; + __u8 battery_capacity; + __u8 power_raw; + __u8 ps_connected; struct power_supply battery; struct power_supply ac; -#endif + __u8 led_selector; + struct led_classdev *leds[4]; }; -#ifdef CONFIG_HID_WACOM_POWER_SUPPLY -/*percent of battery capacity, 0 means AC online*/ -static unsigned short batcap[8] = { 1, 15, 25, 35, 50, 70, 100, 0 }; +/*percent of battery capacity for Graphire + 8th value means AC online and show 100% capacity */ +static unsigned short batcap_gr[8] = { 1, 15, 25, 35, 50, 70, 100, 100 }; +/*percent of battery capacity for Intuos4 WL, AC has a separate bit*/ +static unsigned short batcap_i4[8] = { 1, 15, 30, 45, 60, 70, 85, 100 }; static enum power_supply_property wacom_battery_props[] = { POWER_SUPPLY_PROP_PRESENT, @@ -64,13 +69,123 @@ static enum power_supply_property wacom_ac_props[] = { POWER_SUPPLY_PROP_SCOPE, }; +static void wacom_leds_set_brightness(struct led_classdev *led_dev, + enum led_brightness value) +{ + struct device *dev = led_dev->dev->parent; + struct hid_device *hdev; + struct wacom_data *wdata; + unsigned char *buf; + __u8 led = 0; + int i; + + hdev = container_of(dev, struct hid_device, dev); + wdata = hid_get_drvdata(hdev); + for (i = 0; i < 4; ++i) { + if (wdata->leds[i] == led_dev) + wdata->led_selector = i; + } + + led = wdata->led_selector | 0x04; + buf = kzalloc(9, GFP_KERNEL); + if (buf) { + buf[0] = WAC_CMD_LED_CONTROL; + buf[1] = led; + buf[2] = value; + hdev->hid_output_raw_report(hdev, buf, 9, HID_FEATURE_REPORT); + kfree(buf); + } + + return; +} + +static enum led_brightness wacom_leds_get_brightness(struct led_classdev *led_dev) +{ + struct wacom_data *wdata; + struct device *dev = led_dev->dev->parent; + int value = 0; + int i; + + wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev)); + + for (i = 0; i < 4; ++i) { + if (wdata->leds[i] == led_dev) { + value = wdata->leds[i]->brightness; + break; + } + } + + return value; +} + + +static int wacom_initialize_leds(struct hid_device *hdev) +{ + struct wacom_data *wdata = hid_get_drvdata(hdev); + struct led_classdev *led; + struct device *dev = &hdev->dev; + size_t namesz = strlen(dev_name(dev)) + 12; + char *name; + int i, ret; + + wdata->led_selector = 0; + + for (i = 0; i < 4; i++) { + led = kzalloc(sizeof(struct led_classdev) + namesz, GFP_KERNEL); + if (!led) { + hid_warn(hdev, + "can't allocate memory for LED selector\n"); + ret = -ENOMEM; + goto err; + } + + name = (void *)&led[1]; + snprintf(name, namesz, "%s:selector:%d", dev_name(dev), i); + led->name = name; + led->brightness = 0; + led->max_brightness = 127; + led->brightness_get = wacom_leds_get_brightness; + led->brightness_set = wacom_leds_set_brightness; + + wdata->leds[i] = led; + + ret = led_classdev_register(dev, wdata->leds[i]); + + if (ret) { + wdata->leds[i] = NULL; + kfree(led); + hid_warn(hdev, "can't register LED\n"); + goto err; + } + } + +err: + return ret; +} + +static void wacom_destroy_leds(struct hid_device *hdev) +{ + struct wacom_data *wdata = hid_get_drvdata(hdev); + struct led_classdev *led; + int i; + + for (i = 0; i < 4; ++i) { + if (wdata->leds[i]) { + led = wdata->leds[i]; + wdata->leds[i] = NULL; + led_classdev_unregister(led); + kfree(led); + } + } + +} + static int wacom_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct wacom_data *wdata = container_of(psy, struct wacom_data, battery); - int power_state = batcap[wdata->battery_capacity]; int ret = 0; switch (psp) { @@ -81,11 +196,7 @@ static int wacom_battery_get_property(struct power_supply *psy, val->intval = POWER_SUPPLY_SCOPE_DEVICE; break; case POWER_SUPPLY_PROP_CAPACITY: - /* show 100% battery capacity when charging */ - if (power_state == 0) - val->intval = 100; - else - val->intval = power_state; + val->intval = wdata->battery_capacity; break; default: ret = -EINVAL; @@ -99,17 +210,13 @@ static int wacom_ac_get_property(struct power_supply *psy, union power_supply_propval *val) { struct wacom_data *wdata = container_of(psy, struct wacom_data, ac); - int power_state = batcap[wdata->battery_capacity]; int ret = 0; switch (psp) { case POWER_SUPPLY_PROP_PRESENT: /* fall through */ case POWER_SUPPLY_PROP_ONLINE: - if (power_state == 0) - val->intval = 1; - else - val->intval = 0; + val->intval = wdata->ps_connected; break; case POWER_SUPPLY_PROP_SCOPE: val->intval = POWER_SUPPLY_SCOPE_DEVICE; @@ -120,41 +227,16 @@ static int wacom_ac_get_property(struct power_supply *psy, } return ret; } -#endif - -static void wacom_set_features(struct hid_device *hdev) -{ - int ret; - __u8 rep_data[2]; - - /*set high speed, tablet mode*/ - rep_data[0] = 0x03; - rep_data[1] = 0x20; - ret = hdev->hid_output_raw_report(hdev, rep_data, 2, - HID_FEATURE_REPORT); - return; -} -static void wacom_poke(struct hid_device *hdev, u8 speed) +static void wacom_set_features(struct hid_device *hdev, u8 speed) { struct wacom_data *wdata = hid_get_drvdata(hdev); int limit, ret; - char rep_data[2]; - - rep_data[0] = 0x03 ; rep_data[1] = 0x00; - limit = 3; - do { - ret = hdev->hid_output_raw_report(hdev, rep_data, 2, - HID_FEATURE_REPORT); - } while (ret < 0 && limit-- > 0); - - if (ret >= 0) { - if (speed == 0) - rep_data[0] = 0x05; - else - rep_data[0] = 0x06; + __u8 rep_data[2]; - rep_data[1] = 0x00; + switch (hdev->product) { + case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH: + rep_data[0] = 0x03 ; rep_data[1] = 0x00; limit = 3; do { ret = hdev->hid_output_raw_report(hdev, rep_data, 2, @@ -162,17 +244,47 @@ static void wacom_poke(struct hid_device *hdev, u8 speed) } while (ret < 0 && limit-- > 0); if (ret >= 0) { - wdata->high_speed = speed; - return; + if (speed == 0) + rep_data[0] = 0x05; + else + rep_data[0] = 0x06; + + rep_data[1] = 0x00; + limit = 3; + do { + ret = hdev->hid_output_raw_report(hdev, + rep_data, 2, HID_FEATURE_REPORT); + } while (ret < 0 && limit-- > 0); + + if (ret >= 0) { + wdata->high_speed = speed; + return; + } } + + /* + * Note that if the raw queries fail, it's not a hard failure + * and it is safe to continue + */ + hid_warn(hdev, "failed to poke device, command %d, err %d\n", + rep_data[0], ret); + break; + case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH: + if (speed == 1) + wdata->features &= ~0x20; + else + wdata->features |= 0x20; + + rep_data[0] = 0x03; + rep_data[1] = wdata->features; + + ret = hdev->hid_output_raw_report(hdev, rep_data, 2, + HID_FEATURE_REPORT); + if (ret >= 0) + wdata->high_speed = speed; + break; } - /* - * Note that if the raw queries fail, it's not a hard failure and it - * is safe to continue - */ - hid_warn(hdev, "failed to poke device, command %d, err %d\n", - rep_data[0], ret); return; } @@ -196,7 +308,7 @@ static ssize_t wacom_store_speed(struct device *dev, return -EINVAL; if (new_speed == 0 || new_speed == 1) { - wacom_poke(hdev, new_speed); + wacom_set_features(hdev, new_speed); return strnlen(buf, PAGE_SIZE); } else return -EINVAL; @@ -310,12 +422,16 @@ static int wacom_gr_parse_report(struct hid_device *hdev, input_sync(input); } -#ifdef CONFIG_HID_WACOM_POWER_SUPPLY - /* Store current battery capacity */ + /* Store current battery capacity and power supply state*/ rw = (data[7] >> 2 & 0x07); - if (rw != wdata->battery_capacity) - wdata->battery_capacity = rw; -#endif + if (rw != wdata->power_raw) { + wdata->power_raw = rw; + wdata->battery_capacity = batcap_gr[rw]; + if (rw == 7) + wdata->ps_connected = 1; + else + wdata->ps_connected = 0; + } return 1; } @@ -369,6 +485,7 @@ static void wacom_i4_parse_pen_report(struct wacom_data *wdata, { __u16 x, y, pressure; __u8 distance; + __u8 tilt_x, tilt_y; switch (data[1]) { case 0x80: /* Out of proximity report */ @@ -405,6 +522,8 @@ static void wacom_i4_parse_pen_report(struct wacom_data *wdata, pressure = (data[6] << 3) | ((data[7] & 0xC0) >> 5) | (data[1] & 0x01); distance = (data[9] >> 2) & 0x3f; + tilt_x = ((data[7] << 1) & 0x7e) | (data[8] >> 7); + tilt_y = data[8] & 0x7f; input_report_key(input, BTN_TOUCH, pressure > 1); @@ -415,6 +534,8 @@ static void wacom_i4_parse_pen_report(struct wacom_data *wdata, input_report_abs(input, ABS_Y, y); input_report_abs(input, ABS_PRESSURE, pressure); input_report_abs(input, ABS_DISTANCE, distance); + input_report_abs(input, ABS_TILT_X, tilt_x); + input_report_abs(input, ABS_TILT_Y, tilt_y); input_report_abs(input, ABS_MISC, wdata->id); input_event(input, EV_MSC, MSC_SERIAL, wdata->serial); input_report_key(input, wdata->tool, 1); @@ -455,6 +576,7 @@ static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report, struct input_dev *input; unsigned char *data = (unsigned char *) raw_data; int i; + __u8 power_raw; if (!(hdev->claimed & HID_CLAIMED_INPUT)) return 0; @@ -462,13 +584,15 @@ static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report, hidinput = list_entry(hdev->inputs.next, struct hid_input, list); input = hidinput->input; - /* Check if this is a tablet report */ - if (data[0] != 0x03) - return 0; - switch (hdev->product) { case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH: - return wacom_gr_parse_report(hdev, wdata, input, data); + if (data[0] == 0x03) { + return wacom_gr_parse_report(hdev, wdata, input, data); + } else { + hid_err(hdev, "Unknown report: %d,%d size:%d\n", + data[0], data[1], size); + return 0; + } break; case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH: i = 1; @@ -482,6 +606,13 @@ static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report, wacom_i4_parse_report(hdev, wdata, input, data + i); i += 10; wacom_i4_parse_report(hdev, wdata, input, data + i); + power_raw = data[i+10]; + if (power_raw != wdata->power_raw) { + wdata->power_raw = power_raw; + wdata->battery_capacity = batcap_i4[power_raw & 0x07]; + wdata->ps_connected = power_raw & 0x08; + } + break; default: hid_err(hdev, "Unknown report: %d,%d size:%d\n", @@ -546,6 +677,8 @@ static int wacom_input_mapped(struct hid_device *hdev, struct hid_input *hi, input_set_abs_params(input, ABS_Y, 0, 25400, 4, 0); input_set_abs_params(input, ABS_PRESSURE, 0, 2047, 0, 0); input_set_abs_params(input, ABS_DISTANCE, 0, 63, 0, 0); + input_set_abs_params(input, ABS_TILT_X, 0, 127, 0, 0); + input_set_abs_params(input, ABS_TILT_Y, 0, 127, 0, 0); break; } @@ -584,19 +717,19 @@ static int wacom_probe(struct hid_device *hdev, hid_warn(hdev, "can't create sysfs speed attribute err: %d\n", ret); - switch (hdev->product) { - case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH: - /* Set Wacom mode 2 with high reporting speed */ - wacom_poke(hdev, 1); - break; - case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH: + wdata->features = 0; + wacom_set_features(hdev, 1); + + if (hdev->product == USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) { sprintf(hdev->name, "%s", "Wacom Intuos4 WL"); - wdata->features = 0; - wacom_set_features(hdev); - break; + ret = wacom_initialize_leds(hdev); + if (ret) { + hid_warn(hdev, + "can't create led attribute, err: %d\n", ret); + goto destroy_leds; + } } -#ifdef CONFIG_HID_WACOM_POWER_SUPPLY wdata->battery.properties = wacom_battery_props; wdata->battery.num_properties = ARRAY_SIZE(wacom_battery_props); wdata->battery.get_property = wacom_battery_get_property; @@ -629,16 +762,15 @@ static int wacom_probe(struct hid_device *hdev, } power_supply_powers(&wdata->ac, &hdev->dev); -#endif return 0; -#ifdef CONFIG_HID_WACOM_POWER_SUPPLY err_ac: power_supply_unregister(&wdata->battery); err_battery: device_remove_file(&hdev->dev, &dev_attr_speed); hid_hw_stop(hdev); -#endif +destroy_leds: + wacom_destroy_leds(hdev); err_free: kfree(wdata); return ret; @@ -646,16 +778,14 @@ err_free: static void wacom_remove(struct hid_device *hdev) { -#ifdef CONFIG_HID_WACOM_POWER_SUPPLY struct wacom_data *wdata = hid_get_drvdata(hdev); -#endif + + wacom_destroy_leds(hdev); device_remove_file(&hdev->dev, &dev_attr_speed); hid_hw_stop(hdev); -#ifdef CONFIG_HID_WACOM_POWER_SUPPLY power_supply_unregister(&wdata->battery); power_supply_unregister(&wdata->ac); -#endif kfree(hid_get_drvdata(hdev)); } @@ -693,5 +823,5 @@ static void __exit wacom_exit(void) module_init(wacom_init); module_exit(wacom_exit); +MODULE_DESCRIPTION("Driver for Wacom Graphire Bluetooth and Wacom Intuos4 WL"); MODULE_LICENSE("GPL"); - diff --git a/drivers/hid/hid-waltop.c b/drivers/hid/hid-waltop.c index 2cfd95c4467b..745e4e9a8cf2 100644 --- a/drivers/hid/hid-waltop.c +++ b/drivers/hid/hid-waltop.c @@ -502,28 +502,146 @@ static __u8 media_tablet_14_1_inch_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -struct waltop_state { - u8 pressure0; - u8 pressure1; +/* + * See Sirius Battery Free Tablet description, device and HID report descriptors + * at + * http://sf.net/apps/mediawiki/digimend/?title=Waltop_Sirius_Battery_Free_Tablet + */ + +/* Size of the original report descriptor of Sirius Battery Free Tablet */ +#define SIRIUS_BATTERY_FREE_TABLET_RDESC_ORIG_SIZE 335 + +/* Fixed Sirius Battery Free Tablet descriptor */ +static __u8 sirius_battery_free_tablet_rdesc_fixed[] = { + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x02, /* Usage (Pen), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x10, /* Report ID (16), */ + 0x09, 0x20, /* Usage (Stylus), */ + 0xA0, /* Collection (Physical), */ + 0x95, 0x01, /* Report Count (1), */ + 0x15, 0x01, /* Logical Minimum (1), */ + 0x25, 0x03, /* Logical Maximum (3), */ + 0x75, 0x02, /* Report Size (2), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x09, 0x44, /* Usage (Barrel Switch), */ + 0x09, 0x46, /* Usage (Tablet Pick), */ + 0x80, /* Input, */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x09, 0x3C, /* Usage (Invert), */ + 0x81, 0x02, /* Input (Variable), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x09, 0x32, /* Usage (In Range), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0xA4, /* Push, */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x55, 0xFD, /* Unit Exponent (-3), */ + 0x65, 0x13, /* Unit (Inch), */ + 0x34, /* Physical Minimum (0), */ + 0x14, /* Logical Minimum (0), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0x46, 0x10, 0x27, /* Physical Maximum (10000), */ + 0x26, 0x20, 0x4E, /* Logical Maximum (20000), */ + 0x09, 0x30, /* Usage (X), */ + 0x81, 0x02, /* Input (Variable), */ + 0x46, 0x70, 0x17, /* Physical Maximum (6000), */ + 0x26, 0xE0, 0x2E, /* Logical Maximum (12000), */ + 0x09, 0x31, /* Usage (Y), */ + 0x81, 0x02, /* Input (Variable), */ + 0xB4, /* Pop, */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0x14, /* Logical Minimum (0), */ + 0x26, 0xFF, 0x03, /* Logical Maximum (1023), */ + 0x09, 0x30, /* Usage (Tip Pressure), */ + 0x81, 0x02, /* Input (Variable), */ + 0xA4, /* Push, */ + 0x55, 0xFE, /* Unit Exponent (-2), */ + 0x65, 0x12, /* Unit (Radians), */ + 0x35, 0x97, /* Physical Minimum (-105), */ + 0x45, 0x69, /* Physical Maximum (105), */ + 0x15, 0x97, /* Logical Minimum (-105), */ + 0x25, 0x69, /* Logical Maximum (105), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x02, /* Report Count (2), */ + 0x09, 0x3D, /* Usage (X Tilt), */ + 0x09, 0x3E, /* Usage (Y Tilt), */ + 0x81, 0x02, /* Input (Variable), */ + 0xB4, /* Pop, */ + 0xC0, /* End Collection, */ + 0xC0, /* End Collection, */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x02, /* Usage (Mouse), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x01, /* Report ID (1), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xA0, /* Collection (Physical), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x09, 0x38, /* Usage (Wheel), */ + 0x15, 0xFF, /* Logical Minimum (-1), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x06, /* Input (Variable, Relative), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0xC0, /* End Collection, */ + 0xC0, /* End Collection, */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x06, /* Usage (Keyboard), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x0D, /* Report ID (13), */ + 0x05, 0x07, /* Usage Page (Keyboard), */ + 0x19, 0xE0, /* Usage Minimum (KB Leftcontrol), */ + 0x29, 0xE7, /* Usage Maximum (KB Right GUI), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x08, /* Report Count (8), */ + 0x81, 0x02, /* Input (Variable), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x01, /* Input (Constant), */ + 0x18, /* Usage Minimum (None), */ + 0x29, 0x65, /* Usage Maximum (KB Application), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x65, /* Logical Maximum (101), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x05, /* Report Count (5), */ + 0x80, /* Input, */ + 0xC0, /* End Collection, */ + 0x05, 0x0C, /* Usage Page (Consumer), */ + 0x09, 0x01, /* Usage (Consumer Control), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x0C, /* Report ID (12), */ + 0x09, 0xE9, /* Usage (Volume Inc), */ + 0x09, 0xEA, /* Usage (Volume Dec), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x02, /* Report Count (2), */ + 0x81, 0x02, /* Input (Variable), */ + 0x75, 0x06, /* Report Size (6), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0xC0 /* End Collection */ }; static int waltop_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret; - struct waltop_state *s; - - s = kzalloc(sizeof(*s), GFP_KERNEL); - if (s == NULL) { - hid_err(hdev, "can't allocate device state\n"); - ret = -ENOMEM; - goto err; - } - - s->pressure0 = 0; - s->pressure1 = 0; - - hid_set_drvdata(hdev, s); ret = hid_parse(hdev); if (ret) { @@ -539,7 +657,6 @@ static int waltop_probe(struct hid_device *hdev, return 0; err: - kfree(s); return ret; } @@ -583,6 +700,12 @@ static __u8 *waltop_report_fixup(struct hid_device *hdev, __u8 *rdesc, *rsize = sizeof(media_tablet_14_1_inch_rdesc_fixed); } break; + case USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET: + if (*rsize == SIRIUS_BATTERY_FREE_TABLET_RDESC_ORIG_SIZE) { + rdesc = sirius_battery_free_tablet_rdesc_fixed; + *rsize = sizeof(sirius_battery_free_tablet_rdesc_fixed); + } + break; } return rdesc; } @@ -590,39 +713,72 @@ static __u8 *waltop_report_fixup(struct hid_device *hdev, __u8 *rdesc, static int waltop_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) { - /* If this is a pen input report of a tablet with PID 0038 */ - if (hdev->product == USB_DEVICE_ID_WALTOP_PID_0038 && - report->type == HID_INPUT_REPORT && - report->id == 16 && - size == 8) { - struct waltop_state *s = hid_get_drvdata(hdev); - + /* If this is a pen input report */ + if (report->type == HID_INPUT_REPORT && report->id == 16 && size >= 8) { /* - * Ignore maximum pressure reported when a barrel button is - * pressed. + * Ignore reported pressure when a barrel button is pressed, + * because it is rarely correct. */ /* If a barrel button is pressed */ if ((data[1] & 0xF) > 1) { - /* Use the last known pressure */ - data[6] = s->pressure0; - data[7] = s->pressure1; - } else { - /* Remember reported pressure */ - s->pressure0 = data[6]; - s->pressure1 = data[7]; + /* Report zero pressure */ + data[6] = 0; + data[7] = 0; } } + /* If this is a pen input report of Sirius Battery Free Tablet */ + if (hdev->product == USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET && + report->type == HID_INPUT_REPORT && + report->id == 16 && + size == 10) { + /* + * The tablet reports tilt as roughly sin(a)*21 (18 means 60 + * degrees). + * + * This array stores angles as radians * 100, corresponding to + * reported values up to 60 degrees, as expected by userspace. + */ + static const s8 tilt_to_radians[] = { + 0, 5, 10, 14, 19, 24, 29, 34, 40, 45, + 50, 56, 62, 68, 74, 81, 88, 96, 105 + }; + + s8 tilt_x = (s8)data[8]; + s8 tilt_y = (s8)data[9]; + s8 sign_x = tilt_x >= 0 ? 1 : -1; + s8 sign_y = tilt_y >= 0 ? 1 : -1; + + tilt_x *= sign_x; + tilt_y *= sign_y; + + /* + * Reverse the Y Tilt direction to match the HID standard and + * userspace expectations. See HID Usage Tables v1.12 16.3.2 + * Tilt Orientation. + */ + sign_y *= -1; + + /* + * This effectively clamps reported tilt to 60 degrees - the + * range expected by userspace + */ + if (tilt_x > ARRAY_SIZE(tilt_to_radians) - 1) + tilt_x = ARRAY_SIZE(tilt_to_radians) - 1; + if (tilt_y > ARRAY_SIZE(tilt_to_radians) - 1) + tilt_y = ARRAY_SIZE(tilt_to_radians) - 1; + + data[8] = tilt_to_radians[tilt_x] * sign_x; + data[9] = tilt_to_radians[tilt_y] * sign_y; + } + return 0; } static void waltop_remove(struct hid_device *hdev) { - struct waltop_state *s = hid_get_drvdata(hdev); - hid_hw_stop(hdev); - kfree(s); } static const struct hid_device_id waltop_devices[] = { @@ -638,6 +794,8 @@ static const struct hid_device_id waltop_devices[] = { USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH) }, + { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, + USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET) }, { } }; MODULE_DEVICE_TABLE(hid, waltop_devices); diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index cac3589b1ed5..84e2fbec5fbb 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -769,7 +769,7 @@ static void __ir_to_input(struct wiimote_data *wdata, const __u8 *ir, /* * Basic IR data is encoded into 3 bytes. The first two bytes are the - * upper 8 bit of the X/Y data, the 3rd byte contains the lower 2 bits + * lower 8 bit of the X/Y data, the 3rd byte contains the upper 2 bits * of both. * If data is packed, then the 3rd byte is put first and slightly * reordered. This allows to interleave packed and non-packed data to @@ -778,17 +778,11 @@ static void __ir_to_input(struct wiimote_data *wdata, const __u8 *ir, */ if (packed) { - x = ir[1] << 2; - y = ir[2] << 2; - - x |= ir[0] & 0x3; - y |= (ir[0] >> 2) & 0x3; + x = ir[1] | ((ir[0] & 0x03) << 8); + y = ir[2] | ((ir[0] & 0x0c) << 6); } else { - x = ir[0] << 2; - y = ir[1] << 2; - - x |= (ir[2] >> 4) & 0x3; - y |= (ir[2] >> 6) & 0x3; + x = ir[0] | ((ir[2] & 0x30) << 4); + y = ir[1] | ((ir[2] & 0xc0) << 2); } input_report_abs(wdata->ir, xid, x); diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index cf7d6d58e79f..36fa77b40ffb 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -87,11 +87,13 @@ static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, len = list->buffer[list->tail].len > count ? count : list->buffer[list->tail].len; - if (copy_to_user(buffer, list->buffer[list->tail].value, len)) { - ret = -EFAULT; - goto out; + if (list->buffer[list->tail].value) { + if (copy_to_user(buffer, list->buffer[list->tail].value, len)) { + ret = -EFAULT; + goto out; + } + ret = len; } - ret = len; kfree(list->buffer[list->tail].value); list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1); @@ -437,19 +439,24 @@ static const struct file_operations hidraw_ops = { .llseek = noop_llseek, }; -void hidraw_report_event(struct hid_device *hid, u8 *data, int len) +int hidraw_report_event(struct hid_device *hid, u8 *data, int len) { struct hidraw *dev = hid->hidraw; struct hidraw_list *list; + int ret = 0; list_for_each_entry(list, &dev->list, node) { - list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC); + if (!(list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC))) { + ret = -ENOMEM; + break; + } list->buffer[list->head].len = len; list->head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1); kill_fasync(&list->fasync, SIGIO, POLL_IN); } wake_up_interruptible(&dev->wait); + return ret; } EXPORT_SYMBOL_GPL(hidraw_report_event); diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 340d6ae646ed..482f936fc29b 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -28,6 +28,7 @@ #include <linux/input.h> #include <linux/wait.h> #include <linux/workqueue.h> +#include <linux/string.h> #include <linux/usb.h> @@ -86,8 +87,13 @@ static int hid_start_in(struct hid_device *hid) !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) + if (rc != 0) { clear_bit(HID_IN_RUNNING, &usbhid->iofl); + if (rc == -ENOSPC) + set_bit(HID_NO_BANDWIDTH, &usbhid->iofl); + } else { + clear_bit(HID_NO_BANDWIDTH, &usbhid->iofl); + } } spin_unlock_irqrestore(&usbhid->lock, flags); return rc; @@ -173,8 +179,10 @@ static void hid_io_error(struct hid_device *hid) if (time_after(jiffies, usbhid->stop_retry)) { - /* Retries failed, so do a port reset */ - if (!test_and_set_bit(HID_RESET_PENDING, &usbhid->iofl)) { + /* Retries failed, so do a port reset unless we lack bandwidth*/ + if (test_bit(HID_NO_BANDWIDTH, &usbhid->iofl) + && !test_and_set_bit(HID_RESET_PENDING, &usbhid->iofl)) { + schedule_work(&usbhid->reset_work); goto done; } @@ -749,7 +757,7 @@ static int hid_get_class_descriptor(struct usb_device *dev, int ifnum, int usbhid_open(struct hid_device *hid) { struct usbhid_device *usbhid = hid->driver_data; - int res; + int res = 0; mutex_lock(&hid_open_mut); if (!hid->open++) { @@ -757,17 +765,27 @@ int usbhid_open(struct hid_device *hid) /* the device must be awake to reliably request remote wakeup */ if (res < 0) { hid->open--; - mutex_unlock(&hid_open_mut); - return -EIO; + res = -EIO; + goto done; } usbhid->intf->needs_remote_wakeup = 1; - if (hid_start_in(hid)) - hid_io_error(hid); - + res = hid_start_in(hid); + if (res) { + if (res != -ENOSPC) { + hid_io_error(hid); + res = 0; + } else { + /* no use opening if resources are insufficient */ + hid->open--; + res = -EBUSY; + usbhid->intf->needs_remote_wakeup = 0; + } + } usb_autopm_put_interface(usbhid->intf); } +done: mutex_unlock(&hid_open_mut); - return 0; + return res; } void usbhid_close(struct hid_device *hid) @@ -1396,7 +1414,34 @@ 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; + struct usb_host_interface *interface = intf->cur_altsetting; int status; + char *rdesc; + + /* Fetch and examine the HID report descriptor. If this + * has changed, then rebind. Since usbcore's check of the + * configuration descriptors passed, we already know that + * the size of the HID report descriptor has not changed. + */ + rdesc = kmalloc(hid->rsize, GFP_KERNEL); + if (!rdesc) { + dbg_hid("couldn't allocate rdesc memory (post_reset)\n"); + return 1; + } + status = hid_get_class_descriptor(dev, + interface->desc.bInterfaceNumber, + HID_DT_REPORT, rdesc, hid->rsize); + if (status < 0) { + dbg_hid("reading report descriptor failed (post_reset)\n"); + kfree(rdesc); + return 1; + } + status = memcmp(rdesc, hid->rdesc, hid->rsize); + kfree(rdesc); + if (status != 0) { + dbg_hid("report descriptor changed\n"); + return 1; + } spin_lock_irq(&usbhid->lock); clear_bit(HID_RESET_PENDING, &usbhid->iofl); @@ -1553,28 +1598,15 @@ static struct usb_driver hid_driver = { .supports_autosuspend = 1, }; -static const struct hid_device_id hid_usb_table[] = { - { HID_USB_DEVICE(HID_ANY_ID, HID_ANY_ID) }, - { } -}; - struct usb_interface *usbhid_find_interface(int minor) { return usb_find_interface(&hid_driver, minor); } -static struct hid_driver hid_usb_driver = { - .name = "generic-usb", - .id_table = hid_usb_table, -}; - static int __init hid_init(void) { int retval = -ENOMEM; - retval = hid_register_driver(&hid_usb_driver); - if (retval) - goto hid_register_fail; retval = usbhid_quirks_init(quirks_param); if (retval) goto usbhid_quirks_init_fail; @@ -1587,8 +1619,6 @@ static int __init hid_init(void) usb_register_fail: usbhid_quirks_exit(); usbhid_quirks_init_fail: - hid_unregister_driver(&hid_usb_driver); -hid_register_fail: return retval; } @@ -1596,7 +1626,6 @@ static void __exit hid_exit(void) { usb_deregister(&hid_driver); usbhid_quirks_exit(); - hid_unregister_driver(&hid_usb_driver); } module_init(hid_init); diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 782c63955f29..0597ee604f6e 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -88,6 +88,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH, HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_WISEGROUP_LTD2, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index b1ec0e2aeb57..14599e256791 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -34,6 +34,7 @@ #include <linux/hid.h> #include <linux/hiddev.h> #include <linux/compat.h> +#include <linux/vmalloc.h> #include "usbhid.h" #ifdef CONFIG_USB_DYNAMIC_MINORS @@ -250,13 +251,13 @@ static int hiddev_release(struct inode * inode, struct file * file) } else { mutex_unlock(&list->hiddev->existancelock); kfree(list->hiddev); - kfree(list); + vfree(list); return 0; } } mutex_unlock(&list->hiddev->existancelock); - kfree(list); + vfree(list); return 0; } @@ -278,7 +279,7 @@ static int hiddev_open(struct inode *inode, struct file *file) hid = usb_get_intfdata(intf); hiddev = hid->hiddev; - if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL))) + if (!(list = vzalloc(sizeof(struct hiddev_list)))) return -ENOMEM; mutex_init(&list->thread_lock); list->hiddev = hiddev; @@ -322,7 +323,7 @@ bail_unlock: mutex_unlock(&hiddev->existancelock); bail: file->private_data = NULL; - kfree(list); + vfree(list); return res; } diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index cb8f703efde5..1883d7b94870 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h @@ -55,6 +55,7 @@ struct usb_interface *usbhid_find_interface(int minor); #define HID_STARTED 8 #define HID_REPORTED_IDLE 9 #define HID_KEYS_PRESSED 10 +#define HID_NO_BANDWIDTH 11 /* * USB-specific HID struct, to be pointed to |