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
  ...
This commit is contained in:
Linus Torvalds 2012-05-22 19:21:48 -07:00
commit 3c2c4b73aa
29 changed files with 1468 additions and 567 deletions

View File

@ -9,6 +9,14 @@ Description:
or 0 otherwise. Writing to this file one of these values or 0 otherwise. Writing to this file one of these values
switches reporting speed. switches reporting speed.
What: /sys/class/leds/0005\:056A\:00BD.0001\:selector\:*/
Date: May 2012
Kernel Version: 3.5
Contact: linux-bluetooth@vger.kernel.org
Description:
LED selector for Intuos4 WL. There are 4 leds, but only one LED
can be lit at a time. Max brightness is 127.
What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/led What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/led
Date: August 2011 Date: August 2011
Contact: linux-input@vger.kernel.org Contact: linux-input@vger.kernel.org

View File

@ -32,9 +32,13 @@ config HID
If unsure, say Y. If unsure, say Y.
config HID_BATTERY_STRENGTH config HID_BATTERY_STRENGTH
bool bool "Battery level reporting for HID devices"
depends on HID && POWER_SUPPLY && HID = POWER_SUPPLY depends on HID && POWER_SUPPLY && HID = POWER_SUPPLY
default n 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 config HIDRAW
bool "/dev/hidraw raw HID device support" bool "/dev/hidraw raw HID device support"
@ -60,6 +64,18 @@ source "drivers/hid/usbhid/Kconfig"
menu "Special HID drivers" menu "Special HID drivers"
depends on HID 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 config HID_A4TECH
tristate "A4 tech mice" if EXPERT tristate "A4 tech mice" if EXPERT
depends on USB_HID depends on USB_HID
@ -92,6 +108,12 @@ config HID_APPLE
Say Y here if you want support for keyboards of Apple iBooks, PowerBooks, Say Y here if you want support for keyboards of Apple iBooks, PowerBooks,
MacBooks, MacBook Pros and Apple Aluminum. 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 config HID_BELKIN
tristate "Belkin Flip KVM and Wireless keyboard" if EXPERT tristate "Belkin Flip KVM and Wireless keyboard" if EXPERT
depends on USB_HID depends on USB_HID
@ -595,16 +617,10 @@ config THRUSTMASTER_FF
config HID_WACOM config HID_WACOM
tristate "Wacom Bluetooth devices support" tristate "Wacom Bluetooth devices support"
depends on BT_HIDP depends on BT_HIDP
---help--- depends on LEDS_CLASS
Support for Wacom Graphire Bluetooth tablet.
config HID_WACOM_POWER_SUPPLY
bool "Wacom Bluetooth devices power supply status support"
depends on HID_WACOM
select POWER_SUPPLY select POWER_SUPPLY
---help--- ---help---
Say Y here if you want to enable power supply status monitoring for Support for Wacom Graphire Bluetooth and Intuos4 WL tablets.
Wacom Bluetooth devices.
config HID_WIIMOTE config HID_WIIMOTE
tristate "Nintendo Wii Remote support" tristate "Nintendo Wii Remote support"

View File

@ -9,6 +9,8 @@ endif
obj-$(CONFIG_HID) += hid.o obj-$(CONFIG_HID) += hid.o
obj-$(CONFIG_HID_GENERIC) += hid-generic.o
hid-$(CONFIG_HIDRAW) += hidraw.o hid-$(CONFIG_HIDRAW) += hidraw.o
hid-logitech-y := hid-lg.o hid-logitech-y := hid-lg.o
@ -36,6 +38,7 @@ endif
obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o
obj-$(CONFIG_HID_ACRUX) += hid-axff.o obj-$(CONFIG_HID_ACRUX) += hid-axff.o
obj-$(CONFIG_HID_APPLE) += hid-apple.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_BELKIN) += hid-belkin.o
obj-$(CONFIG_HID_CHERRY) += hid-cherry.o obj-$(CONFIG_HID_CHERRY) += hid-cherry.o
obj-$(CONFIG_HID_CHICONY) += hid-chicony.o obj-$(CONFIG_HID_CHICONY) += hid-chicony.o

View File

@ -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) { if (asc->quirks & APPLE_ISO_KEYBOARD) {
trans = apple_find_translation(apple_iso_keyboard, usage->code); trans = apple_find_translation(apple_iso_keyboard, usage->code);
if (trans) { 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), { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
APPLE_ISO_KEYBOARD }, 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), { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN }, .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI), { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI),

54
drivers/hid/hid-aureal.c Normal file
View File

@ -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");

View File

@ -230,9 +230,16 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
return -1; return -1;
} }
if (parser->global.logical_maximum < parser->global.logical_minimum) { /* Handle both signed and unsigned cases properly */
hid_err(parser->device, "logical range invalid %d %d\n", if ((parser->global.logical_minimum < 0 &&
parser->global.logical_minimum, parser->global.logical_maximum); 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; 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_close_report(struct hid_device *device)
static void hid_device_release(struct device *dev)
{ {
struct hid_device *device = container_of(dev, struct hid_device, dev);
unsigned i, j; unsigned i, j;
for (i = 0; i < HID_REPORT_TYPES; i++) { for (i = 0; i < HID_REPORT_TYPES; i++) {
@ -562,11 +568,34 @@ static void hid_device_release(struct device *dev)
if (report) if (report)
hid_free_report(report); hid_free_report(report);
} }
memset(report_enum, 0, sizeof(*report_enum));
INIT_LIST_HEAD(&report_enum->report_list);
} }
kfree(device->rdesc); kfree(device->rdesc);
device->rdesc = NULL;
device->rsize = 0;
kfree(device->collection); 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; 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 * 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 * @start: report start
* @size: report size * @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 * Parse a report description into a hid_device structure. Reports are
* enumerated, fields are attached to these reports. * enumerated, fields are attached to these reports.
* 0 returned on success, otherwise nonzero error value. * 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, int hid_open_report(struct hid_device *device)
unsigned size)
{ {
struct hid_parser *parser; struct hid_parser *parser;
struct hid_item item; struct hid_item item;
unsigned int size;
__u8 *start;
__u8 *end; __u8 *end;
int ret; int ret;
static int (*dispatch_type[])(struct hid_parser *parser, 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 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) if (device->driver->report_fixup)
start = device->driver->report_fixup(device, start, &size); 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; parser->device = device;
end = start + size; 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; ret = -EINVAL;
while ((start = fetch_item(start, end, &item)) != NULL) { while ((start = fetch_item(start, end, &item)) != NULL) {
@ -704,6 +826,7 @@ int hid_parse_report(struct hid_device *device, __u8 *start,
goto err; goto err;
} }
vfree(parser); vfree(parser);
device->status |= HID_STAT_PARSED;
return 0; 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)); hid_err(device, "item fetching failed at offset %d\n", (int)(end - start));
err: err:
vfree(parser); vfree(parser);
hid_close_report(device);
return ret; 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 * 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; 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) int interrupt)
{ {
struct hid_report_enum *report_enum = hid->report_enum + type; 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; unsigned int a;
int rsize, csize = size; int rsize, csize = size;
u8 *cdata = data; u8 *cdata = data;
int ret = 0;
report = hid_get_report(report_enum, data); report = hid_get_report(report_enum, data);
if (!report) if (!report)
return; goto out;
if (report_enum->numbered) { if (report_enum->numbered) {
cdata++; 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) if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event)
hid->hiddev_report_event(hid, report); hid->hiddev_report_event(hid, report);
if (hid->claimed & HID_CLAIMED_HIDRAW) if (hid->claimed & HID_CLAIMED_HIDRAW) {
hidraw_report_event(hid, data, size); ret = hidraw_report_event(hid, data, size);
if (ret)
goto out;
}
for (a = 0; a < report->maxfield; a++) for (a = 0; a < report->maxfield; a++)
hid_input_field(hid, report->field[a], cdata, interrupt); hid_input_field(hid, report->field[a], cdata, interrupt);
if (hid->claimed & HID_CLAIMED_INPUT) if (hid->claimed & HID_CLAIMED_INPUT)
hidinput_report_event(hid, report); hidinput_report_event(hid, report);
out:
return ret;
} }
EXPORT_SYMBOL_GPL(hid_report_raw_event); 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: unlock:
up(&hid->driver_lock); up(&hid->driver_lock);
@ -1158,7 +1288,8 @@ EXPORT_SYMBOL_GPL(hid_input_report);
static bool hid_match_one_id(struct hid_device *hdev, static bool hid_match_one_id(struct hid_device *hdev,
const struct hid_device_id *id) 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->vendor == HID_ANY_ID || id->vendor == hdev->vendor) &&
(id->product == HID_ANY_ID || id->product == hdev->product); (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, if ((connect_mask & HID_CONNECT_HIDINPUT) && !hidinput_connect(hdev,
connect_mask & HID_CONNECT_HIDINPUT_FORCE)) connect_mask & HID_CONNECT_HIDINPUT_FORCE))
hdev->claimed |= HID_CLAIMED_INPUT; 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 && if ((connect_mask & HID_CONNECT_HIDDEV) && hdev->hiddev_connect &&
!hdev->hiddev_connect(hdev, !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 */ /* a list of devices for which there is a specialized driver on HID bus */
static const struct hid_device_id hid_have_special_driver[] = { 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_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_X5_005D) },
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_RP_649) }, { 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_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_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_IRCONTROL4) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) }, { 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_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_FOUNTAIN_TP_ONLY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_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_AUREAL, USB_DEVICE_ID_AUREAL_W01RN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUSTEK_MULTITOUCH_YFO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM) }, { 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) },
{ HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE_2) }, { 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) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION_SOLAR) }, { 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_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_WIRELESS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS2) }, { 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_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_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_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_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_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, 0x0006) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) }, { 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_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_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II) },
{ HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) }, { 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_PSX_ADAPTOR) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_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, 0x0003) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0012) }, { 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) },
{ 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_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) }, { 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_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_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KEYTOUCH, USB_DEVICE_ID_KEYTOUCH_IEC) }, { 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) }, { 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_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_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_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_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) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) }, { 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_LOGITECH_RUMBLEPAD2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) }, { 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_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) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) }, { 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) }, { 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_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_PKB1700) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) }, { 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_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_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_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_KONE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) }, { 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_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_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_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_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) }, { 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_TIVO, USB_DEVICE_ID_TIVO_SLIDE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) }, { 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_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_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_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_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_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_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_TABLET_WP1062) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UNITEC, USB_DEVICE_ID_UNITEC_USB_TOUCH_0709) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UNITEC, USB_DEVICE_ID_UNITEC_USB_TOUCH_0A19) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) }, { 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_SUPER_JOY_BOX_3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD) }, { 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_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_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_MEDIA_TABLET_14_1_INCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_XAT, USB_DEVICE_ID_XAT_CSR) }, { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET) },
{ 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_X_TENSIONS, USB_DEVICE_ID_SPEEDLINK_VAD_CEZANNE) }, { 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, 0x0005) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) }, { 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; return -ENOMEM;
dynid->id.bus = bus; dynid->id.bus = bus;
dynid->id.group = HID_GROUP_ANY;
dynid->id.vendor = vendor; dynid->id.vendor = vendor;
dynid->id.product = product; dynid->id.product = product;
dynid->id.driver_data = driver_data; 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_driver *hdrv = container_of(drv, struct hid_driver, driver);
struct hid_device *hdev = container_of(dev, struct hid_device, dev); struct hid_device *hdev = container_of(dev, struct hid_device, dev);
if ((hdev->quirks & HID_QUIRK_MULTITOUCH) && return hid_match_device(hdev, hdrv) != NULL;
!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;
} }
static int hid_device_probe(struct device *dev) static int hid_device_probe(struct device *dev)
@ -1707,23 +1769,22 @@ static int hid_device_probe(struct device *dev)
if (!hdev->driver) { if (!hdev->driver) {
id = hid_match_device(hdev, hdrv); id = hid_match_device(hdev, hdrv);
if (id == NULL) { if (id == NULL) {
if (!((hdev->quirks & HID_QUIRK_MULTITOUCH) && ret = -ENODEV;
!strncmp(hdrv->name, "hid-multitouch", 14))) { goto unlock;
ret = -ENODEV;
goto unlock;
}
} }
hdev->driver = hdrv; hdev->driver = hdrv;
if (hdrv->probe) { if (hdrv->probe) {
ret = hdrv->probe(hdev, id); ret = hdrv->probe(hdev, id);
} else { /* default probe */ } else { /* default probe */
ret = hid_parse(hdev); ret = hid_open_report(hdev);
if (!ret) if (!ret)
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
} }
if (ret) if (ret) {
hid_close_report(hdev);
hdev->driver = NULL; hdev->driver = NULL;
}
} }
unlock: unlock:
up(&hdev->driver_lock); up(&hdev->driver_lock);
@ -1744,6 +1805,7 @@ static int hid_device_remove(struct device *dev)
hdrv->remove(hdev); hdrv->remove(hdev);
else /* default remove */ else /* default remove */
hid_hw_stop(hdev); hid_hw_stop(hdev);
hid_close_report(hdev);
hdev->driver = NULL; hdev->driver = NULL;
} }
@ -1751,6 +1813,23 @@ static int hid_device_remove(struct device *dev)
return 0; 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) static int hid_uevent(struct device *dev, struct kobj_uevent_env *env)
{ {
struct hid_device *hdev = container_of(dev, struct hid_device, dev); 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)) if (add_uevent_var(env, "HID_UNIQ=%s", hdev->uniq))
return -ENOMEM; return -ENOMEM;
if (add_uevent_var(env, "MODALIAS=hid:b%04Xv%08Xp%08X", if (add_uevent_var(env, "MODALIAS=hid:b%04Xg%04Xv%08Xp%08X",
hdev->bus, hdev->vendor, hdev->product)) hdev->bus, hdev->group, hdev->vendor, hdev->product))
return -ENOMEM; return -ENOMEM;
return 0; 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 = { static struct bus_type hid_bus_type = {
.name = "hid", .name = "hid",
.dev_attrs = hid_dev_attrs,
.match = hid_bus_match, .match = hid_bus_match,
.probe = hid_device_probe, .probe = hid_device_probe,
.remove = hid_device_remove, .remove = hid_device_remove,
@ -2075,6 +2155,26 @@ int hid_add_device(struct hid_device *hdev)
&& (hid_ignore(hdev) || (hdev->quirks & HID_QUIRK_IGNORE))) && (hid_ignore(hdev) || (hdev->quirks & HID_QUIRK_IGNORE)))
return -ENODEV; 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 /* XXX hack, any other cleaner solution after the driver core
* is converted to allow more than 20 bytes as the device name? */ * is converted to allow more than 20 bytes as the device name? */
dev_set_name(&hdev->dev, "%04X:%04X:%04X.%04X", hdev->bus, 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 *hid_allocate_device(void)
{ {
struct hid_device *hdev; struct hid_device *hdev;
unsigned int i;
int ret = -ENOMEM; int ret = -ENOMEM;
hdev = kzalloc(sizeof(*hdev), GFP_KERNEL); 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.release = hid_device_release;
hdev->dev.bus = &hid_bus_type; hdev->dev.bus = &hid_bus_type;
hdev->collection = kcalloc(HID_DEFAULT_NUM_COLLECTIONS, hid_close_report(hdev);
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);
init_waitqueue_head(&hdev->debug_wait); init_waitqueue_head(&hdev->debug_wait);
INIT_LIST_HEAD(&hdev->debug_list); INIT_LIST_HEAD(&hdev->debug_list);
sema_init(&hdev->driver_lock, 1); sema_init(&hdev->driver_lock, 1);
return hdev; return hdev;
err:
put_device(&hdev->dev);
return ERR_PTR(ret);
} }
EXPORT_SYMBOL_GPL(hid_allocate_device); EXPORT_SYMBOL_GPL(hid_allocate_device);
@ -2141,6 +2230,9 @@ static void hid_remove_device(struct hid_device *hdev)
hid_debug_unregister(hdev); hid_debug_unregister(hdev);
hdev->status &= ~HID_STAT_ADDED; hdev->status &= ~HID_STAT_ADDED;
} }
kfree(hdev->dev_rdesc);
hdev->dev_rdesc = NULL;
hdev->dev_rsize = 0;
} }
/** /**

53
drivers/hid/hid-generic.c Normal file
View File

@ -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");

View File

@ -430,6 +430,15 @@ cleanup:
return ret; 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) static int mousevsc_hid_open(struct hid_device *hid)
{ {
return 0; return 0;
@ -449,6 +458,7 @@ static void mousevsc_hid_stop(struct hid_device *hid)
} }
static struct hid_ll_driver mousevsc_ll_driver = { static struct hid_ll_driver mousevsc_ll_driver = {
.parse = mousevsc_hid_parse,
.open = mousevsc_hid_open, .open = mousevsc_hid_open,
.close = mousevsc_hid_close, .close = mousevsc_hid_close,
.start = mousevsc_hid_start, .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"); sprintf(hid_dev->name, "%s", "Microsoft Vmbus HID-compliant Mouse");
hid_set_drvdata(hid_dev, device);
ret = hid_add_device(hid_dev); ret = hid_add_device(hid_dev);
if (ret) if (ret)
goto probe_err1; 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) { if (ret) {
hid_err(hid_dev, "parse failed\n"); hid_err(hid_dev, "parse failed\n");
goto probe_err2; goto probe_err2;

View File

@ -154,9 +154,15 @@
#define USB_DEVICE_ID_ATMEL_MULTITOUCH 0x211c #define USB_DEVICE_ID_ATMEL_MULTITOUCH 0x211c
#define USB_DEVICE_ID_ATMEL_MXT_DIGITIZER 0x2118 #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_VENDOR_ID_AVERMEDIA 0x07ca
#define USB_DEVICE_ID_AVER_FM_MR800 0xb800 #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_VENDOR_ID_BELKIN 0x050d
#define USB_DEVICE_ID_FLIP_KVM 0x3201 #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_WP5540U 0x0004
#define USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U 0x0005 #define USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U 0x0005
#define USB_DEVICE_ID_UCLOGIC_TABLET_WP1062 0x0064 #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_VENDOR_ID_UNITEC 0x227d
#define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709 0x0709 #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_PID_0038 0x0038
#define USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH 0x0501 #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_MEDIA_TABLET_14_1_INCH 0x0500
#define USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET 0x0502
#define USB_VENDOR_ID_WISEGROUP 0x0925 #define USB_VENDOR_ID_WISEGROUP 0x0925
#define USB_DEVICE_ID_SMARTJOY_PLUS 0x0005 #define USB_DEVICE_ID_SMARTJOY_PLUS 0x0005

View File

@ -225,7 +225,10 @@ static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)
* Verify and convert units. * Verify and convert units.
* See HID specification v1.11 6.2.2.7 Global Items for unit decoding * 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 */ if (field->unit == 0x11) { /* If centimeters */
/* Convert to millimeters */ /* Convert to millimeters */
unit_exponent += 1; unit_exponent += 1;
@ -239,7 +242,13 @@ static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code)
} else { } else {
return 0; 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 */ if (field->unit == 0x14) { /* If degrees */
/* Convert to radians */ /* Convert to radians */
prev = logical_extents; 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 */ } else if (field->unit != 0x12) { /* If not radians */
return 0; return 0;
} }
} else { break;
default:
return 0; return 0;
} }
@ -623,6 +634,14 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
map_key_clear(BTN_TOOL_RUBBER); map_key_clear(BTN_TOOL_RUBBER);
break; 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 0x33: /* Touch */
case 0x42: /* TipSwitch */ case 0x42: /* TipSwitch */
case 0x43: /* TipSwitch2 */ case 0x43: /* TipSwitch2 */
@ -638,10 +657,6 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
map_key_clear(BTN_STYLUS2); map_key_clear(BTN_STYLUS2);
break; break;
case 0x51: /* ContactID */
device->quirks |= HID_QUIRK_MULTITOUCH;
goto unknown;
default: goto unknown; default: goto unknown;
} }
break; 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)) if (hidinput && input_register_device(hidinput->input))
goto out_cleanup; goto out_cleanup;

View File

@ -109,23 +109,23 @@ static __u8 dfp_rdesc_fixed[] = {
static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize) 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) { rdesc[84] == 0x8c && rdesc[85] == 0x02) {
hid_info(hdev, hid_info(hdev,
"fixing up Logitech keyboard report descriptor\n"); "fixing up Logitech keyboard report descriptor\n");
rdesc[84] = rdesc[89] = 0x4d; rdesc[84] = rdesc[89] = 0x4d;
rdesc[85] = rdesc[90] = 0x10; 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[32] == 0x81 && rdesc[33] == 0x06 &&
rdesc[49] == 0x81 && rdesc[50] == 0x06) { rdesc[49] == 0x81 && rdesc[50] == 0x06) {
hid_info(hdev, hid_info(hdev,
"fixing up rel/abs in Logitech report descriptor\n"); "fixing up rel/abs in Logitech report descriptor\n");
rdesc[33] = rdesc[50] = 0x02; 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[41] == 0x95 && rdesc[42] == 0x0B &&
rdesc[47] == 0x05 && rdesc[48] == 0x09) { rdesc[47] == 0x05 && rdesc[48] == 0x09) {
hid_info(hdev, "fixing up Logitech Speed Force Wireless button descriptor\n"); 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, 0, 0, 0, 0, 0,183,184,185,186,187,
188,189,190,191,192,193,194, 0, 0, 0 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; unsigned int hid = usage->hid;
if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER && 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)) lg_dinovo_mapping(hi, usage, bit, max))
return 1; 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; return 1;
if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON) 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 */ /* Special handling for Logitech Cordless Desktop */
if (field->application == HID_GD_MOUSE) { if (field->application == HID_GD_MOUSE) {
if ((quirks & LG_IGNORE_DOUBLED_WHEEL) && if ((drv_data->quirks & LG_IGNORE_DOUBLED_WHEEL) &&
(hid == 7 || hid == 8)) (hid == 7 || hid == 8))
return -1; return -1;
} else { } else {
if ((quirks & LG_EXPANDED_KEYMAP) && if ((drv_data->quirks & LG_EXPANDED_KEYMAP) &&
hid < ARRAY_SIZE(e_keymap) && hid < ARRAY_SIZE(e_keymap) &&
e_keymap[hid] != 0) { e_keymap[hid] != 0) {
hid_map_usage(hi, usage, bit, max, EV_KEY, 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, struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max) 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))
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)) usage->type == EV_REL || usage->type == EV_ABS))
clear_bit(usage->code, *bit); 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, static int lg_event(struct hid_device *hdev, struct hid_field *field,
struct hid_usage *usage, __s32 value) 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, input_event(field->hidinput->input, usage->type, usage->code,
-value); -value);
return 1; 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) 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; unsigned int connect_mask = HID_CONNECT_DEFAULT;
struct lg_drv_data *drv_data;
int ret; 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; hdev->quirks |= HID_QUIRK_NOGET;
ret = hid_parse(hdev); 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; 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; connect_mask &= ~HID_CONNECT_FF;
ret = hid_hw_start(hdev, connect_mask); 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); lgff_init(hdev);
if (quirks & LG_FF2) if (drv_data->quirks & LG_FF2)
lg2ff_init(hdev); lg2ff_init(hdev);
if (quirks & LG_FF3) if (drv_data->quirks & LG_FF3)
lg3ff_init(hdev); lg3ff_init(hdev);
if (quirks & LG_FF4) if (drv_data->quirks & LG_FF4)
lg4ff_init(hdev); lg4ff_init(hdev);
return 0; return 0;
err_free: err_free:
kfree(drv_data);
return ret; return ret;
} }
static void lg_remove(struct hid_device *hdev) static void lg_remove(struct hid_device *hdev)
{ {
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_FF4) if (drv_data->quirks & LG_FF4)
lg4ff_deinit(hdev); lg4ff_deinit(hdev);
hid_hw_stop(hdev); hid_hw_stop(hdev);
kfree(drv_data);
} }
static const struct hid_device_id lg_devices[] = { static const struct hid_device_id lg_devices[] = {

View File

@ -1,6 +1,11 @@
#ifndef __HID_LG_H #ifndef __HID_LG_H
#define __HID_LG_H #define __HID_LG_H
struct lg_drv_data {
unsigned long quirks;
void *device_props; /* Device specific properties */
};
#ifdef CONFIG_LOGITECH_FF #ifdef CONFIG_LOGITECH_FF
int lgff_init(struct hid_device *hdev); int lgff_init(struct hid_device *hdev);
#else #else

View File

@ -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> * 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 DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IRWXO, lg4ff_range_show, lg4ff_range_store);
static bool list_inited;
struct lg4ff_device_entry { struct lg4ff_device_entry {
char *device_id; /* Use name in respective kobject structure's address as the ID */
__u16 range; __u16 range;
__u16 min_range; __u16 min_range;
__u16 max_range; __u16 max_range;
__u8 leds; #ifdef CONFIG_LEDS_CLASS
__u8 led_state;
struct led_classdev *led[5];
#endif
struct list_head list; struct list_head list;
void (*set_range)(struct hid_device *hid, u16 range); void (*set_range)(struct hid_device *hid, u16 range);
}; };
static struct lg4ff_device_entry device_list;
static const signed short lg4ff_wheel_effects[] = { static const signed short lg4ff_wheel_effects[] = {
FF_CONSTANT, FF_CONSTANT,
FF_AUTOCENTER, 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 */ /* Read current range and display it in terminal */
static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, char *buf) 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 hid_device *hid = to_hid_device(dev);
struct lg4ff_device_entry *entry;
struct lg_drv_data *drv_data;
size_t count; size_t count;
list_for_each(h, &device_list.list) { drv_data = hid_get_drvdata(hid);
entry = list_entry(h, struct lg4ff_device_entry, list); if (!drv_data) {
if (strcmp(entry->device_id, (&hid->dev)->kobj.name) == 0) hid_err(hid, "Private driver data not found!\n");
break; 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; 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 */ * 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) 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 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); __u16 range = simple_strtoul(buf, NULL, 10);
list_for_each(h, &device_list.list) { drv_data = hid_get_drvdata(hid);
entry = list_entry(h, struct lg4ff_device_entry, list); if (!drv_data) {
if (strcmp(entry->device_id, (&hid->dev)->kobj.name) == 0) hid_err(hid, "Private driver data not found!\n");
break; return 0;
} }
if (h == &device_list.list) {
dbg_hid("Device not found!"); entry = drv_data->device_props;
return count; if (!entry) {
hid_err(hid, "Device properties not found!\n");
return 0;
} }
if (range == 0) if (range == 0)
@ -336,6 +339,88 @@ static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *at
return count; 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) int lg4ff_init(struct hid_device *hid)
{ {
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); 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_report *report;
struct hid_field *field; struct hid_field *field;
struct lg4ff_device_entry *entry; struct lg4ff_device_entry *entry;
struct lg_drv_data *drv_data;
struct usb_device_descriptor *udesc; struct usb_device_descriptor *udesc;
int error, i, j; int error, i, j;
__u16 bcdDevice, rev_maj, rev_min; __u16 bcdDevice, rev_maj, rev_min;
@ -423,28 +509,24 @@ int lg4ff_init(struct hid_device *hid)
dev->ff->set_autocenter(dev, 0); dev->ff->set_autocenter(dev, 0);
} }
/* Initialize device_list if this is the first device to handle by lg4ff */ /* Get private driver data */
if (!list_inited) { drv_data = hid_get_drvdata(hid);
INIT_LIST_HEAD(&device_list.list); if (!drv_data) {
list_inited = 1; 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); entry = kzalloc(sizeof(struct lg4ff_device_entry), GFP_KERNEL);
if (!entry) { if (!entry) {
hid_err(hid, "Cannot add device, insufficient memory.\n"); hid_err(hid, "Cannot add device, insufficient memory to allocate device properties.\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);
return -ENOMEM; return -ENOMEM;
} }
drv_data->device_props = entry;
entry->min_range = lg4ff_devices[i].min_range; entry->min_range = lg4ff_devices[i].min_range;
entry->max_range = lg4ff_devices[i].max_range; entry->max_range = lg4ff_devices[i].max_range;
entry->set_range = lg4ff_devices[i].set_range; entry->set_range = lg4ff_devices[i].set_range;
list_add(&entry->list, &device_list.list);
/* Create sysfs interface */ /* Create sysfs interface */
error = device_create_file(&hid->dev, &dev_attr_range); 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) if (entry->set_range != NULL)
entry->set_range(hid, entry->range); 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; return 0;
} }
int lg4ff_deinit(struct hid_device *hid) int lg4ff_deinit(struct hid_device *hid)
{ {
bool found = 0;
struct lg4ff_device_entry *entry; struct lg4ff_device_entry *entry;
struct list_head *h, *g; struct lg_drv_data *drv_data;
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;
}
}
if (!found) { device_remove_file(&hid->dev, &dev_attr_range);
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; 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"); dbg_hid("Device successfully unregistered\n");
return 0; return 0;
} }

View File

@ -26,6 +26,7 @@
#include <linux/hid.h> #include <linux/hid.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/usb.h> #include <linux/usb.h>
#include <asm/unaligned.h>
#include "usbhid/usbhid.h" #include "usbhid/usbhid.h"
#include "hid-ids.h" #include "hid-ids.h"
#include "hid-logitech-dj.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) */ /* Maximum size of all defined hid reports in bytes (including report id) */
#define MAX_REPORT_SIZE 8 #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. /* 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) * 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; goto dj_device_allocate_fail;
} }
dj_dev->reports_supported = le32_to_cpu( dj_dev->reports_supported = get_unaligned_le32(
dj_report->report_params[DEVICE_PAIRED_RF_REPORT_TYPE]); dj_report->report_params + DEVICE_PAIRED_RF_REPORT_TYPE);
dj_dev->hdev = dj_hiddev; dj_dev->hdev = dj_hiddev;
dj_dev->dj_receiver_dev = djrcv_dev; dj_dev->dj_receiver_dev = djrcv_dev;
dj_dev->device_index = dj_report->device_index; 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; 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) static int logi_dj_ll_parse(struct hid_device *hid)
{ {
struct dj_device *djdev = hid->driver_data; struct dj_device *djdev = hid->driver_data;
unsigned int rsize = 0;
char *rdesc;
int retval; int retval;
dbg_hid("%s\n", __func__); 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->version = 0x0111;
djdev->hdev->country = 0x00; djdev->hdev->country = 0x00;
rdesc = kmalloc(MAX_RDESC_SIZE, GFP_KERNEL);
if (!rdesc)
return -ENOMEM;
if (djdev->reports_supported & STD_KEYBOARD) { if (djdev->reports_supported & STD_KEYBOARD) {
dbg_hid("%s: sending a kbd descriptor, reports_supported: %x\n", dbg_hid("%s: sending a kbd descriptor, reports_supported: %x\n",
__func__, djdev->reports_supported); __func__, djdev->reports_supported);
retval = hid_parse_report(hid, rdcat(&rdesc, &rsize, kbd_descriptor, sizeof(kbd_descriptor));
(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;
}
} }
if (djdev->reports_supported & STD_MOUSE) { if (djdev->reports_supported & STD_MOUSE) {
dbg_hid("%s: sending a mouse descriptor, reports_supported: " dbg_hid("%s: sending a mouse descriptor, reports_supported: "
"%x\n", __func__, djdev->reports_supported); "%x\n", __func__, djdev->reports_supported);
retval = hid_parse_report(hid, rdcat(&rdesc, &rsize, mse_descriptor, sizeof(mse_descriptor));
(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;
}
} }
if (djdev->reports_supported & MULTIMEDIA) { if (djdev->reports_supported & MULTIMEDIA) {
dbg_hid("%s: sending a multimedia report descriptor: %x\n", dbg_hid("%s: sending a multimedia report descriptor: %x\n",
__func__, djdev->reports_supported); __func__, djdev->reports_supported);
retval = hid_parse_report(hid, rdcat(&rdesc, &rsize, consumer_descriptor, sizeof(consumer_descriptor));
(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;
}
} }
if (djdev->reports_supported & POWER_KEYS) { if (djdev->reports_supported & POWER_KEYS) {
dbg_hid("%s: sending a power keys report descriptor: %x\n", dbg_hid("%s: sending a power keys report descriptor: %x\n",
__func__, djdev->reports_supported); __func__, djdev->reports_supported);
retval = hid_parse_report(hid, rdcat(&rdesc, &rsize, syscontrol_descriptor, sizeof(syscontrol_descriptor));
(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;
}
} }
if (djdev->reports_supported & MEDIA_CENTER) { if (djdev->reports_supported & MEDIA_CENTER) {
dbg_hid("%s: sending a media center report descriptor: %x\n", dbg_hid("%s: sending a media center report descriptor: %x\n",
__func__, djdev->reports_supported); __func__, djdev->reports_supported);
retval = hid_parse_report(hid, rdcat(&rdesc, &rsize, media_descriptor, sizeof(media_descriptor));
(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;
}
} }
if (djdev->reports_supported & KBD_LEDS) { if (djdev->reports_supported & KBD_LEDS) {
@ -554,7 +539,10 @@ static int logi_dj_ll_parse(struct hid_device *hid)
__func__, djdev->reports_supported); __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, static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type,

View File

@ -70,9 +70,16 @@ struct mt_class {
bool is_indirect; /* true for touchpads */ bool is_indirect; /* true for touchpads */
}; };
struct mt_fields {
unsigned usages[HID_MAX_FIELDS];
unsigned int length;
};
struct mt_device { struct mt_device {
struct mt_slot curdata; /* placeholder of incoming data */ struct mt_slot curdata; /* placeholder of incoming data */
struct mt_class mtclass; /* our mt device class */ 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_field_index; /* last field index of the report */
unsigned last_slot_field; /* the last field of a slot */ unsigned last_slot_field; /* the last field of a slot */
__s8 inputmode; /* InputMode HID feature, -1 if non-existent */ __s8 inputmode; /* InputMode HID feature, -1 if non-existent */
@ -110,6 +117,9 @@ struct mt_device {
#define MT_DEFAULT_MAXCONTACT 10 #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 * these device-dependent functions determine what slot corresponds
* to a valid contact that was just read. * 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); 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) struct hid_input *hi)
{ {
if (!test_bit(usage->hid, hi->input->absbit)) struct mt_fields *f = td->fields;
td->last_slot_field = usage->hid;
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, 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); cls->sn_move);
/* touchscreen emulation */ /* touchscreen emulation */
set_abs(hi->input, ABS_X, field, cls->sn_move); 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; td->last_field_index = field->index;
return 1; return 1;
case HID_GD_Y: case HID_GD_Y:
@ -340,7 +354,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
cls->sn_move); cls->sn_move);
/* touchscreen emulation */ /* touchscreen emulation */
set_abs(hi->input, ABS_Y, field, cls->sn_move); 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; td->last_field_index = field->index;
return 1; return 1;
} }
@ -349,24 +363,24 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
case HID_UP_DIGITIZER: case HID_UP_DIGITIZER:
switch (usage->hid) { switch (usage->hid) {
case HID_DG_INRANGE: case HID_DG_INRANGE:
set_last_slot_field(usage, td, hi); mt_store_field(usage, td, hi);
td->last_field_index = field->index; td->last_field_index = field->index;
return 1; return 1;
case HID_DG_CONFIDENCE: case HID_DG_CONFIDENCE:
set_last_slot_field(usage, td, hi); mt_store_field(usage, td, hi);
td->last_field_index = field->index; td->last_field_index = field->index;
return 1; return 1;
case HID_DG_TIPSWITCH: case HID_DG_TIPSWITCH:
hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH);
input_set_capability(hi->input, 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; td->last_field_index = field->index;
return 1; return 1;
case HID_DG_CONTACTID: case HID_DG_CONTACTID:
if (!td->maxcontacts) if (!td->maxcontacts)
td->maxcontacts = MT_DEFAULT_MAXCONTACT; td->maxcontacts = MT_DEFAULT_MAXCONTACT;
input_mt_init_slots(hi->input, td->maxcontacts); 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->last_field_index = field->index;
td->touches_by_report++; td->touches_by_report++;
return 1; 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); EV_ABS, ABS_MT_TOUCH_MAJOR);
set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field, set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field,
cls->sn_width); cls->sn_width);
set_last_slot_field(usage, td, hi); mt_store_field(usage, td, hi);
td->last_field_index = field->index; td->last_field_index = field->index;
return 1; return 1;
case HID_DG_HEIGHT: case HID_DG_HEIGHT:
@ -385,7 +399,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
cls->sn_height); cls->sn_height);
input_set_abs_params(hi->input, input_set_abs_params(hi->input,
ABS_MT_ORIENTATION, 0, 1, 0, 0); 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; td->last_field_index = field->index;
return 1; return 1;
case HID_DG_TIPPRESSURE: case HID_DG_TIPPRESSURE:
@ -396,7 +410,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
/* touchscreen emulation */ /* touchscreen emulation */
set_abs(hi->input, ABS_PRESSURE, field, set_abs(hi->input, ABS_PRESSURE, field,
cls->sn_pressure); cls->sn_pressure);
set_last_slot_field(usage, td, hi); mt_store_field(usage, td, hi);
td->last_field_index = field->index; td->last_field_index = field->index;
return 1; return 1;
case HID_DG_CONTACTCOUNT: 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) static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
{ {
int ret, i; 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. * that emit events over several HID messages.
*/ */
hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC; hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC;
hdev->quirks &= ~HID_QUIRK_MULTITOUCH;
td = kzalloc(sizeof(struct mt_device), GFP_KERNEL); td = kzalloc(sizeof(struct mt_device), GFP_KERNEL);
if (!td) { 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; td->maxcontact_report_id = -1;
hid_set_drvdata(hdev, td); 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); ret = hid_parse(hdev);
if (ret != 0) if (ret != 0)
goto fail; goto fail;
@ -674,14 +719,10 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (ret) if (ret)
goto fail; goto fail;
if (!id && td->touches_by_report == 1) { mt_post_parse(td);
/* the device has been sent by hid-generic */
mtclass = &td->mtclass; if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID)
mtclass->quirks |= MT_QUIRK_ALWAYS_VALID; mt_post_parse_default_settings(td);
mtclass->quirks &= ~MT_QUIRK_NOT_SEEN_MEANS_UP;
mtclass->quirks &= ~MT_QUIRK_VALID_IS_INRANGE;
mtclass->quirks &= ~MT_QUIRK_VALID_IS_CONFIDENCE;
}
td->slots = kzalloc(td->maxcontacts * sizeof(struct mt_slot), td->slots = kzalloc(td->maxcontacts * sizeof(struct mt_slot),
GFP_KERNEL); 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_maxcontacts(hdev);
mt_set_input_mode(hdev); mt_set_input_mode(hdev);
kfree(td->fields);
td->fields = NULL;
return 0; return 0;
fail: fail:
kfree(td->fields);
kfree(td); kfree(td);
return ret; return ret;
} }
@ -727,50 +772,54 @@ static const struct hid_device_id mt_devices[] = {
/* 3M panels */ /* 3M panels */
{ .driver_data = MT_CLS_3M, { .driver_data = MT_CLS_3M,
HID_USB_DEVICE(USB_VENDOR_ID_3M, MT_USB_DEVICE(USB_VENDOR_ID_3M,
USB_DEVICE_ID_3M1968) }, USB_DEVICE_ID_3M1968) },
{ .driver_data = MT_CLS_3M, { .driver_data = MT_CLS_3M,
HID_USB_DEVICE(USB_VENDOR_ID_3M, MT_USB_DEVICE(USB_VENDOR_ID_3M,
USB_DEVICE_ID_3M2256) }, USB_DEVICE_ID_3M2256) },
{ .driver_data = MT_CLS_3M, { .driver_data = MT_CLS_3M,
HID_USB_DEVICE(USB_VENDOR_ID_3M, MT_USB_DEVICE(USB_VENDOR_ID_3M,
USB_DEVICE_ID_3M3266) }, USB_DEVICE_ID_3M3266) },
/* ActionStar panels */ /* ActionStar panels */
{ .driver_data = MT_CLS_DEFAULT, { .driver_data = MT_CLS_DEFAULT,
HID_USB_DEVICE(USB_VENDOR_ID_ACTIONSTAR, MT_USB_DEVICE(USB_VENDOR_ID_ACTIONSTAR,
USB_DEVICE_ID_ACTIONSTAR_1011) }, USB_DEVICE_ID_ACTIONSTAR_1011) },
/* Atmel panels */ /* Atmel panels */
{ .driver_data = MT_CLS_SERIAL, { .driver_data = MT_CLS_SERIAL,
HID_USB_DEVICE(USB_VENDOR_ID_ATMEL, MT_USB_DEVICE(USB_VENDOR_ID_ATMEL,
USB_DEVICE_ID_ATMEL_MULTITOUCH) }, USB_DEVICE_ID_ATMEL_MULTITOUCH) },
{ .driver_data = MT_CLS_SERIAL, { .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) }, 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 */ /* Cando panels */
{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, { .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) }, USB_DEVICE_ID_CANDO_MULTI_TOUCH) },
{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, { .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) }, USB_DEVICE_ID_CANDO_MULTI_TOUCH_10_1) },
{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, { .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) }, USB_DEVICE_ID_CANDO_MULTI_TOUCH_11_6) },
{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, { .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) }, USB_DEVICE_ID_CANDO_MULTI_TOUCH_15_6) },
/* Chunghwa Telecom touch panels */ /* Chunghwa Telecom touch panels */
{ .driver_data = MT_CLS_DEFAULT, { .driver_data = MT_CLS_DEFAULT,
HID_USB_DEVICE(USB_VENDOR_ID_CHUNGHWAT, MT_USB_DEVICE(USB_VENDOR_ID_CHUNGHWAT,
USB_DEVICE_ID_CHUNGHWAT_MULTITOUCH) }, USB_DEVICE_ID_CHUNGHWAT_MULTITOUCH) },
/* CVTouch panels */ /* CVTouch panels */
{ .driver_data = MT_CLS_DEFAULT, { .driver_data = MT_CLS_DEFAULT,
HID_USB_DEVICE(USB_VENDOR_ID_CVTOUCH, MT_USB_DEVICE(USB_VENDOR_ID_CVTOUCH,
USB_DEVICE_ID_CVTOUCH_SCREEN) }, USB_DEVICE_ID_CVTOUCH_SCREEN) },
/* Cypress panel */ /* Cypress panel */
@ -780,225 +829,227 @@ static const struct hid_device_id mt_devices[] = {
/* eGalax devices (resistive) */ /* eGalax devices (resistive) */
{ .driver_data = MT_CLS_EGALAX, { .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) }, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480D) },
{ .driver_data = MT_CLS_EGALAX, { .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) }, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E) },
/* eGalax devices (capacitive) */ /* eGalax devices (capacitive) */
{ .driver_data = MT_CLS_EGALAX, { .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) }, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C) },
{ .driver_data = MT_CLS_EGALAX_SERIAL, { .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) }, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7207) },
{ .driver_data = MT_CLS_EGALAX_SERIAL, { .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) }, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_725E) },
{ .driver_data = MT_CLS_EGALAX_SERIAL, { .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) }, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224) },
{ .driver_data = MT_CLS_EGALAX_SERIAL, { .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) }, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_722A) },
{ .driver_data = MT_CLS_EGALAX, { .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) }, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B) },
{ .driver_data = MT_CLS_EGALAX_SERIAL, { .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) }, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7262) },
{ .driver_data = MT_CLS_EGALAX, { .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) }, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1) },
{ .driver_data = MT_CLS_EGALAX_SERIAL, { .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) }, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72AA) },
{ .driver_data = MT_CLS_EGALAX, { .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) }, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA) },
{ .driver_data = MT_CLS_EGALAX, { .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) }, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302) },
{ .driver_data = MT_CLS_EGALAX_SERIAL, { .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) }, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7349) },
{ .driver_data = MT_CLS_EGALAX_SERIAL, { .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) }, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) },
/* Elo TouchSystems IntelliTouch Plus panel */ /* Elo TouchSystems IntelliTouch Plus panel */
{ .driver_data = MT_CLS_DUAL_NSMU_CONTACTID, { .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) }, USB_DEVICE_ID_ELO_TS2515) },
/* GeneralTouch panel */ /* GeneralTouch panel */
{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER, { .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) }, USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS) },
/* Gametel game controller */ /* Gametel game controller */
{ .driver_data = MT_CLS_DEFAULT, { .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) }, USB_DEVICE_ID_GAMETEL_MT_MODE) },
/* GoodTouch panels */ /* GoodTouch panels */
{ .driver_data = MT_CLS_DEFAULT, { .driver_data = MT_CLS_DEFAULT,
HID_USB_DEVICE(USB_VENDOR_ID_GOODTOUCH, MT_USB_DEVICE(USB_VENDOR_ID_GOODTOUCH,
USB_DEVICE_ID_GOODTOUCH_000f) }, USB_DEVICE_ID_GOODTOUCH_000f) },
/* Hanvon panels */ /* Hanvon panels */
{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, { .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) }, USB_DEVICE_ID_HANVON_ALT_MULTITOUCH) },
/* Ideacom panel */ /* Ideacom panel */
{ .driver_data = MT_CLS_SERIAL, { .driver_data = MT_CLS_SERIAL,
HID_USB_DEVICE(USB_VENDOR_ID_IDEACOM, MT_USB_DEVICE(USB_VENDOR_ID_IDEACOM,
USB_DEVICE_ID_IDEACOM_IDC6650) }, USB_DEVICE_ID_IDEACOM_IDC6650) },
{ .driver_data = MT_CLS_SERIAL, { .driver_data = MT_CLS_SERIAL,
HID_USB_DEVICE(USB_VENDOR_ID_IDEACOM, MT_USB_DEVICE(USB_VENDOR_ID_IDEACOM,
USB_DEVICE_ID_IDEACOM_IDC6651) }, USB_DEVICE_ID_IDEACOM_IDC6651) },
/* Ilitek dual touch panel */ /* Ilitek dual touch panel */
{ .driver_data = MT_CLS_DEFAULT, { .driver_data = MT_CLS_DEFAULT,
HID_USB_DEVICE(USB_VENDOR_ID_ILITEK, MT_USB_DEVICE(USB_VENDOR_ID_ILITEK,
USB_DEVICE_ID_ILITEK_MULTITOUCH) }, USB_DEVICE_ID_ILITEK_MULTITOUCH) },
/* IRTOUCH panels */ /* IRTOUCH panels */
{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, { .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) }, USB_DEVICE_ID_IRTOUCH_INFRARED_USB) },
/* LG Display panels */ /* LG Display panels */
{ .driver_data = MT_CLS_DEFAULT, { .driver_data = MT_CLS_DEFAULT,
HID_USB_DEVICE(USB_VENDOR_ID_LG, MT_USB_DEVICE(USB_VENDOR_ID_LG,
USB_DEVICE_ID_LG_MULTITOUCH) }, USB_DEVICE_ID_LG_MULTITOUCH) },
/* Lumio panels */ /* Lumio panels */
{ .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, { .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) }, USB_DEVICE_ID_CRYSTALTOUCH) },
{ .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, { .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) }, USB_DEVICE_ID_CRYSTALTOUCH_DUAL) },
/* MosArt panels */ /* MosArt panels */
{ .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, { .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)}, USB_DEVICE_ID_ASUS_T91MT)},
{ .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, { .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) }, USB_DEVICE_ID_ASUSTEK_MULTITOUCH_YFO) },
{ .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, { .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) }, USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART) },
/* Panasonic panels */ /* Panasonic panels */
{ .driver_data = MT_CLS_PANASONIC, { .driver_data = MT_CLS_PANASONIC,
HID_USB_DEVICE(USB_VENDOR_ID_PANASONIC, MT_USB_DEVICE(USB_VENDOR_ID_PANASONIC,
USB_DEVICE_ID_PANABOARD_UBT780) }, USB_DEVICE_ID_PANABOARD_UBT780) },
{ .driver_data = MT_CLS_PANASONIC, { .driver_data = MT_CLS_PANASONIC,
HID_USB_DEVICE(USB_VENDOR_ID_PANASONIC, MT_USB_DEVICE(USB_VENDOR_ID_PANASONIC,
USB_DEVICE_ID_PANABOARD_UBT880) }, USB_DEVICE_ID_PANABOARD_UBT880) },
/* PenMount panels */ /* PenMount panels */
{ .driver_data = MT_CLS_CONFIDENCE, { .driver_data = MT_CLS_CONFIDENCE,
HID_USB_DEVICE(USB_VENDOR_ID_PENMOUNT, MT_USB_DEVICE(USB_VENDOR_ID_PENMOUNT,
USB_DEVICE_ID_PENMOUNT_PCI) }, USB_DEVICE_ID_PENMOUNT_PCI) },
/* PixArt optical touch screen */ /* PixArt optical touch screen */
{ .driver_data = MT_CLS_INRANGE_CONTACTNUMBER, { .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) }, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN) },
{ .driver_data = MT_CLS_INRANGE_CONTACTNUMBER, { .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) }, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1) },
{ .driver_data = MT_CLS_INRANGE_CONTACTNUMBER, { .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) }, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2) },
/* PixCir-based panels */ /* PixCir-based panels */
{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, { .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) }, USB_DEVICE_ID_HANVON_MULTITOUCH) },
{ .driver_data = MT_CLS_DUAL_INRANGE_CONTACTID, { .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) }, USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH) },
/* Quanta-based panels */ /* Quanta-based panels */
{ .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID, { .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) }, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) },
{ .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID, { .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) }, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001) },
{ .driver_data = MT_CLS_CONFIDENCE_CONTACT_ID, { .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) }, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008) },
/* Stantum panels */ /* Stantum panels */
{ .driver_data = MT_CLS_CONFIDENCE, { .driver_data = MT_CLS_CONFIDENCE,
HID_USB_DEVICE(USB_VENDOR_ID_STANTUM, MT_USB_DEVICE(USB_VENDOR_ID_STANTUM,
USB_DEVICE_ID_MTP)}, USB_DEVICE_ID_MTP)},
{ .driver_data = MT_CLS_CONFIDENCE, { .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)}, USB_DEVICE_ID_MTP_STM)},
{ .driver_data = MT_CLS_CONFIDENCE, { .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)}, USB_DEVICE_ID_MTP_SITRONIX)},
/* TopSeed panels */ /* TopSeed panels */
{ .driver_data = MT_CLS_TOPSEED, { .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) }, USB_DEVICE_ID_TOPSEED2_PERIPAD_701) },
/* Touch International panels */ /* Touch International panels */
{ .driver_data = MT_CLS_DEFAULT, { .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) }, USB_DEVICE_ID_TOUCH_INTL_MULTI_TOUCH) },
/* Unitec panels */ /* Unitec panels */
{ .driver_data = MT_CLS_DEFAULT, { .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) }, USB_DEVICE_ID_UNITEC_USB_TOUCH_0709) },
{ .driver_data = MT_CLS_DEFAULT, { .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) }, USB_DEVICE_ID_UNITEC_USB_TOUCH_0A19) },
/* XAT */ /* XAT */
{ .driver_data = MT_CLS_DEFAULT, { .driver_data = MT_CLS_DEFAULT,
HID_USB_DEVICE(USB_VENDOR_ID_XAT, MT_USB_DEVICE(USB_VENDOR_ID_XAT,
USB_DEVICE_ID_XAT_CSR) }, USB_DEVICE_ID_XAT_CSR) },
/* Xiroku */ /* Xiroku */
{ .driver_data = MT_CLS_DEFAULT, { .driver_data = MT_CLS_DEFAULT,
HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, MT_USB_DEVICE(USB_VENDOR_ID_XIROKU,
USB_DEVICE_ID_XIROKU_SPX) }, USB_DEVICE_ID_XIROKU_SPX) },
{ .driver_data = MT_CLS_DEFAULT, { .driver_data = MT_CLS_DEFAULT,
HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, MT_USB_DEVICE(USB_VENDOR_ID_XIROKU,
USB_DEVICE_ID_XIROKU_MPX) }, USB_DEVICE_ID_XIROKU_MPX) },
{ .driver_data = MT_CLS_DEFAULT, { .driver_data = MT_CLS_DEFAULT,
HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, MT_USB_DEVICE(USB_VENDOR_ID_XIROKU,
USB_DEVICE_ID_XIROKU_CSR) }, USB_DEVICE_ID_XIROKU_CSR) },
{ .driver_data = MT_CLS_DEFAULT, { .driver_data = MT_CLS_DEFAULT,
HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, MT_USB_DEVICE(USB_VENDOR_ID_XIROKU,
USB_DEVICE_ID_XIROKU_SPX1) }, USB_DEVICE_ID_XIROKU_SPX1) },
{ .driver_data = MT_CLS_DEFAULT, { .driver_data = MT_CLS_DEFAULT,
HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, MT_USB_DEVICE(USB_VENDOR_ID_XIROKU,
USB_DEVICE_ID_XIROKU_MPX1) }, USB_DEVICE_ID_XIROKU_MPX1) },
{ .driver_data = MT_CLS_DEFAULT, { .driver_data = MT_CLS_DEFAULT,
HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, MT_USB_DEVICE(USB_VENDOR_ID_XIROKU,
USB_DEVICE_ID_XIROKU_CSR1) }, USB_DEVICE_ID_XIROKU_CSR1) },
{ .driver_data = MT_CLS_DEFAULT, { .driver_data = MT_CLS_DEFAULT,
HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, MT_USB_DEVICE(USB_VENDOR_ID_XIROKU,
USB_DEVICE_ID_XIROKU_SPX2) }, USB_DEVICE_ID_XIROKU_SPX2) },
{ .driver_data = MT_CLS_DEFAULT, { .driver_data = MT_CLS_DEFAULT,
HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, MT_USB_DEVICE(USB_VENDOR_ID_XIROKU,
USB_DEVICE_ID_XIROKU_MPX2) }, USB_DEVICE_ID_XIROKU_MPX2) },
{ .driver_data = MT_CLS_DEFAULT, { .driver_data = MT_CLS_DEFAULT,
HID_USB_DEVICE(USB_VENDOR_ID_XIROKU, MT_USB_DEVICE(USB_VENDOR_ID_XIROKU,
USB_DEVICE_ID_XIROKU_CSR2) }, 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); MODULE_DEVICE_TABLE(hid, mt_devices);

View File

@ -14,6 +14,7 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/hid.h> #include <linux/hid.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/usb.h>
#include "hid-ids.h" #include "hid-ids.h"
@ -352,9 +353,125 @@ static __u8 pf1209_rdesc_fixed[] = {
0xC0 /* End Collection */ 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, static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize) unsigned int *rsize)
{ {
struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
__u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
switch (hdev->product) { switch (hdev->product) {
case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209: case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209:
if (*rsize == PF1209_RDESC_ORIG_SIZE) { 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); *rsize = sizeof(wp1062_rdesc_fixed);
} }
break; 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; return rdesc;
@ -402,6 +541,8 @@ static const struct hid_device_id uclogic_devices[] = {
USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) }, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) }, 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); MODULE_DEVICE_TABLE(hid, uclogic_devices);

View File

@ -24,15 +24,16 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/hid.h> #include <linux/hid.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/leds.h>
#include <linux/slab.h> #include <linux/slab.h>
#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
#include <linux/power_supply.h> #include <linux/power_supply.h>
#endif
#include "hid-ids.h" #include "hid-ids.h"
#define PAD_DEVICE_ID 0x0F #define PAD_DEVICE_ID 0x0F
#define WAC_CMD_LED_CONTROL 0x20
struct wacom_data { struct wacom_data {
__u16 tool; __u16 tool;
__u16 butstate; __u16 butstate;
@ -41,16 +42,20 @@ struct wacom_data {
__u32 id; __u32 id;
__u32 serial; __u32 serial;
unsigned char high_speed; unsigned char high_speed;
#ifdef CONFIG_HID_WACOM_POWER_SUPPLY __u8 battery_capacity;
int battery_capacity; __u8 power_raw;
__u8 ps_connected;
struct power_supply battery; struct power_supply battery;
struct power_supply ac; struct power_supply ac;
#endif __u8 led_selector;
struct led_classdev *leds[4];
}; };
#ifdef CONFIG_HID_WACOM_POWER_SUPPLY /*percent of battery capacity for Graphire
/*percent of battery capacity, 0 means AC online*/ 8th value means AC online and show 100% capacity */
static unsigned short batcap[8] = { 1, 15, 25, 35, 50, 70, 100, 0 }; 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[] = { static enum power_supply_property wacom_battery_props[] = {
POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_PRESENT,
@ -64,13 +69,123 @@ static enum power_supply_property wacom_ac_props[] = {
POWER_SUPPLY_PROP_SCOPE, 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, static int wacom_battery_get_property(struct power_supply *psy,
enum power_supply_property psp, enum power_supply_property psp,
union power_supply_propval *val) union power_supply_propval *val)
{ {
struct wacom_data *wdata = container_of(psy, struct wacom_data *wdata = container_of(psy,
struct wacom_data, battery); struct wacom_data, battery);
int power_state = batcap[wdata->battery_capacity];
int ret = 0; int ret = 0;
switch (psp) { switch (psp) {
@ -81,11 +196,7 @@ static int wacom_battery_get_property(struct power_supply *psy,
val->intval = POWER_SUPPLY_SCOPE_DEVICE; val->intval = POWER_SUPPLY_SCOPE_DEVICE;
break; break;
case POWER_SUPPLY_PROP_CAPACITY: case POWER_SUPPLY_PROP_CAPACITY:
/* show 100% battery capacity when charging */ val->intval = wdata->battery_capacity;
if (power_state == 0)
val->intval = 100;
else
val->intval = power_state;
break; break;
default: default:
ret = -EINVAL; ret = -EINVAL;
@ -99,17 +210,13 @@ static int wacom_ac_get_property(struct power_supply *psy,
union power_supply_propval *val) union power_supply_propval *val)
{ {
struct wacom_data *wdata = container_of(psy, struct wacom_data, ac); struct wacom_data *wdata = container_of(psy, struct wacom_data, ac);
int power_state = batcap[wdata->battery_capacity];
int ret = 0; int ret = 0;
switch (psp) { switch (psp) {
case POWER_SUPPLY_PROP_PRESENT: case POWER_SUPPLY_PROP_PRESENT:
/* fall through */ /* fall through */
case POWER_SUPPLY_PROP_ONLINE: case POWER_SUPPLY_PROP_ONLINE:
if (power_state == 0) val->intval = wdata->ps_connected;
val->intval = 1;
else
val->intval = 0;
break; break;
case POWER_SUPPLY_PROP_SCOPE: case POWER_SUPPLY_PROP_SCOPE:
val->intval = POWER_SUPPLY_SCOPE_DEVICE; val->intval = POWER_SUPPLY_SCOPE_DEVICE;
@ -120,41 +227,16 @@ static int wacom_ac_get_property(struct power_supply *psy,
} }
return ret; return ret;
} }
#endif
static void wacom_set_features(struct hid_device *hdev) static void wacom_set_features(struct hid_device *hdev, u8 speed)
{
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)
{ {
struct wacom_data *wdata = hid_get_drvdata(hdev); struct wacom_data *wdata = hid_get_drvdata(hdev);
int limit, ret; int limit, ret;
char rep_data[2]; __u8 rep_data[2];
rep_data[0] = 0x03 ; rep_data[1] = 0x00; switch (hdev->product) {
limit = 3; case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH:
do { rep_data[0] = 0x03 ; rep_data[1] = 0x00;
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;
rep_data[1] = 0x00;
limit = 3; limit = 3;
do { do {
ret = hdev->hid_output_raw_report(hdev, rep_data, 2, 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); } while (ret < 0 && limit-- > 0);
if (ret >= 0) { if (ret >= 0) {
wdata->high_speed = speed; if (speed == 0)
return; 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; return;
} }
@ -196,7 +308,7 @@ static ssize_t wacom_store_speed(struct device *dev,
return -EINVAL; return -EINVAL;
if (new_speed == 0 || new_speed == 1) { if (new_speed == 0 || new_speed == 1) {
wacom_poke(hdev, new_speed); wacom_set_features(hdev, new_speed);
return strnlen(buf, PAGE_SIZE); return strnlen(buf, PAGE_SIZE);
} else } else
return -EINVAL; return -EINVAL;
@ -310,12 +422,16 @@ static int wacom_gr_parse_report(struct hid_device *hdev,
input_sync(input); input_sync(input);
} }
#ifdef CONFIG_HID_WACOM_POWER_SUPPLY /* Store current battery capacity and power supply state*/
/* Store current battery capacity */
rw = (data[7] >> 2 & 0x07); rw = (data[7] >> 2 & 0x07);
if (rw != wdata->battery_capacity) if (rw != wdata->power_raw) {
wdata->battery_capacity = rw; wdata->power_raw = rw;
#endif wdata->battery_capacity = batcap_gr[rw];
if (rw == 7)
wdata->ps_connected = 1;
else
wdata->ps_connected = 0;
}
return 1; return 1;
} }
@ -369,6 +485,7 @@ static void wacom_i4_parse_pen_report(struct wacom_data *wdata,
{ {
__u16 x, y, pressure; __u16 x, y, pressure;
__u8 distance; __u8 distance;
__u8 tilt_x, tilt_y;
switch (data[1]) { switch (data[1]) {
case 0x80: /* Out of proximity report */ 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) pressure = (data[6] << 3) | ((data[7] & 0xC0) >> 5)
| (data[1] & 0x01); | (data[1] & 0x01);
distance = (data[9] >> 2) & 0x3f; 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); 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_Y, y);
input_report_abs(input, ABS_PRESSURE, pressure); input_report_abs(input, ABS_PRESSURE, pressure);
input_report_abs(input, ABS_DISTANCE, distance); 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_report_abs(input, ABS_MISC, wdata->id);
input_event(input, EV_MSC, MSC_SERIAL, wdata->serial); input_event(input, EV_MSC, MSC_SERIAL, wdata->serial);
input_report_key(input, wdata->tool, 1); 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; struct input_dev *input;
unsigned char *data = (unsigned char *) raw_data; unsigned char *data = (unsigned char *) raw_data;
int i; int i;
__u8 power_raw;
if (!(hdev->claimed & HID_CLAIMED_INPUT)) if (!(hdev->claimed & HID_CLAIMED_INPUT))
return 0; 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); hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
input = hidinput->input; input = hidinput->input;
/* Check if this is a tablet report */
if (data[0] != 0x03)
return 0;
switch (hdev->product) { switch (hdev->product) {
case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH: 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; break;
case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH: case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH:
i = 1; 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); wacom_i4_parse_report(hdev, wdata, input, data + i);
i += 10; i += 10;
wacom_i4_parse_report(hdev, wdata, input, data + i); 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; break;
default: default:
hid_err(hdev, "Unknown report: %d,%d size:%d\n", 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_Y, 0, 25400, 4, 0);
input_set_abs_params(input, ABS_PRESSURE, 0, 2047, 0, 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_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; break;
} }
@ -584,19 +717,19 @@ static int wacom_probe(struct hid_device *hdev,
hid_warn(hdev, hid_warn(hdev,
"can't create sysfs speed attribute err: %d\n", ret); "can't create sysfs speed attribute err: %d\n", ret);
switch (hdev->product) { wdata->features = 0;
case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH: wacom_set_features(hdev, 1);
/* Set Wacom mode 2 with high reporting speed */
wacom_poke(hdev, 1); if (hdev->product == USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) {
break;
case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH:
sprintf(hdev->name, "%s", "Wacom Intuos4 WL"); sprintf(hdev->name, "%s", "Wacom Intuos4 WL");
wdata->features = 0; ret = wacom_initialize_leds(hdev);
wacom_set_features(hdev); if (ret) {
break; 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.properties = wacom_battery_props;
wdata->battery.num_properties = ARRAY_SIZE(wacom_battery_props); wdata->battery.num_properties = ARRAY_SIZE(wacom_battery_props);
wdata->battery.get_property = wacom_battery_get_property; 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); power_supply_powers(&wdata->ac, &hdev->dev);
#endif
return 0; return 0;
#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
err_ac: err_ac:
power_supply_unregister(&wdata->battery); power_supply_unregister(&wdata->battery);
err_battery: err_battery:
device_remove_file(&hdev->dev, &dev_attr_speed); device_remove_file(&hdev->dev, &dev_attr_speed);
hid_hw_stop(hdev); hid_hw_stop(hdev);
#endif destroy_leds:
wacom_destroy_leds(hdev);
err_free: err_free:
kfree(wdata); kfree(wdata);
return ret; return ret;
@ -646,16 +778,14 @@ err_free:
static void wacom_remove(struct hid_device *hdev) static void wacom_remove(struct hid_device *hdev)
{ {
#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
struct wacom_data *wdata = hid_get_drvdata(hdev); struct wacom_data *wdata = hid_get_drvdata(hdev);
#endif
wacom_destroy_leds(hdev);
device_remove_file(&hdev->dev, &dev_attr_speed); device_remove_file(&hdev->dev, &dev_attr_speed);
hid_hw_stop(hdev); hid_hw_stop(hdev);
#ifdef CONFIG_HID_WACOM_POWER_SUPPLY
power_supply_unregister(&wdata->battery); power_supply_unregister(&wdata->battery);
power_supply_unregister(&wdata->ac); power_supply_unregister(&wdata->ac);
#endif
kfree(hid_get_drvdata(hdev)); kfree(hid_get_drvdata(hdev));
} }
@ -693,5 +823,5 @@ static void __exit wacom_exit(void)
module_init(wacom_init); module_init(wacom_init);
module_exit(wacom_exit); module_exit(wacom_exit);
MODULE_DESCRIPTION("Driver for Wacom Graphire Bluetooth and Wacom Intuos4 WL");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@ -502,28 +502,146 @@ static __u8 media_tablet_14_1_inch_rdesc_fixed[] = {
0xC0 /* End Collection */ 0xC0 /* End Collection */
}; };
struct waltop_state { /*
u8 pressure0; * See Sirius Battery Free Tablet description, device and HID report descriptors
u8 pressure1; * 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, static int waltop_probe(struct hid_device *hdev,
const struct hid_device_id *id) const struct hid_device_id *id)
{ {
int ret; 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); ret = hid_parse(hdev);
if (ret) { if (ret) {
@ -539,7 +657,6 @@ static int waltop_probe(struct hid_device *hdev,
return 0; return 0;
err: err:
kfree(s);
return ret; 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); *rsize = sizeof(media_tablet_14_1_inch_rdesc_fixed);
} }
break; 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; 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, static int waltop_raw_event(struct hid_device *hdev, struct hid_report *report,
u8 *data, int size) u8 *data, int size)
{ {
/* If this is a pen input report of a tablet with PID 0038 */ /* If this is a pen input report */
if (hdev->product == USB_DEVICE_ID_WALTOP_PID_0038 && if (report->type == HID_INPUT_REPORT && report->id == 16 && size >= 8) {
report->type == HID_INPUT_REPORT &&
report->id == 16 &&
size == 8) {
struct waltop_state *s = hid_get_drvdata(hdev);
/* /*
* Ignore maximum pressure reported when a barrel button is * Ignore reported pressure when a barrel button is pressed,
* pressed. * because it is rarely correct.
*/ */
/* If a barrel button is pressed */ /* If a barrel button is pressed */
if ((data[1] & 0xF) > 1) { if ((data[1] & 0xF) > 1) {
/* Use the last known pressure */ /* Report zero pressure */
data[6] = s->pressure0; data[6] = 0;
data[7] = s->pressure1; data[7] = 0;
} else {
/* Remember reported pressure */
s->pressure0 = data[6];
s->pressure1 = data[7];
} }
} }
/* 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; return 0;
} }
static void waltop_remove(struct hid_device *hdev) static void waltop_remove(struct hid_device *hdev)
{ {
struct waltop_state *s = hid_get_drvdata(hdev);
hid_hw_stop(hdev); hid_hw_stop(hdev);
kfree(s);
} }
static const struct hid_device_id waltop_devices[] = { 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) }, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP,
USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH) }, 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); MODULE_DEVICE_TABLE(hid, waltop_devices);

View File

@ -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 * 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. * of both.
* If data is packed, then the 3rd byte is put first and slightly * If data is packed, then the 3rd byte is put first and slightly
* reordered. This allows to interleave packed and non-packed data to * 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) { if (packed) {
x = ir[1] << 2; x = ir[1] | ((ir[0] & 0x03) << 8);
y = ir[2] << 2; y = ir[2] | ((ir[0] & 0x0c) << 6);
x |= ir[0] & 0x3;
y |= (ir[0] >> 2) & 0x3;
} else { } else {
x = ir[0] << 2; x = ir[0] | ((ir[2] & 0x30) << 4);
y = ir[1] << 2; y = ir[1] | ((ir[2] & 0xc0) << 2);
x |= (ir[2] >> 4) & 0x3;
y |= (ir[2] >> 6) & 0x3;
} }
input_report_abs(wdata->ir, xid, x); input_report_abs(wdata->ir, xid, x);

View File

@ -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 ? len = list->buffer[list->tail].len > count ?
count : list->buffer[list->tail].len; count : list->buffer[list->tail].len;
if (copy_to_user(buffer, list->buffer[list->tail].value, len)) { if (list->buffer[list->tail].value) {
ret = -EFAULT; if (copy_to_user(buffer, list->buffer[list->tail].value, len)) {
goto out; ret = -EFAULT;
goto out;
}
ret = len;
} }
ret = len;
kfree(list->buffer[list->tail].value); kfree(list->buffer[list->tail].value);
list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1); list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1);
@ -437,19 +439,24 @@ static const struct file_operations hidraw_ops = {
.llseek = noop_llseek, .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 *dev = hid->hidraw;
struct hidraw_list *list; struct hidraw_list *list;
int ret = 0;
list_for_each_entry(list, &dev->list, node) { 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->buffer[list->head].len = len;
list->head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1); list->head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1);
kill_fasync(&list->fasync, SIGIO, POLL_IN); kill_fasync(&list->fasync, SIGIO, POLL_IN);
} }
wake_up_interruptible(&dev->wait); wake_up_interruptible(&dev->wait);
return ret;
} }
EXPORT_SYMBOL_GPL(hidraw_report_event); EXPORT_SYMBOL_GPL(hidraw_report_event);

View File

@ -28,6 +28,7 @@
#include <linux/input.h> #include <linux/input.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/string.h>
#include <linux/usb.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_bit(HID_REPORTED_IDLE, &usbhid->iofl) &&
!test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) { !test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) {
rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC); rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC);
if (rc != 0) if (rc != 0) {
clear_bit(HID_IN_RUNNING, &usbhid->iofl); 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); spin_unlock_irqrestore(&usbhid->lock, flags);
return rc; return rc;
@ -173,8 +179,10 @@ static void hid_io_error(struct hid_device *hid)
if (time_after(jiffies, usbhid->stop_retry)) { if (time_after(jiffies, usbhid->stop_retry)) {
/* Retries failed, so do a port reset */ /* Retries failed, so do a port reset unless we lack bandwidth*/
if (!test_and_set_bit(HID_RESET_PENDING, &usbhid->iofl)) { if (test_bit(HID_NO_BANDWIDTH, &usbhid->iofl)
&& !test_and_set_bit(HID_RESET_PENDING, &usbhid->iofl)) {
schedule_work(&usbhid->reset_work); schedule_work(&usbhid->reset_work);
goto done; 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) int usbhid_open(struct hid_device *hid)
{ {
struct usbhid_device *usbhid = hid->driver_data; struct usbhid_device *usbhid = hid->driver_data;
int res; int res = 0;
mutex_lock(&hid_open_mut); mutex_lock(&hid_open_mut);
if (!hid->open++) { if (!hid->open++) {
@ -757,17 +765,27 @@ int usbhid_open(struct hid_device *hid)
/* the device must be awake to reliably request remote wakeup */ /* the device must be awake to reliably request remote wakeup */
if (res < 0) { if (res < 0) {
hid->open--; hid->open--;
mutex_unlock(&hid_open_mut); res = -EIO;
return -EIO; goto done;
} }
usbhid->intf->needs_remote_wakeup = 1; usbhid->intf->needs_remote_wakeup = 1;
if (hid_start_in(hid)) res = hid_start_in(hid);
hid_io_error(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); usb_autopm_put_interface(usbhid->intf);
} }
done:
mutex_unlock(&hid_open_mut); mutex_unlock(&hid_open_mut);
return 0; return res;
} }
void usbhid_close(struct hid_device *hid) 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 usb_device *dev = interface_to_usbdev (intf);
struct hid_device *hid = usb_get_intfdata(intf); struct hid_device *hid = usb_get_intfdata(intf);
struct usbhid_device *usbhid = hid->driver_data; struct usbhid_device *usbhid = hid->driver_data;
struct usb_host_interface *interface = intf->cur_altsetting;
int status; 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); spin_lock_irq(&usbhid->lock);
clear_bit(HID_RESET_PENDING, &usbhid->iofl); clear_bit(HID_RESET_PENDING, &usbhid->iofl);
@ -1553,28 +1598,15 @@ static struct usb_driver hid_driver = {
.supports_autosuspend = 1, .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) struct usb_interface *usbhid_find_interface(int minor)
{ {
return usb_find_interface(&hid_driver, 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) static int __init hid_init(void)
{ {
int retval = -ENOMEM; int retval = -ENOMEM;
retval = hid_register_driver(&hid_usb_driver);
if (retval)
goto hid_register_fail;
retval = usbhid_quirks_init(quirks_param); retval = usbhid_quirks_init(quirks_param);
if (retval) if (retval)
goto usbhid_quirks_init_fail; goto usbhid_quirks_init_fail;
@ -1587,8 +1619,6 @@ static int __init hid_init(void)
usb_register_fail: usb_register_fail:
usbhid_quirks_exit(); usbhid_quirks_exit();
usbhid_quirks_init_fail: usbhid_quirks_init_fail:
hid_unregister_driver(&hid_usb_driver);
hid_register_fail:
return retval; return retval;
} }
@ -1596,7 +1626,6 @@ static void __exit hid_exit(void)
{ {
usb_deregister(&hid_driver); usb_deregister(&hid_driver);
usbhid_quirks_exit(); usbhid_quirks_exit();
hid_unregister_driver(&hid_usb_driver);
} }
module_init(hid_init); module_init(hid_init);

View File

@ -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_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_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_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, 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 }, { USB_VENDOR_ID_WISEGROUP_LTD2, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },

View File

@ -34,6 +34,7 @@
#include <linux/hid.h> #include <linux/hid.h>
#include <linux/hiddev.h> #include <linux/hiddev.h>
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/vmalloc.h>
#include "usbhid.h" #include "usbhid.h"
#ifdef CONFIG_USB_DYNAMIC_MINORS #ifdef CONFIG_USB_DYNAMIC_MINORS
@ -250,13 +251,13 @@ static int hiddev_release(struct inode * inode, struct file * file)
} else { } else {
mutex_unlock(&list->hiddev->existancelock); mutex_unlock(&list->hiddev->existancelock);
kfree(list->hiddev); kfree(list->hiddev);
kfree(list); vfree(list);
return 0; return 0;
} }
} }
mutex_unlock(&list->hiddev->existancelock); mutex_unlock(&list->hiddev->existancelock);
kfree(list); vfree(list);
return 0; return 0;
} }
@ -278,7 +279,7 @@ static int hiddev_open(struct inode *inode, struct file *file)
hid = usb_get_intfdata(intf); hid = usb_get_intfdata(intf);
hiddev = hid->hiddev; hiddev = hid->hiddev;
if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL))) if (!(list = vzalloc(sizeof(struct hiddev_list))))
return -ENOMEM; return -ENOMEM;
mutex_init(&list->thread_lock); mutex_init(&list->thread_lock);
list->hiddev = hiddev; list->hiddev = hiddev;
@ -322,7 +323,7 @@ bail_unlock:
mutex_unlock(&hiddev->existancelock); mutex_unlock(&hiddev->existancelock);
bail: bail:
file->private_data = NULL; file->private_data = NULL;
kfree(list); vfree(list);
return res; return res;
} }

View File

@ -55,6 +55,7 @@ struct usb_interface *usbhid_find_interface(int minor);
#define HID_STARTED 8 #define HID_STARTED 8
#define HID_REPORTED_IDLE 9 #define HID_REPORTED_IDLE 9
#define HID_KEYS_PRESSED 10 #define HID_KEYS_PRESSED 10
#define HID_NO_BANDWIDTH 11
/* /*
* USB-specific HID struct, to be pointed to * USB-specific HID struct, to be pointed to

View File

@ -317,13 +317,18 @@ struct hid_item {
#define HID_QUIRK_BADPAD 0x00000020 #define HID_QUIRK_BADPAD 0x00000020
#define HID_QUIRK_MULTI_INPUT 0x00000040 #define HID_QUIRK_MULTI_INPUT 0x00000040
#define HID_QUIRK_HIDINPUT_FORCE 0x00000080 #define HID_QUIRK_HIDINPUT_FORCE 0x00000080
#define HID_QUIRK_MULTITOUCH 0x00000100
#define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000 #define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000
#define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000 #define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000
#define HID_QUIRK_NO_INIT_REPORTS 0x20000000 #define HID_QUIRK_NO_INIT_REPORTS 0x20000000
#define HID_QUIRK_NO_IGNORE 0x40000000 #define HID_QUIRK_NO_IGNORE 0x40000000
#define HID_QUIRK_NO_INPUT_SYNC 0x80000000 #define HID_QUIRK_NO_INPUT_SYNC 0x80000000
/*
* HID device groups
*/
#define HID_GROUP_GENERIC 0x0001
#define HID_GROUP_MULTITOUCH 0x0002
/* /*
* This is the global environment of the parser. This information is * This is the global environment of the parser. This information is
* persistent for main-items. The global environment can be saved and * persistent for main-items. The global environment can be saved and
@ -467,6 +472,8 @@ struct hid_driver;
struct hid_ll_driver; struct hid_ll_driver;
struct hid_device { /* device report descriptor */ struct hid_device { /* device report descriptor */
__u8 *dev_rdesc;
unsigned dev_rsize;
__u8 *rdesc; __u8 *rdesc;
unsigned rsize; unsigned rsize;
struct hid_collection *collection; /* List of HID collections */ struct hid_collection *collection; /* List of HID collections */
@ -474,6 +481,7 @@ struct hid_device { /* device report descriptor */
unsigned maxcollection; /* Number of parsed collections */ unsigned maxcollection; /* Number of parsed collections */
unsigned maxapplication; /* Number of applications */ unsigned maxapplication; /* Number of applications */
__u16 bus; /* BUS ID */ __u16 bus; /* BUS ID */
__u16 group; /* Report group */
__u32 vendor; /* Vendor ID */ __u32 vendor; /* Vendor ID */
__u32 product; /* Product ID */ __u32 product; /* Product ID */
__u32 version; /* HID version */ __u32 version; /* HID version */
@ -578,12 +586,12 @@ struct hid_descriptor {
struct hid_class_descriptor desc[1]; struct hid_class_descriptor desc[1];
} __attribute__ ((packed)); } __attribute__ ((packed));
#define HID_DEVICE(b, ven, prod) \ #define HID_DEVICE(b, g, ven, prod) \
.bus = (b), \ .bus = (b), .group = (g), .vendor = (ven), .product = (prod)
.vendor = (ven), .product = (prod) #define HID_USB_DEVICE(ven, prod) \
.bus = BUS_USB, .vendor = (ven), .product = (prod)
#define HID_USB_DEVICE(ven, prod) HID_DEVICE(BUS_USB, ven, prod) #define HID_BLUETOOTH_DEVICE(ven, prod) \
#define HID_BLUETOOTH_DEVICE(ven, prod) HID_DEVICE(BUS_BLUETOOTH, ven, prod) .bus = BUS_BLUETOOTH, .vendor = (ven), .product = (prod)
#define HID_REPORT_ID(rep) \ #define HID_REPORT_ID(rep) \
.report_type = (rep) .report_type = (rep)
@ -735,6 +743,7 @@ void hid_output_report(struct hid_report *report, __u8 *data);
struct hid_device *hid_allocate_device(void); struct hid_device *hid_allocate_device(void);
struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id); struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id);
int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size); int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size);
int hid_open_report(struct hid_device *device);
int hid_check_keys_pressed(struct hid_device *hid); int hid_check_keys_pressed(struct hid_device *hid);
int hid_connect(struct hid_device *hid, unsigned int connect_mask); int hid_connect(struct hid_device *hid, unsigned int connect_mask);
void hid_disconnect(struct hid_device *hid); void hid_disconnect(struct hid_device *hid);
@ -805,16 +814,7 @@ static inline void hid_map_usage_clear(struct hid_input *hidinput,
*/ */
static inline int __must_check hid_parse(struct hid_device *hdev) static inline int __must_check hid_parse(struct hid_device *hdev)
{ {
int ret; return hid_open_report(hdev);
if (hdev->status & HID_STAT_PARSED)
return 0;
ret = hdev->ll_driver->parse(hdev);
if (!ret)
hdev->status |= HID_STAT_PARSED;
return ret;
} }
/** /**
@ -896,7 +896,7 @@ static inline int hid_hw_power(struct hid_device *hdev, int level)
return hdev->ll_driver->power ? hdev->ll_driver->power(hdev, level) : 0; return hdev->ll_driver->power ? hdev->ll_driver->power(hdev, level) : 0;
} }
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); int interrupt);
extern int hid_generic_init(void); extern int hid_generic_init(void);

View File

@ -76,13 +76,13 @@ struct hidraw_list {
#ifdef CONFIG_HIDRAW #ifdef CONFIG_HIDRAW
int hidraw_init(void); int hidraw_init(void);
void hidraw_exit(void); void hidraw_exit(void);
void hidraw_report_event(struct hid_device *, u8 *, int); int hidraw_report_event(struct hid_device *, u8 *, int);
int hidraw_connect(struct hid_device *); int hidraw_connect(struct hid_device *);
void hidraw_disconnect(struct hid_device *); void hidraw_disconnect(struct hid_device *);
#else #else
static inline int hidraw_init(void) { return 0; } static inline int hidraw_init(void) { return 0; }
static inline void hidraw_exit(void) { } static inline void hidraw_exit(void) { }
static inline void hidraw_report_event(struct hid_device *hid, u8 *data, int len) { } static inline int hidraw_report_event(struct hid_device *hid, u8 *data, int len) { return 0; }
static inline int hidraw_connect(struct hid_device *hid) { return -1; } static inline int hidraw_connect(struct hid_device *hid) { return -1; }
static inline void hidraw_disconnect(struct hid_device *hid) { } static inline void hidraw_disconnect(struct hid_device *hid) { }
#endif #endif

View File

@ -132,10 +132,12 @@ struct usb_device_id {
#define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200 #define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200
#define HID_ANY_ID (~0) #define HID_ANY_ID (~0)
#define HID_BUS_ANY 0xffff
#define HID_GROUP_ANY 0x0000
struct hid_device_id { struct hid_device_id {
__u16 bus; __u16 bus;
__u16 pad1; __u16 group;
__u32 vendor; __u32 vendor;
__u32 product; __u32 product;
kernel_ulong_t driver_data kernel_ulong_t driver_data

View File

@ -1195,41 +1195,16 @@ int hidp_get_conninfo(struct hidp_conninfo *ci)
return err; return err;
} }
static const struct hid_device_id hidp_table[] = {
{ HID_BLUETOOTH_DEVICE(HID_ANY_ID, HID_ANY_ID) },
{ }
};
static struct hid_driver hidp_driver = {
.name = "generic-bluetooth",
.id_table = hidp_table,
};
static int __init hidp_init(void) static int __init hidp_init(void)
{ {
int ret;
BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION); BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION);
ret = hid_register_driver(&hidp_driver); return hidp_init_sockets();
if (ret)
goto err;
ret = hidp_init_sockets();
if (ret)
goto err_drv;
return 0;
err_drv:
hid_unregister_driver(&hidp_driver);
err:
return ret;
} }
static void __exit hidp_exit(void) static void __exit hidp_exit(void)
{ {
hidp_cleanup_sockets(); hidp_cleanup_sockets();
hid_unregister_driver(&hidp_driver);
} }
module_init(hidp_init); module_init(hidp_init);

View File

@ -336,10 +336,13 @@ static int do_hid_entry(const char *filename,
struct hid_device_id *id, char *alias) struct hid_device_id *id, char *alias)
{ {
id->bus = TO_NATIVE(id->bus); id->bus = TO_NATIVE(id->bus);
id->group = TO_NATIVE(id->group);
id->vendor = TO_NATIVE(id->vendor); id->vendor = TO_NATIVE(id->vendor);
id->product = TO_NATIVE(id->product); id->product = TO_NATIVE(id->product);
sprintf(alias, "hid:b%04X", id->bus); sprintf(alias, "hid:");
ADD(alias, "b", id->bus != HID_BUS_ANY, id->bus);
ADD(alias, "g", id->group != HID_GROUP_ANY, id->group);
ADD(alias, "v", id->vendor != HID_ANY_ID, id->vendor); ADD(alias, "v", id->vendor != HID_ANY_ID, id->vendor);
ADD(alias, "p", id->product != HID_ANY_ID, id->product); ADD(alias, "p", id->product != HID_ANY_ID, id->product);