Commit 5489375d authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid:
  HID: completely remove apple mightymouse from blacklist
  HID: support larger reports than 64 bytes in hiddev
  HID: local function should be static
  HID: ignore Philips IEEE802.15.4 RF Dongle
  HID: ignore all recent SoundGraph iMON devices
  HID: fix memory leak on error patch in debug code
  HID: fix overrun in quirks initialization
  HID: Drop NULL test on list_entry result
  HID: driver for Twinhan USB 6253:0100 remote control
  HID: adding __init/__exit macros to module init/exit functions
  HID: add rumble support for Thrustmaster Dual Trigger 3-in-1
  HID: ntrig tool separation and pen usages
  HID: Avoid double spin_lock_init on usbhid->lock
  HID: add force feedback support for Logitech WingMan Formula Force GP
  HID: Support new variants of Samsung USB IR receiver (0419:0001)
  HID: fix memory leak on error path in debug code
  HID: fix debugfs build with !CONFIG_DEBUG_FS
  HID: use debugfs for events/reports dumping
  HID: use debugfs for report dumping descriptor
parents 355bbd8c 8123e8f7
...@@ -31,21 +31,6 @@ config HID ...@@ -31,21 +31,6 @@ config HID
If unsure, say Y. If unsure, say Y.
config HID_DEBUG
bool "HID debugging support"
default y
depends on HID
---help---
This option lets the HID layer output diagnostics about its internal
state, resolve HID usages, dump HID fields, etc. Individual HID drivers
use this debugging facility to output information about individual HID
devices, etc.
This feature is useful for those who are either debugging the HID parser
or any HID hardware device.
If unsure, say Y.
config HIDRAW config HIDRAW
bool "/dev/hidraw raw HID device support" bool "/dev/hidraw raw HID device support"
depends on HID depends on HID
...@@ -152,6 +137,13 @@ config HID_GYRATION ...@@ -152,6 +137,13 @@ config HID_GYRATION
---help--- ---help---
Support for Gyration remote control. Support for Gyration remote control.
config HID_TWINHAN
tristate "Twinhan" if EMBEDDED
depends on USB_HID
default !EMBEDDED
---help---
Support for Twinhan IR remote control.
config HID_KENSINGTON config HID_KENSINGTON
tristate "Kensington" if EMBEDDED tristate "Kensington" if EMBEDDED
depends on USB_HID depends on USB_HID
...@@ -176,6 +168,7 @@ config LOGITECH_FF ...@@ -176,6 +168,7 @@ config LOGITECH_FF
- Logitech WingMan Cordless RumblePad 2 - Logitech WingMan Cordless RumblePad 2
- Logitech WingMan Force 3D - Logitech WingMan Force 3D
- Logitech Formula Force EX - Logitech Formula Force EX
- Logitech WingMan Formula Force GP
- Logitech MOMO Force wheel - Logitech MOMO Force wheel
and if you want to enable force feedback for them. and if you want to enable force feedback for them.
...@@ -314,9 +307,9 @@ config THRUSTMASTER_FF ...@@ -314,9 +307,9 @@ config THRUSTMASTER_FF
depends on HID_THRUSTMASTER depends on HID_THRUSTMASTER
select INPUT_FF_MEMLESS select INPUT_FF_MEMLESS
---help--- ---help---
Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or 3,
a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel and a THRUSTMASTER Dual Trigger 3-in-1 or a THRUSTMASTER Ferrari GT
want to enable force feedback support for it. Rumble Force or Force Feedback Wheel.
config HID_WACOM config HID_WACOM
tristate "Wacom Bluetooth devices support" if EMBEDDED tristate "Wacom Bluetooth devices support" if EMBEDDED
......
...@@ -3,9 +3,12 @@ ...@@ -3,9 +3,12 @@
# #
hid-objs := hid-core.o hid-input.o hid-objs := hid-core.o hid-input.o
ifdef CONFIG_DEBUG_FS
hid-objs += hid-debug.o
endif
obj-$(CONFIG_HID) += hid.o obj-$(CONFIG_HID) += hid.o
hid-$(CONFIG_HID_DEBUG) += hid-debug.o
hid-$(CONFIG_HIDRAW) += hidraw.o hid-$(CONFIG_HIDRAW) += hidraw.o
hid-logitech-objs := hid-lg.o hid-logitech-objs := hid-lg.o
...@@ -40,6 +43,7 @@ obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o ...@@ -40,6 +43,7 @@ obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o
obj-$(CONFIG_HID_GREENASIA) += hid-gaff.o obj-$(CONFIG_HID_GREENASIA) += hid-gaff.o
obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o
obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o
obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o
obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o
obj-$(CONFIG_HID_WACOM) += hid-wacom.o obj-$(CONFIG_HID_WACOM) += hid-wacom.o
......
...@@ -145,12 +145,12 @@ static struct hid_driver a4_driver = { ...@@ -145,12 +145,12 @@ static struct hid_driver a4_driver = {
.remove = a4_remove, .remove = a4_remove,
}; };
static int a4_init(void) static int __init a4_init(void)
{ {
return hid_register_driver(&a4_driver); return hid_register_driver(&a4_driver);
} }
static void a4_exit(void) static void __exit a4_exit(void)
{ {
hid_unregister_driver(&a4_driver); hid_unregister_driver(&a4_driver);
} }
......
...@@ -451,7 +451,7 @@ static struct hid_driver apple_driver = { ...@@ -451,7 +451,7 @@ static struct hid_driver apple_driver = {
.input_mapped = apple_input_mapped, .input_mapped = apple_input_mapped,
}; };
static int apple_init(void) static int __init apple_init(void)
{ {
int ret; int ret;
...@@ -462,7 +462,7 @@ static int apple_init(void) ...@@ -462,7 +462,7 @@ static int apple_init(void)
return ret; return ret;
} }
static void apple_exit(void) static void __exit apple_exit(void)
{ {
hid_unregister_driver(&apple_driver); hid_unregister_driver(&apple_driver);
} }
......
...@@ -88,12 +88,12 @@ static struct hid_driver belkin_driver = { ...@@ -88,12 +88,12 @@ static struct hid_driver belkin_driver = {
.probe = belkin_probe, .probe = belkin_probe,
}; };
static int belkin_init(void) static int __init belkin_init(void)
{ {
return hid_register_driver(&belkin_driver); return hid_register_driver(&belkin_driver);
} }
static void belkin_exit(void) static void __exit belkin_exit(void)
{ {
hid_unregister_driver(&belkin_driver); hid_unregister_driver(&belkin_driver);
} }
......
...@@ -70,12 +70,12 @@ static struct hid_driver ch_driver = { ...@@ -70,12 +70,12 @@ static struct hid_driver ch_driver = {
.input_mapping = ch_input_mapping, .input_mapping = ch_input_mapping,
}; };
static int ch_init(void) static int __init ch_init(void)
{ {
return hid_register_driver(&ch_driver); return hid_register_driver(&ch_driver);
} }
static void ch_exit(void) static void __exit ch_exit(void)
{ {
hid_unregister_driver(&ch_driver); hid_unregister_driver(&ch_driver);
} }
......
...@@ -63,12 +63,12 @@ static struct hid_driver ch_driver = { ...@@ -63,12 +63,12 @@ static struct hid_driver ch_driver = {
.input_mapping = ch_input_mapping, .input_mapping = ch_input_mapping,
}; };
static int ch_init(void) static int __init ch_init(void)
{ {
return hid_register_driver(&ch_driver); return hid_register_driver(&ch_driver);
} }
static void ch_exit(void) static void __exit ch_exit(void)
{ {
hid_unregister_driver(&ch_driver); hid_unregister_driver(&ch_driver);
} }
......
...@@ -44,12 +44,10 @@ ...@@ -44,12 +44,10 @@
#define DRIVER_DESC "HID core driver" #define DRIVER_DESC "HID core driver"
#define DRIVER_LICENSE "GPL" #define DRIVER_LICENSE "GPL"
#ifdef CONFIG_HID_DEBUG
int hid_debug = 0; int hid_debug = 0;
module_param_named(debug, hid_debug, int, 0600); module_param_named(debug, hid_debug, int, 0600);
MODULE_PARM_DESC(debug, "HID debugging (0=off, 1=probing info, 2=continuous data dumping)"); MODULE_PARM_DESC(debug, "toggle HID debugging messages");
EXPORT_SYMBOL_GPL(hid_debug); EXPORT_SYMBOL_GPL(hid_debug);
#endif
/* /*
* Register a new report for a device. * Register a new report for a device.
...@@ -861,7 +859,7 @@ static void hid_process_event(struct hid_device *hid, struct hid_field *field, ...@@ -861,7 +859,7 @@ static void hid_process_event(struct hid_device *hid, struct hid_field *field,
struct hid_driver *hdrv = hid->driver; struct hid_driver *hdrv = hid->driver;
int ret; int ret;
hid_dump_input(usage, value); hid_dump_input(hid, usage, value);
if (hdrv && hdrv->event && hid_match_usage(hid, usage)) { if (hdrv && hdrv->event && hid_match_usage(hid, usage)) {
ret = hdrv->event(hid, field, usage, value); ret = hdrv->event(hid, field, usage, value);
...@@ -983,11 +981,10 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value) ...@@ -983,11 +981,10 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value)
{ {
unsigned size = field->report_size; unsigned size = field->report_size;
hid_dump_input(field->usage + offset, value); hid_dump_input(field->report->device, field->usage + offset, value);
if (offset >= field->report_count) { if (offset >= field->report_count) {
dbg_hid("offset (%d) exceeds report_count (%d)\n", offset, field->report_count); dbg_hid("offset (%d) exceeds report_count (%d)\n", offset, field->report_count);
hid_dump_field(field, 8);
return -1; return -1;
} }
if (field->logical_minimum < 0) { if (field->logical_minimum < 0) {
...@@ -1078,6 +1075,7 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i ...@@ -1078,6 +1075,7 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i
struct hid_report_enum *report_enum; struct hid_report_enum *report_enum;
struct hid_driver *hdrv; struct hid_driver *hdrv;
struct hid_report *report; struct hid_report *report;
char *buf;
unsigned int i; unsigned int i;
int ret; int ret;
...@@ -1091,18 +1089,38 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i ...@@ -1091,18 +1089,38 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i
return -1; return -1;
} }
dbg_hid("report (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un"); buf = kmalloc(sizeof(char) * HID_DEBUG_BUFSIZE,
interrupt ? GFP_ATOMIC : GFP_KERNEL);
if (!buf) {
report = hid_get_report(report_enum, data); report = hid_get_report(report_enum, data);
if (!report) goto nomem;
}
snprintf(buf, HID_DEBUG_BUFSIZE - 1,
"\nreport (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un");
hid_debug_event(hid, buf);
report = hid_get_report(report_enum, data);
if (!report) {
kfree(buf);
return -1; return -1;
}
/* dump the report */ /* dump the report */
dbg_hid("report %d (size %u) = ", report->id, size); snprintf(buf, HID_DEBUG_BUFSIZE - 1,
for (i = 0; i < size; i++) "report %d (size %u) = ", report->id, size);
dbg_hid_line(" %02x", data[i]); hid_debug_event(hid, buf);
dbg_hid_line("\n"); for (i = 0; i < size; i++) {
snprintf(buf, HID_DEBUG_BUFSIZE - 1,
" %02x", data[i]);
hid_debug_event(hid, buf);
}
hid_debug_event(hid, "\n");
kfree(buf);
nomem:
if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) { if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) {
ret = hdrv->raw_event(hid, report, data, size); ret = hdrv->raw_event(hid, report, data, size);
if (ret != 0) if (ret != 0)
...@@ -1292,6 +1310,7 @@ static const struct hid_device_id hid_blacklist[] = { ...@@ -1292,6 +1310,7 @@ static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) },
...@@ -1311,15 +1330,17 @@ static const struct hid_device_id hid_blacklist[] = { ...@@ -1311,15 +1330,17 @@ static const struct hid_device_id hid_blacklist[] = {
{ 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) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb323) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb324) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) },
{ 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_TWINHAN, USB_DEVICE_ID_TWINHAN_IR_REMOTE) },
{ 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_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
{ 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) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 0x030c) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) },
{ } { }
}; };
...@@ -1622,12 +1643,8 @@ static const struct hid_device_id hid_ignore_list[] = { ...@@ -1622,12 +1643,8 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0002) }, { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0002) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0003) }, { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0003) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) }, { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_POWERCOM, USB_DEVICE_ID_POWERCOM_UPS) }, { HID_USB_DEVICE(USB_VENDOR_ID_POWERCOM, USB_DEVICE_ID_POWERCOM_UPS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD4) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD5) },
{ HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY1) }, { HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY2) }, { HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) }, { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) },
...@@ -1694,6 +1711,11 @@ static bool hid_ignore(struct hid_device *hdev) ...@@ -1694,6 +1711,11 @@ static bool hid_ignore(struct hid_device *hdev)
hdev->product <= USB_DEVICE_ID_LOGITECH_HARMONY_LAST) hdev->product <= USB_DEVICE_ID_LOGITECH_HARMONY_LAST)
return true; return true;
break; break;
case USB_VENDOR_ID_SOUNDGRAPH:
if (hdev->product >= USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST &&
hdev->product <= USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST)
return true;
break;
} }
if (hdev->type == HID_TYPE_USBMOUSE && if (hdev->type == HID_TYPE_USBMOUSE &&
...@@ -1725,6 +1747,8 @@ int hid_add_device(struct hid_device *hdev) ...@@ -1725,6 +1747,8 @@ int hid_add_device(struct hid_device *hdev)
if (!ret) if (!ret)
hdev->status |= HID_STAT_ADDED; hdev->status |= HID_STAT_ADDED;
hid_debug_register(hdev, dev_name(&hdev->dev));
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(hid_add_device); EXPORT_SYMBOL_GPL(hid_add_device);
...@@ -1761,6 +1785,9 @@ struct hid_device *hid_allocate_device(void) ...@@ -1761,6 +1785,9 @@ struct hid_device *hid_allocate_device(void)
for (i = 0; i < HID_REPORT_TYPES; i++) for (i = 0; i < HID_REPORT_TYPES; i++)
INIT_LIST_HEAD(&hdev->report_enum[i].report_list); INIT_LIST_HEAD(&hdev->report_enum[i].report_list);
init_waitqueue_head(&hdev->debug_wait);
INIT_LIST_HEAD(&hdev->debug_list);
return hdev; return hdev;
err: err:
put_device(&hdev->dev); put_device(&hdev->dev);
...@@ -1772,6 +1799,7 @@ static void hid_remove_device(struct hid_device *hdev) ...@@ -1772,6 +1799,7 @@ static void hid_remove_device(struct hid_device *hdev)
{ {
if (hdev->status & HID_STAT_ADDED) { if (hdev->status & HID_STAT_ADDED) {
device_del(&hdev->dev); device_del(&hdev->dev);
hid_debug_unregister(hdev);
hdev->status &= ~HID_STAT_ADDED; hdev->status &= ~HID_STAT_ADDED;
} }
} }
...@@ -1847,6 +1875,10 @@ static int __init hid_init(void) ...@@ -1847,6 +1875,10 @@ static int __init hid_init(void)
{ {
int ret; int ret;
if (hid_debug)
printk(KERN_WARNING "HID: hid_debug is now used solely for parser and driver debugging.\n"
"HID: debugfs is now used for inspecting the device (report descriptor, reports)\n");
ret = bus_register(&hid_bus_type); ret = bus_register(&hid_bus_type);
if (ret) { if (ret) {
printk(KERN_ERR "HID: can't register hid bus\n"); printk(KERN_ERR "HID: can't register hid bus\n");
...@@ -1857,6 +1889,8 @@ static int __init hid_init(void) ...@@ -1857,6 +1889,8 @@ static int __init hid_init(void)
if (ret) if (ret)
goto err_bus; goto err_bus;
hid_debug_init();
return 0; return 0;
err_bus: err_bus:
bus_unregister(&hid_bus_type); bus_unregister(&hid_bus_type);
...@@ -1866,6 +1900,7 @@ static int __init hid_init(void) ...@@ -1866,6 +1900,7 @@ static int __init hid_init(void)
static void __exit hid_exit(void) static void __exit hid_exit(void)
{ {
hid_debug_exit();
hidraw_exit(); hidraw_exit();
bus_unregister(&hid_bus_type); bus_unregister(&hid_bus_type);
} }
......
...@@ -141,12 +141,12 @@ static struct hid_driver cp_driver = { ...@@ -141,12 +141,12 @@ static struct hid_driver cp_driver = {
.probe = cp_probe, .probe = cp_probe,
}; };
static int cp_init(void) static int __init cp_init(void)
{ {
return hid_register_driver(&cp_driver); return hid_register_driver(&cp_driver);
} }
static void cp_exit(void) static void __exit cp_exit(void)
{ {
hid_unregister_driver(&cp_driver); hid_unregister_driver(&cp_driver);
} }
......
/* /*
* (c) 1999 Andreas Gal <gal@cs.uni-magdeburg.de> * (c) 1999 Andreas Gal <gal@cs.uni-magdeburg.de>
* (c) 2000-2001 Vojtech Pavlik <vojtech@ucw.cz> * (c) 2000-2001 Vojtech Pavlik <vojtech@ucw.cz>
* (c) 2007 Jiri Kosina * (c) 2007-2009 Jiri Kosina
* *
* Some debug stuff for the HID parser. * HID debugging support
*/ */
/* /*
...@@ -26,9 +26,17 @@ ...@@ -26,9 +26,17 @@
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/ */
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/poll.h>
#include <linux/hid.h> #include <linux/hid.h>
#include <linux/hid-debug.h> #include <linux/hid-debug.h>
static struct dentry *hid_debug_root;
struct hid_usage_entry { struct hid_usage_entry {
unsigned page; unsigned page;
unsigned usage; unsigned usage;
...@@ -339,72 +347,120 @@ static const struct hid_usage_entry hid_usage_table[] = { ...@@ -339,72 +347,120 @@ static const struct hid_usage_entry hid_usage_table[] = {
{ 0, 0, NULL } { 0, 0, NULL }
}; };
static void resolv_usage_page(unsigned page) { /* Either output directly into simple seq_file, or (if f == NULL)
* allocate a separate buffer that will then be passed to the 'events'
* ringbuffer.
*
* This is because these functions can be called both for "one-shot"
* "rdesc" while resolving, or for blocking "events".
*
* This holds both for resolv_usage_page() and hid_resolv_usage().
*/
static char *resolv_usage_page(unsigned page, struct seq_file *f) {
const struct hid_usage_entry *p; const struct hid_usage_entry *p;
char *buf = NULL;
if (!f) {
buf = kzalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_ATOMIC);
if (!buf)
return ERR_PTR(-ENOMEM);
}
for (p = hid_usage_table; p->description; p++) for (p = hid_usage_table; p->description; p++)
if (p->page == page) { if (p->page == page) {
printk("%s", p->description); if (!f) {
return; snprintf(buf, HID_DEBUG_BUFSIZE, "%s",
p->description);
return buf;
}
else {
seq_printf(f, "%s", p->description);
return NULL;
} }
printk("%04x", page); }
if (!f)
snprintf(buf, HID_DEBUG_BUFSIZE, "%04x", page);
else
seq_printf(f, "%04x", page);
return buf;
} }
void hid_resolv_usage(unsigned usage) { char *hid_resolv_usage(unsigned usage, struct seq_file *f) {
const struct hid_usage_entry *p; const struct hid_usage_entry *p;
char *buf = NULL;
int len = 0;
if (!hid_debug) buf = resolv_usage_page(usage >> 16, f);
return; if (IS_ERR(buf)) {
printk(KERN_ERR "error allocating HID debug buffer\n");
return NULL;
}
resolv_usage_page(usage >> 16);
printk("."); if (!f) {
len = strlen(buf);
snprintf(buf+len, max(0, HID_DEBUG_BUFSIZE - len), ".");
len++;
}
else {
seq_printf(f, ".");
}
for (p = hid_usage_table; p->description; p++) for (p = hid_usage_table; p->description; p++)
if (p->page == (usage >> 16)) { if (p->page == (usage >> 16)) {
for(++p; p->description && p->usage != 0; p++) for(++p; p->description && p->usage != 0; p++)
if (p->usage == (usage & 0xffff)) { if (p->usage == (usage & 0xffff)) {
printk("%s", p->description); if (!f)
return; snprintf(buf + len,
max(0,HID_DEBUG_BUFSIZE - len - 1),
"%s", p->description);
else
seq_printf(f,
"%s",
p->description);
return buf;
} }
break; break;
} }
printk("%04x", usage & 0xffff); if (!f)
snprintf(buf + len, max(0, HID_DEBUG_BUFSIZE - len - 1),
"%04x", usage & 0xffff);
else
seq_printf(f, "%04x", usage & 0xffff);
return buf;
} }
EXPORT_SYMBOL_GPL(hid_resolv_usage); EXPORT_SYMBOL_GPL(hid_resolv_usage);
static void tab(int n) { static void tab(int n, struct seq_file *f) {
printk(KERN_DEBUG "%*s", n, ""); seq_printf(f, "%*s", n, "");
} }
void hid_dump_field(struct hid_field *field, int n) { void hid_dump_field(struct hid_field *field, int n, struct seq_file *f) {
int j; int j;
if (!hid_debug)
return;
if (field->physical) { if (field->physical) {
tab(n); tab(n, f);
printk("Physical("); seq_printf(f, "Physical(");
hid_resolv_usage(field->physical); printk(")\n"); hid_resolv_usage(field->physical, f); seq_printf(f, ")\n");
} }
if (field->logical) { if (field->logical) {
tab(n); tab(n, f);
printk("Logical("); seq_printf(f, "Logical(");
hid_resolv_usage(field->logical); printk(")\n"); hid_resolv_usage(field->logical, f); seq_printf(f, ")\n");
} }
tab(n); printk("Usage(%d)\n", field->maxusage); tab(n, f); seq_printf(f, "Usage(%d)\n", field->maxusage);
for (j = 0; j < field->maxusage; j++) { for (j = 0; j < field->maxusage; j++) {
tab(n+2); hid_resolv_usage(field->usage[j].hid); printk("\n"); tab(n+2, f); hid_resolv_usage(field->usage[j].hid, f); seq_printf(f, "\n");
} }
if (field->logical_minimum != field->logical_maximum) { if (field->logical_minimum != field->logical_maximum) {
tab(n); printk("Logical Minimum(%d)\n", field->logical_minimum); tab(n, f); seq_printf(f, "Logical Minimum(%d)\n", field->logical_minimum);
tab(n); printk("Logical Maximum(%d)\n", field->logical_maximum); tab(n, f); seq_printf(f, "Logical Maximum(%d)\n", field->logical_maximum);
} }
if (field->physical_minimum != field->physical_maximum) { if (field->physical_minimum != field->physical_maximum) {
tab(n); printk("Physical Minimum(%d)\n", field->physical_minimum); tab(n, f); seq_printf(f, "Physical Minimum(%d)\n", field->physical_minimum);
tab(n); printk("Physical Maximum(%d)\n", field->physical_maximum); tab(n, f); seq_printf(f, "Physical Maximum(%d)\n", field->physical_maximum);
} }
if (field->unit_exponent) { if (field->unit_exponent) {
tab(n); printk("Unit Exponent(%d)\n", field->unit_exponent); tab(n, f); seq_printf(f, "Unit Exponent(%d)\n", field->unit_exponent);
} }
if (field->unit) { if (field->unit) {
static const char *systems[5] = { "None", "SI Linear", "SI Rotation", "English Linear", "English Rotation" }; static const char *systems[5] = { "None", "SI Linear", "SI Rotation", "English Linear", "English Rotation" };
...@@ -425,77 +481,75 @@ void hid_dump_field(struct hid_field *field, int n) { ...@@ -425,77 +481,75 @@ void hid_dump_field(struct hid_field *field, int n) {
data >>= 4; data >>= 4;
if(sys > 4) { if(sys > 4) {
tab(n); printk("Unit(Invalid)\n"); tab(n, f); seq_printf(f, "Unit(Invalid)\n");
} }
else { else {
int earlier_unit = 0; int earlier_unit = 0;
tab(n); printk("Unit(%s : ", systems[sys]); tab(n, f); seq_printf(f, "Unit(%s : ", systems[sys]);
for (i=1 ; i<sizeof(__u32)*2 ; i++) { for (i=1 ; i<sizeof(__u32)*2 ; i++) {
char nibble = data & 0xf; char nibble = data & 0xf;
data >>= 4; data >>= 4;
if (nibble != 0) { if (nibble != 0) {
if(earlier_unit++ > 0) if(earlier_unit++ > 0)
printk("*"); seq_printf(f, "*");
printk("%s", units[sys][i]); seq_printf(f, "%s", units[sys][i]);
if(nibble != 1) { if(nibble != 1) {
/* This is a _signed_ nibble(!) */ /* This is a _signed_ nibble(!) */
int val = nibble & 0x7; int val = nibble & 0x7;
if(nibble & 0x08) if(nibble & 0x08)
val = -((0x7 & ~val) +1); val = -((0x7 & ~val) +1);
printk("^%d", val); seq_printf(f, "^%d", val);
} }
} }
} }
printk(")\n"); seq_printf(f, ")\n");
} }
} }
tab(n); printk("Report Size(%u)\n", field->report_size); tab(n, f); seq_printf(f, "Report Size(%u)\n", field->report_size);
tab(n); printk("Report Count(%u)\n", field->report_count); tab(n, f); seq_printf(f, "Report Count(%u)\n", field->report_count);
tab(n); printk("Report Offset(%u)\n", field->report_offset); tab(n, f); seq_printf(f, "Report Offset(%u)\n", field->report_offset);
tab(n); printk("Flags( "); tab(n, f); seq_printf(f, "Flags( ");
j = field->flags; j = field->flags;
printk("%s", HID_MAIN_ITEM_CONSTANT & j ? "Constant " : ""); seq_printf(f, "%s", HID_MAIN_ITEM_CONSTANT & j ? "Constant " : "");
printk("%s", HID_MAIN_ITEM_VARIABLE & j ? "Variable " : "Array "); seq_printf(f, "%s", HID_MAIN_ITEM_VARIABLE & j ? "Variable " : "Array ");
printk("%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute "); seq_printf(f, "%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute ");
printk("%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : ""); seq_printf(f, "%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : "");
printk("%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : ""); seq_printf(f, "%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : "");
printk("%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPreferredState " : ""); seq_printf(f, "%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPreferredState " : "");
printk("%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : ""); seq_printf(f, "%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : "");
printk("%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : ""); seq_printf(f, "%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : "");
printk("%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : ""); seq_printf(f, "%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : "");
printk(")\n"); seq_printf(f, ")\n");
} }
EXPORT_SYMBOL_GPL(hid_dump_field); EXPORT_SYMBOL_GPL(hid_dump_field);
void hid_dump_device(struct hid_device *device) { void hid_dump_device(struct hid_device *device, struct seq_file *f)
{
struct hid_report_enum *report_enum; struct hid_report_enum *report_enum;
struct hid_report *report; struct hid_report *report;
struct list_head *list; struct list_head *list;
unsigned i,k; unsigned i,k;
static const char *table[] = {"INPUT", "OUTPUT", "FEATURE"}; static const char *table[] = {"INPUT", "OUTPUT", "FEATURE"};
if (!hid_debug)
return;
for (i = 0; i < HID_REPORT_TYPES; i++) { for (i = 0; i < HID_REPORT_TYPES; i++) {
report_enum = device->report_enum + i; report_enum = device->report_enum + i;
list = report_enum->report_list.next; list = report_enum->report_list.next;
while (list != &report_enum->report_list) { while (list != &report_enum->report_list) {
report = (struct hid_report *) list; report = (struct hid_report *) list;
tab(2); tab(2, f);
printk("%s", table[i]); seq_printf(f, "%s", table[i]);
if (report->id) if (report->id)
printk("(%d)", report->id); seq_printf(f, "(%d)", report->id);
printk("[%s]", table[report->type]); seq_printf(f, "[%s]", table[report->type]);
printk("\n"); seq_printf(f, "\n");
for (k = 0; k < report->maxfield; k++) { for (k = 0; k < report->maxfield; k++) {
tab(4); tab(4, f);
printk("Field(%d)\n", k); seq_printf(f, "Field(%d)\n", k);
hid_dump_field(report->field[k], 6); hid_dump_field(report->field[k], 6, f);
} }
list = list->next; list = list->next;
} }
...@@ -503,13 +557,37 @@ void hid_dump_device(struct hid_device *device) { ...@@ -503,13 +557,37 @@ void hid_dump_device(struct hid_device *device) {
} }
EXPORT_SYMBOL_GPL(hid_dump_device); EXPORT_SYMBOL_GPL(hid_dump_device);
void hid_dump_input(struct hid_usage *usage, __s32 value) { /* enqueue string to 'events' ring buffer */
if (hid_debug < 2) void hid_debug_event(struct hid_device *hdev, char *buf)
{
int i;
struct hid_debug_list *list;
list_for_each_entry(list, &hdev->debug_list, node) {
for (i = 0; i <= strlen(buf); i++)
list->hid_debug_buf[(list->tail + i) % (HID_DEBUG_BUFSIZE - 1)] =
buf[i];
list->tail = (list->tail + i) % (HID_DEBUG_BUFSIZE - 1);
}
}
EXPORT_SYMBOL_GPL(hid_debug_event);
void hid_dump_input(struct hid_device *hdev, struct hid_usage *usage, __s32 value)
{
char *buf;
int len;
buf = hid_resolv_usage(usage->hid, NULL);
if (!buf)
return; return;
len = strlen(buf);
snprintf(buf + len, HID_DEBUG_BUFSIZE - len - 1, " = %d\n", value);
hid_debug_event(hdev, buf);
kfree(buf);
wake_up_interruptible(&hdev->debug_wait);
printk(KERN_DEBUG "hid-debug: input ");
hid_resolv_usage(usage->hid);
printk(" = %d\n", value);
} }
EXPORT_SYMBOL_GPL(hid_dump_input); EXPORT_SYMBOL_GPL(hid_dump_input);
...@@ -786,12 +864,221 @@ static const char **names[EV_MAX + 1] = { ...@@ -786,12 +864,221 @@ static const char **names[EV_MAX + 1] = {
[EV_SND] = sounds, [EV_REP] = repeats, [EV_SND] = sounds, [EV_REP] = repeats,
}; };
void hid_resolv_event(__u8 type, __u16 code) { void hid_resolv_event(__u8 type, __u16 code, struct seq_file *f) {
if (!hid_debug) seq_printf(f, "%s.%s", events[type] ? events[type] : "?",
return;
printk("%s.%s", events[type] ? events[type] : "?",
names[type] ? (names[type][code] ? names[type][code] : "?") : "?"); names[type] ? (names[type][code] ? names[type][code] : "?") : "?");
} }
EXPORT_SYMBOL_GPL(hid_resolv_event);
void hid_dump_input_mapping(struct hid_device *hid, struct seq_file *f)
{
int i, j, k;
struct hid_report *report;
struct hid_usage *usage;
for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) {
list_for_each_entry(report, &hid->report_enum[k].report_list, list) {
for (i = 0; i < report->maxfield; i++) {
for ( j = 0; j < report->field[i]->maxusage; j++) {
usage = report->field[i]->usage + j;
hid_resolv_usage(usage->hid, f);
seq_printf(f, " ---> ");
hid_resolv_event(usage->type, usage->code, f);
seq_printf(f, "\n");
}
}
}
}
}
static int hid_debug_rdesc_show(struct seq_file *f, void *p)
{
struct hid_device *hdev = f->private;
int i;
/* dump HID report descriptor */
for (i = 0; i < hdev->rsize; i++)
seq_printf(f, "%02x ", hdev->rdesc[i]);
seq_printf(f, "\n\n");
/* dump parsed data and input mappings */
hid_dump_device(hdev, f);
seq_printf(f, "\n");
hid_dump_input_mapping(hdev, f);
return 0;
}
static int hid_debug_rdesc_open(struct inode *inode, struct file *file)
{
return single_open(file, hid_debug_rdesc_show, inode->i_private);
}
static int hid_debug_events_open(struct inode *inode, struct file *file)
{
int err = 0;
struct hid_debug_list *list;
if (!(list = kzalloc(sizeof(struct hid_debug_list), GFP_KERNEL))) {
err = -ENOMEM;
goto out;
}
if (!(list->hid_debug_buf = kzalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_KERNEL))) {
err = -ENOMEM;
kfree(list);
goto out;
}
list->hdev = (struct hid_device *) inode->i_private;
file->private_data = list;
mutex_init(&list->read_mutex);
list_add_tail(&list->node, &list->hdev->debug_list);
out:
return err;
}
static ssize_t hid_debug_events_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct hid_debug_list *list = file->private_data;
int ret = 0, len;
DECLARE_WAITQUEUE(wait, current);
while (ret == 0) {
mutex_lock(&list->read_mutex);
if (list->head == list->tail) {
add_wait_queue(&list->hdev->debug_wait, &wait);
set_current_state(TASK_INTERRUPTIBLE);
while (list->head == list->tail) {
if (file->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
break;
}
if (signal_pending(current)) {
ret = -ERESTARTSYS;
break;
}
if (!list->hdev || !list->hdev->debug) {
ret = -EIO;
break;
}
/* allow O_NONBLOCK from other threads */
mutex_unlock(&list->read_mutex);
schedule();
mutex_lock(&list->read_mutex);
set_current_state(TASK_INTERRUPTIBLE);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&list->hdev->debug_wait, &wait);
}
if (ret)
goto out;
/* pass the ringbuffer contents to userspace */
copy_rest:
if (list->tail == list->head)
goto out;
if (list->tail > list->head) {
len = list->tail - list->head;
if (copy_to_user(buffer + ret, &list->hid_debug_buf[list->head], len)) {
ret = -EFAULT;
goto out;
}
ret += len;
list->head += len;
} else {
len = HID_DEBUG_BUFSIZE - list->head;
if (copy_to_user(buffer, &list->hid_debug_buf[list->head], len)) {
ret = -EFAULT;
goto out;
}
list->head = 0;
ret += len;
goto copy_rest;
}
}
out:
mutex_unlock(&list->read_mutex);
return ret;
}
static unsigned int hid_debug_events_poll(struct file *file, poll_table *wait)
{
struct hid_debug_list *list = file->private_data;
poll_wait(file, &list->hdev->debug_wait, wait);
if (list->head != list->tail)
return POLLIN | POLLRDNORM;
if (!list->hdev->debug)
return POLLERR | POLLHUP;
return 0;
}
static int hid_debug_events_release(struct inode *inode, struct file *file)
{
struct hid_debug_list *list = file->private_data;
list_del(&list->node);
kfree(list->hid_debug_buf);
kfree(list);
return 0;
}
static const struct file_operations hid_debug_rdesc_fops = {
.open = hid_debug_rdesc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static const struct file_operations hid_debug_events_fops = {
.owner = THIS_MODULE,
.open = hid_debug_events_open,
.read = hid_debug_events_read,
.poll = hid_debug_events_poll,
.release = hid_debug_events_release,
};
void hid_debug_register(struct hid_device *hdev, const char *name)
{
hdev->debug_dir = debugfs_create_dir(name, hid_debug_root);
hdev->debug_rdesc = debugfs_create_file("rdesc", 0400,
hdev->debug_dir, hdev, &hid_debug_rdesc_fops);
hdev->debug_events = debugfs_create_file("events", 0400,
hdev->debug_dir, hdev, &hid_debug_events_fops);
hdev->debug = 1;
}
void hid_debug_unregister(struct hid_device *hdev)
{
hdev->debug = 0;
wake_up_interruptible(&hdev->debug_wait);
debugfs_remove(hdev->debug_rdesc);
debugfs_remove(hdev->debug_events);
debugfs_remove(hdev->debug_dir);
}
void hid_debug_init(void)
{
hid_debug_root = debugfs_create_dir("hid", NULL);
}
void hid_debug_exit(void)
{
debugfs_remove_recursive(hid_debug_root);
}
...@@ -78,12 +78,12 @@ static struct hid_driver ez_driver = { ...@@ -78,12 +78,12 @@ static struct hid_driver ez_driver = {
.event = ez_event, .event = ez_event,
}; };
static int ez_init(void) static int __init ez_init(void)
{ {
return hid_register_driver(&ez_driver); return hid_register_driver(&ez_driver);
} }
static void ez_exit(void) static void __exit ez_exit(void)
{ {
hid_unregister_driver(&ez_driver); hid_unregister_driver(&ez_driver);
} }
......
...@@ -81,12 +81,12 @@ static struct hid_driver gyration_driver = { ...@@ -81,12 +81,12 @@ static struct hid_driver gyration_driver = {
.event = gyration_event, .event = gyration_event,
}; };
static int gyration_init(void) static int __init gyration_init(void)
{ {
return hid_register_driver(&gyration_driver); return hid_register_driver(&gyration_driver);
} }
static void gyration_exit(void) static void __exit gyration_exit(void)
{ {
hid_unregister_driver(&gyration_driver); hid_unregister_driver(&gyration_driver);
} }
......
...@@ -296,6 +296,7 @@ ...@@ -296,6 +296,7 @@
#define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283 #define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283
#define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO 0xc286 #define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO 0xc286
#define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294 #define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294
#define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG 0xc293
#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295 #define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295
#define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299 #define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299
#define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a #define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a
...@@ -359,6 +360,9 @@ ...@@ -359,6 +360,9 @@
#define USB_VENDOR_ID_PETALYNX 0x18b1 #define USB_VENDOR_ID_PETALYNX 0x18b1
#define USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE 0x0037 #define USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE 0x0037
#define USB_VENDOR_ID_PHILIPS 0x0471
#define USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE 0x0617
#define USB_VENDOR_ID_PLAYDOTCOM 0x0b43 #define USB_VENDOR_ID_PLAYDOTCOM 0x0b43
#define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII 0x0003 #define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII 0x0003
...@@ -376,11 +380,8 @@ ...@@ -376,11 +380,8 @@
#define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268 #define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268
#define USB_VENDOR_ID_SOUNDGRAPH 0x15c2 #define USB_VENDOR_ID_SOUNDGRAPH 0x15c2
#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD 0x0038 #define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST 0x0034
#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD2 0x0036 #define USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST 0x0046
#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD3 0x0034
#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD4 0x0044
#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD5 0x0045
#define USB_VENDOR_ID_SUN 0x0430 #define USB_VENDOR_ID_SUN 0x0430
#define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab #define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab
...@@ -403,6 +404,9 @@ ...@@ -403,6 +404,9 @@
#define USB_VENDOR_ID_TURBOX 0x062a #define USB_VENDOR_ID_TURBOX 0x062a
#define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201 #define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201
#define USB_VENDOR_ID_TWINHAN 0x6253
#define USB_DEVICE_ID_TWINHAN_IR_REMOTE 0x0100
#define USB_VENDOR_ID_UCLOGIC 0x5543 #define USB_VENDOR_ID_UCLOGIC 0x5543
#define USB_DEVICE_ID_UCLOGIC_TABLET_PF1209 0x0042 #define USB_DEVICE_ID_UCLOGIC_TABLET_PF1209 0x0042
......
...@@ -159,17 +159,12 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel ...@@ -159,17 +159,12 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
field->hidinput = hidinput; field->hidinput = hidinput;
dbg_hid("Mapping: ");
hid_resolv_usage(usage->hid);
dbg_hid_line(" ---> ");
if (field->flags & HID_MAIN_ITEM_CONSTANT) if (field->flags & HID_MAIN_ITEM_CONSTANT)
goto ignore; goto ignore;
/* only LED usages are supported in output fields */ /* only LED usages are supported in output fields */
if (field->report_type == HID_OUTPUT_REPORT && if (field->report_type == HID_OUTPUT_REPORT &&
(usage->hid & HID_USAGE_PAGE) != HID_UP_LED) { (usage->hid & HID_USAGE_PAGE) != HID_UP_LED) {
dbg_hid_line(" [non-LED output field] ");
goto ignore; goto ignore;
} }
...@@ -561,15 +556,9 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel ...@@ -561,15 +556,9 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
set_bit(MSC_SCAN, input->mscbit); set_bit(MSC_SCAN, input->mscbit);
} }
hid_resolv_event(usage->type, usage->code);
dbg_hid_line("\n");
return;
ignore: ignore:
dbg_hid_line("IGNORED\n");
return; return;
} }
void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value)
......
...@@ -48,12 +48,12 @@ static struct hid_driver ks_driver = { ...@@ -48,12 +48,12 @@ static struct hid_driver ks_driver = {
.input_mapping = ks_input_mapping, .input_mapping = ks_input_mapping,
}; };
static int ks_init(void) static int __init ks_init(void)
{ {
return hid_register_driver(&ks_driver); return hid_register_driver(&ks_driver);
} }
static void ks_exit(void) static void __exit ks_exit(void)
{ {
hid_unregister_driver(&ks_driver); hid_unregister_driver(&ks_driver);
} }
......
...@@ -54,12 +54,12 @@ static struct hid_driver kye_driver = { ...@@ -54,12 +54,12 @@ static struct hid_driver kye_driver = {
.report_fixup = kye_report_fixup, .report_fixup = kye_report_fixup,
}; };
static int kye_init(void) static int __init kye_init(void)
{ {
return hid_register_driver(&kye_driver); return hid_register_driver(&kye_driver);
} }
static void kye_exit(void) static void __exit kye_exit(void)
{ {
hid_unregister_driver(&kye_driver); hid_unregister_driver(&kye_driver);
} }
......
...@@ -299,6 +299,8 @@ static const struct hid_device_id lg_devices[] = { ...@@ -299,6 +299,8 @@ static const struct hid_device_id lg_devices[] = {
.driver_data = LG_FF }, .driver_data = LG_FF },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL), { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
.driver_data = LG_FF }, .driver_data = LG_FF },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ),
.driver_data = LG_FF },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2), { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
.driver_data = LG_FF2 }, .driver_data = LG_FF2 },
{ } { }
...@@ -315,12 +317,12 @@ static struct hid_driver lg_driver = { ...@@ -315,12 +317,12 @@ static struct hid_driver lg_driver = {
.probe = lg_probe, .probe = lg_probe,
}; };
static int lg_init(void) static int __init lg_init(void)
{ {
return hid_register_driver(&lg_driver); return hid_register_driver(&lg_driver);
} }
static void lg_exit(void) static void __exit lg_exit(void)
{ {
hid_unregister_driver(&lg_driver); hid_unregister_driver(&lg_driver);
} }
......
...@@ -67,6 +67,7 @@ static const struct dev_type devices[] = { ...@@ -67,6 +67,7 @@ static const struct dev_type devices[] = {
{ 0x046d, 0xc219, ff_rumble }, { 0x046d, 0xc219, ff_rumble },
{ 0x046d, 0xc283, ff_joystick }, { 0x046d, 0xc283, ff_joystick },
{ 0x046d, 0xc286, ff_joystick_ac }, { 0x046d, 0xc286, ff_joystick_ac },
{ 0x046d, 0xc293, ff_joystick },
{ 0x046d, 0xc294, ff_wheel }, { 0x046d, 0xc294, ff_wheel },
{ 0x046d, 0xc295, ff_joystick }, { 0x046d, 0xc295, ff_joystick },
{ 0x046d, 0xca03, ff_wheel }, { 0x046d, 0xca03, ff_wheel },
...@@ -150,11 +151,6 @@ int lgff_init(struct hid_device* hid) ...@@ -150,11 +151,6 @@ int lgff_init(struct hid_device* hid)
/* Check that the report looks ok */ /* Check that the report looks ok */
report = list_entry(report_list->next, struct hid_report, list); report = list_entry(report_list->next, struct hid_report, list);
if (!report) {
err_hid("NULL output report");
return -1;
}
field = report->field[0]; field = report->field[0];
if (!field) { if (!field) {
err_hid("NULL field"); err_hid("NULL field");
......
...@@ -197,12 +197,12 @@ static struct hid_driver ms_driver = { ...@@ -197,12 +197,12 @@ static struct hid_driver ms_driver = {
.probe = ms_probe, .probe = ms_probe,
}; };
static int ms_init(void) static int __init ms_init(void)
{ {
return hid_register_driver(&ms_driver); return hid_register_driver(&ms_driver);
} }
static void ms_exit(void) static void __exit ms_exit(void)
{ {
hid_unregister_driver(&ms_driver); hid_unregister_driver(&ms_driver);
} }
......
...@@ -65,12 +65,12 @@ static struct hid_driver mr_driver = { ...@@ -65,12 +65,12 @@ static struct hid_driver mr_driver = {
.input_mapping = mr_input_mapping, .input_mapping = mr_input_mapping,
}; };
static int mr_init(void) static int __init mr_init(void)
{ {
return hid_register_driver(&mr_driver); return hid_register_driver(&mr_driver);
} }
static void mr_exit(void) static void __exit mr_exit(void)
{ {
hid_unregister_driver(&mr_driver); hid_unregister_driver(&mr_driver);
} }
......
...@@ -27,6 +27,9 @@ ...@@ -27,6 +27,9 @@
struct ntrig_data { struct ntrig_data {
__s32 x, y, id, w, h; __s32 x, y, id, w, h;
char reading_a_point, found_contact_id; char reading_a_point, found_contact_id;
char pen_active;
char finger_active;
char inverted;
}; };
/* /*
...@@ -63,10 +66,7 @@ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi, ...@@ -63,10 +66,7 @@ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi,
case HID_UP_DIGITIZER: case HID_UP_DIGITIZER:
switch (usage->hid) { switch (usage->hid) {
/* we do not want to map these for now */ /* we do not want to map these for now */
case HID_DG_INVERT: /* value is always 0 */
case HID_DG_ERASER: /* value is always 0 */
case HID_DG_CONTACTID: /* value is useless */ case HID_DG_CONTACTID: /* value is useless */
case HID_DG_BARRELSWITCH: /* doubtful */
case HID_DG_INPUTMODE: case HID_DG_INPUTMODE:
case HID_DG_DEVICEINDEX: case HID_DG_DEVICEINDEX:
case HID_DG_CONTACTCOUNT: case HID_DG_CONTACTCOUNT:
...@@ -125,6 +125,18 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field, ...@@ -125,6 +125,18 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
if (hid->claimed & HID_CLAIMED_INPUT) { if (hid->claimed & HID_CLAIMED_INPUT) {
switch (usage->hid) { switch (usage->hid) {
case HID_DG_INRANGE:
if (field->application & 0x3)
nd->pen_active = (value != 0);
else
nd->finger_active = (value != 0);
return 0;
case HID_DG_INVERT:
nd->inverted = value;
return 0;
case HID_GD_X: case HID_GD_X:
nd->x = value; nd->x = value;
nd->reading_a_point = 1; nd->reading_a_point = 1;
...@@ -147,7 +159,11 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field, ...@@ -147,7 +159,11 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
* report received in a finger event. We want * report received in a finger event. We want
* to emit a normal (X, Y) position * to emit a normal (X, Y) position
*/ */
if (! nd->found_contact_id) { if (!nd->found_contact_id) {
if (nd->pen_active && nd->finger_active) {
input_report_key(input, BTN_TOOL_DOUBLETAP, 0);
input_report_key(input, BTN_TOOL_DOUBLETAP, 1);
}
input_event(input, EV_ABS, ABS_X, nd->x); input_event(input, EV_ABS, ABS_X, nd->x);
input_event(input, EV_ABS, ABS_Y, nd->y); input_event(input, EV_ABS, ABS_Y, nd->y);
} }
...@@ -159,6 +175,14 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field, ...@@ -159,6 +175,14 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
* to emit a normal (X, Y) position * to emit a normal (X, Y) position
*/ */
if (! nd->found_contact_id) { if (! nd->found_contact_id) {
if (nd->pen_active && nd->finger_active) {
input_report_key(input,
nd->inverted ? BTN_TOOL_RUBBER : BTN_TOOL_PEN
, 0);
input_report_key(input,
nd->inverted ? BTN_TOOL_RUBBER : BTN_TOOL_PEN
, 1);
}
input_event(input, EV_ABS, ABS_X, nd->x); input_event(input, EV_ABS, ABS_X, nd->x);
input_event(input, EV_ABS, ABS_Y, nd->y); input_event(input, EV_ABS, ABS_Y, nd->y);
input_event(input, EV_ABS, ABS_PRESSURE, value); input_event(input, EV_ABS, ABS_PRESSURE, value);
...@@ -233,6 +257,7 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -233,6 +257,7 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (ret) if (ret)
kfree (nd); kfree (nd);
return ret; return ret;
} }
...@@ -265,12 +290,12 @@ static struct hid_driver ntrig_driver = { ...@@ -265,12 +290,12 @@ static struct hid_driver ntrig_driver = {
.event = ntrig_event, .event = ntrig_event,
}; };
static int ntrig_init(void) static int __init ntrig_init(void)
{ {
return hid_register_driver(&ntrig_driver); return hid_register_driver(&ntrig_driver);
} }
static void ntrig_exit(void) static void __exit ntrig_exit(void)
{ {
hid_unregister_driver(&ntrig_driver); hid_unregister_driver(&ntrig_driver);
} }
......
...@@ -105,12 +105,12 @@ static struct hid_driver pl_driver = { ...@@ -105,12 +105,12 @@ static struct hid_driver pl_driver = {
.probe = pl_probe, .probe = pl_probe,
}; };
static int pl_init(void) static int __init pl_init(void)
{ {
return hid_register_driver(&pl_driver); return hid_register_driver(&pl_driver);
} }
static void pl_exit(void) static void __exit pl_exit(void)
{ {
hid_unregister_driver(&pl_driver); hid_unregister_driver(&pl_driver);
} }
......
...@@ -217,12 +217,12 @@ static struct hid_driver pl_driver = { ...@@ -217,12 +217,12 @@ static struct hid_driver pl_driver = {
.probe = pl_probe, .probe = pl_probe,
}; };
static int pl_init(void) static int __init pl_init(void)
{ {
return hid_register_driver(&pl_driver); return hid_register_driver(&pl_driver);
} }
static void pl_exit(void) static void __exit pl_exit(void)
{ {
hid_unregister_driver(&pl_driver); hid_unregister_driver(&pl_driver);
} }
......
...@@ -25,25 +25,48 @@ ...@@ -25,25 +25,48 @@
/* /*
* Samsung IrDA remote controller (reports as Cypress USB Mouse). * Samsung IrDA remote controller (reports as Cypress USB Mouse).
* *
* There are several variants for 0419:0001:
*
* 1. 184 byte report descriptor
* Vendor specific report #4 has a size of 48 bit, * Vendor specific report #4 has a size of 48 bit,
* and therefore is not accepted when inspecting the descriptors. * and therefore is not accepted when inspecting the descriptors.
* As a workaround we reinterpret the report as: * As a workaround we reinterpret the report as:
* Variable type, count 6, size 8 bit, log. maximum 255 * Variable type, count 6, size 8 bit, log. maximum 255
* The burden to reconstruct the data is moved into user space. * The burden to reconstruct the data is moved into user space.
*
* 2. 203 byte report descriptor
* Report #4 has an array field with logical range 0..18 instead of 1..15.
*
* 3. 135 byte report descriptor
* Report #4 has an array field with logical range 0..17 instead of 1..14.
*/ */
static void samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc, static void samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int rsize) unsigned int rsize)
{ {
if (rsize >= 182 && rdesc[175] == 0x25 && rdesc[176] == 0x40 && if (rsize == 184 && rdesc[175] == 0x25 && rdesc[176] == 0x40 &&
rdesc[177] == 0x75 && rdesc[178] == 0x30 && rdesc[177] == 0x75 && rdesc[178] == 0x30 &&
rdesc[179] == 0x95 && rdesc[180] == 0x01 && rdesc[179] == 0x95 && rdesc[180] == 0x01 &&
rdesc[182] == 0x40) { rdesc[182] == 0x40) {
dev_info(&hdev->dev, "fixing up Samsung IrDA report " dev_info(&hdev->dev, "fixing up Samsung IrDA %d byte report "
"descriptor\n"); "descriptor\n", 184);
rdesc[176] = 0xff; rdesc[176] = 0xff;
rdesc[178] = 0x08; rdesc[178] = 0x08;
rdesc[180] = 0x06; rdesc[180] = 0x06;
rdesc[182] = 0x42; rdesc[182] = 0x42;
} else
if (rsize == 203 && rdesc[192] == 0x15 && rdesc[193] == 0x0 &&
rdesc[194] == 0x25 && rdesc[195] == 0x12) {
dev_info(&hdev->dev, "fixing up Samsung IrDA %d byte report "
"descriptor\n", 203);
rdesc[193] = 0x1;
rdesc[195] = 0xf;
} else
if (rsize == 135 && rdesc[124] == 0x15 && rdesc[125] == 0x0 &&
rdesc[126] == 0x25 && rdesc[127] == 0x11) {
dev_info(&hdev->dev, "fixing up Samsung IrDA %d byte report "
"descriptor\n", 135);
rdesc[125] = 0x1;
rdesc[127] = 0xe;
} }
} }
...@@ -51,6 +74,7 @@ static int samsung_probe(struct hid_device *hdev, ...@@ -51,6 +74,7 @@ static int samsung_probe(struct hid_device *hdev,
const struct hid_device_id *id) const struct hid_device_id *id)
{ {
int ret; int ret;
unsigned int cmask = HID_CONNECT_DEFAULT;
ret = hid_parse(hdev); ret = hid_parse(hdev);
if (ret) { if (ret) {
...@@ -58,8 +82,13 @@ static int samsung_probe(struct hid_device *hdev, ...@@ -58,8 +82,13 @@ static int samsung_probe(struct hid_device *hdev,
goto err_free; goto err_free;
} }
ret = hid_hw_start(hdev, (HID_CONNECT_DEFAULT & ~HID_CONNECT_HIDINPUT) | if (hdev->rsize == 184) {
HID_CONNECT_HIDDEV_FORCE); /* disable hidinput, force hiddev */
cmask = (cmask & ~HID_CONNECT_HIDINPUT) |
HID_CONNECT_HIDDEV_FORCE;
}
ret = hid_hw_start(hdev, cmask);
if (ret) { if (ret) {
dev_err(&hdev->dev, "hw start failed\n"); dev_err(&hdev->dev, "hw start failed\n");
goto err_free; goto err_free;
...@@ -83,12 +112,12 @@ static struct hid_driver samsung_driver = { ...@@ -83,12 +112,12 @@ static struct hid_driver samsung_driver = {
.probe = samsung_probe, .probe = samsung_probe,
}; };
static int samsung_init(void) static int __init samsung_init(void)
{ {
return hid_register_driver(&samsung_driver); return hid_register_driver(&samsung_driver);
} }
static void samsung_exit(void) static void __exit samsung_exit(void)
{ {
hid_unregister_driver(&samsung_driver); hid_unregister_driver(&samsung_driver);
} }
......
...@@ -163,12 +163,12 @@ static struct hid_driver sjoy_driver = { ...@@ -163,12 +163,12 @@ static struct hid_driver sjoy_driver = {
.probe = sjoy_probe, .probe = sjoy_probe,
}; };
static int sjoy_init(void) static int __init sjoy_init(void)
{ {
return hid_register_driver(&sjoy_driver); return hid_register_driver(&sjoy_driver);
} }
static void sjoy_exit(void) static void __exit sjoy_exit(void)
{ {
hid_unregister_driver(&sjoy_driver); hid_unregister_driver(&sjoy_driver);
} }
......
...@@ -135,12 +135,12 @@ static struct hid_driver sony_driver = { ...@@ -135,12 +135,12 @@ static struct hid_driver sony_driver = {
.report_fixup = sony_report_fixup, .report_fixup = sony_report_fixup,
}; };
static int sony_init(void) static int __init sony_init(void)
{ {
return hid_register_driver(&sony_driver); return hid_register_driver(&sony_driver);
} }
static void sony_exit(void) static void __exit sony_exit(void)
{ {
hid_unregister_driver(&sony_driver); hid_unregister_driver(&sony_driver);
} }
......
...@@ -65,12 +65,12 @@ static struct hid_driver sp_driver = { ...@@ -65,12 +65,12 @@ static struct hid_driver sp_driver = {
.input_mapping = sp_input_mapping, .input_mapping = sp_input_mapping,
}; };
static int sp_init(void) static int __init sp_init(void)
{ {
return hid_register_driver(&sp_driver); return hid_register_driver(&sp_driver);
} }
static void sp_exit(void) static void __exit sp_exit(void)
{ {
hid_unregister_driver(&sp_driver); hid_unregister_driver(&sp_driver);
} }
......
...@@ -243,7 +243,11 @@ static int tm_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -243,7 +243,11 @@ static int tm_probe(struct hid_device *hdev, const struct hid_device_id *id)
static const struct hid_device_id tm_devices[] = { static const struct hid_device_id tm_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300), { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300),
.driver_data = (unsigned long)ff_rumble }, .driver_data = (unsigned long)ff_rumble },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304), { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304), /* FireStorm Dual Power 2 (and 3) */
.driver_data = (unsigned long)ff_rumble },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb323), /* Dual Trigger 3-in-1 (PC Mode) */
.driver_data = (unsigned long)ff_rumble },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb324), /* Dual Trigger 3-in-1 (PS3 Mode) */
.driver_data = (unsigned long)ff_rumble }, .driver_data = (unsigned long)ff_rumble },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651), /* FGT Rumble Force Wheel */ { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651), /* FGT Rumble Force Wheel */
.driver_data = (unsigned long)ff_rumble }, .driver_data = (unsigned long)ff_rumble },
...@@ -259,12 +263,12 @@ static struct hid_driver tm_driver = { ...@@ -259,12 +263,12 @@ static struct hid_driver tm_driver = {
.probe = tm_probe, .probe = tm_probe,
}; };
static int tm_init(void) static int __init tm_init(void)
{ {
return hid_register_driver(&tm_driver); return hid_register_driver(&tm_driver);
} }
static void tm_exit(void) static void __exit tm_exit(void)
{ {
hid_unregister_driver(&tm_driver); hid_unregister_driver(&tm_driver);
} }
......
...@@ -60,12 +60,12 @@ static struct hid_driver ts_driver = { ...@@ -60,12 +60,12 @@ static struct hid_driver ts_driver = {
.input_mapping = ts_input_mapping, .input_mapping = ts_input_mapping,
}; };
static int ts_init(void) static int __init ts_init(void)
{ {
return hid_register_driver(&ts_driver); return hid_register_driver(&ts_driver);
} }
static void ts_exit(void) static void __exit ts_exit(void)
{ {
hid_unregister_driver(&ts_driver); hid_unregister_driver(&ts_driver);
} }
......
/*
* HID driver for TwinHan IR remote control
*
* Based on hid-gyration.c
*
* Copyright (c) 2009 Bruno Prmont <bonbons@linux-vserver.org>
*/
/*
* 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.
*/
#include <linux/device.h>
#include <linux/input.h>
#include <linux/hid.h>
#include <linux/module.h>
#include "hid-ids.h"
/* Remote control key layout + listing:
*
* Full Screen Power
* KEY_SCREEN KEY_POWER2
*
* 1 2 3
* KEY_NUMERIC_1 KEY_NUMERIC_2 KEY_NUMERIC_3
*
* 4 5 6
* KEY_NUMERIC_4 KEY_NUMERIC_5 KEY_NUMERIC_6
*
* 7 8 9
* KEY_NUMERIC_7 KEY_NUMERIC_8 KEY_NUMERIC_9
*
* REC 0 Favorite
* KEY_RECORD KEY_NUMERIC_0 KEY_FAVORITES
*
* Rewind Forward
* KEY_REWIND CH+ KEY_FORWARD
* KEY_CHANNELUP
*
* VOL- > VOL+
* KEY_VOLUMEDOWN KEY_PLAY KEY_VOLUMEUP
*
* CH-
* KEY_CHANNELDOWN
* Recall Stop
* KEY_RESTART KEY_STOP
*
* Timeshift/Pause Mute Cancel
* KEY_PAUSE KEY_MUTE KEY_CANCEL
*
* Capture Preview EPG
* KEY_PRINT KEY_PROGRAM KEY_EPG
*
* Record List Tab Teletext
* KEY_LIST KEY_TAB KEY_TEXT
*/
#define th_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
EV_KEY, (c))
static int twinhan_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
if ((usage->hid & HID_USAGE_PAGE) != HID_UP_KEYBOARD)
return 0;
switch (usage->hid & HID_USAGE) {
/* Map all keys from Twinhan Remote */
case 0x004: th_map_key_clear(KEY_TEXT); break;
case 0x006: th_map_key_clear(KEY_RESTART); break;
case 0x008: th_map_key_clear(KEY_EPG); break;
case 0x00c: th_map_key_clear(KEY_REWIND); break;
case 0x00e: th_map_key_clear(KEY_PROGRAM); break;
case 0x00f: th_map_key_clear(KEY_LIST); break;
case 0x010: th_map_key_clear(KEY_MUTE); break;
case 0x011: th_map_key_clear(KEY_FORWARD); break;
case 0x013: th_map_key_clear(KEY_PRINT); break;
case 0x017: th_map_key_clear(KEY_PAUSE); break;
case 0x019: th_map_key_clear(KEY_FAVORITES); break;
case 0x01d: th_map_key_clear(KEY_SCREEN); break;
case 0x01e: th_map_key_clear(KEY_NUMERIC_1); break;
case 0x01f: th_map_key_clear(KEY_NUMERIC_2); break;
case 0x020: th_map_key_clear(KEY_NUMERIC_3); break;
case 0x021: th_map_key_clear(KEY_NUMERIC_4); break;
case 0x022: th_map_key_clear(KEY_NUMERIC_5); break;
case 0x023: th_map_key_clear(KEY_NUMERIC_6); break;
case 0x024: th_map_key_clear(KEY_NUMERIC_7); break;
case 0x025: th_map_key_clear(KEY_NUMERIC_8); break;
case 0x026: th_map_key_clear(KEY_NUMERIC_9); break;
case 0x027: th_map_key_clear(KEY_NUMERIC_0); break;
case 0x028: th_map_key_clear(KEY_PLAY); break;
case 0x029: th_map_key_clear(KEY_CANCEL); break;
case 0x02b: th_map_key_clear(KEY_TAB); break;
/* Power = 0x0e0 + 0x0e1 + 0x0e2 + 0x03f */
case 0x03f: th_map_key_clear(KEY_POWER2); break;
case 0x04a: th_map_key_clear(KEY_RECORD); break;
case 0x04b: th_map_key_clear(KEY_CHANNELUP); break;
case 0x04d: th_map_key_clear(KEY_STOP); break;
case 0x04e: th_map_key_clear(KEY_CHANNELDOWN); break;
/* Volume down = 0x0e1 + 0x051 */
case 0x051: th_map_key_clear(KEY_VOLUMEDOWN); break;
/* Volume up = 0x0e1 + 0x052 */
case 0x052: th_map_key_clear(KEY_VOLUMEUP); break;
/* Kill the extra keys used for multi-key "power" and "volume" keys
* as well as continuously to release CTRL,ALT,META,... keys */
case 0x0e0:
case 0x0e1:
case 0x0e2:
case 0x0e3:
case 0x0e4:
case 0x0e5:
case 0x0e6:
case 0x0e7:
default:
return -1;
}
return 1;
}
static const struct hid_device_id twinhan_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_TWINHAN, USB_DEVICE_ID_TWINHAN_IR_REMOTE) },
{ }
};
MODULE_DEVICE_TABLE(hid, twinhan_devices);
static struct hid_driver twinhan_driver = {
.name = "twinhan",
.id_table = twinhan_devices,
.input_mapping = twinhan_input_mapping,
};
static int twinhan_init(void)
{
return hid_register_driver(&twinhan_driver);
}
static void twinhan_exit(void)
{
hid_unregister_driver(&twinhan_driver);
}
module_init(twinhan_init);
module_exit(twinhan_exit);
MODULE_LICENSE("GPL");
...@@ -237,7 +237,7 @@ static struct hid_driver wacom_driver = { ...@@ -237,7 +237,7 @@ static struct hid_driver wacom_driver = {
.raw_event = wacom_raw_event, .raw_event = wacom_raw_event,
}; };
static int wacom_init(void) static int __init wacom_init(void)
{ {
int ret; int ret;
...@@ -248,7 +248,7 @@ static int wacom_init(void) ...@@ -248,7 +248,7 @@ static int wacom_init(void)
return ret; return ret;
} }
static void wacom_exit(void) static void __exit wacom_exit(void)
{ {
hid_unregister_driver(&wacom_driver); hid_unregister_driver(&wacom_driver);
} }
......
...@@ -152,12 +152,12 @@ static struct hid_driver zp_driver = { ...@@ -152,12 +152,12 @@ static struct hid_driver zp_driver = {
.probe = zp_probe, .probe = zp_probe,
}; };
static int zp_init(void) static int __init zp_init(void)
{ {
return hid_register_driver(&zp_driver); return hid_register_driver(&zp_driver);
} }
static void zp_exit(void) static void __exit zp_exit(void)
{ {
hid_unregister_driver(&zp_driver); hid_unregister_driver(&zp_driver);
} }
......
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
* Copyright (c) 1999 Andreas Gal * Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2008 Jiri Kosina
* Copyright (c) 2007-2008 Oliver Neukum * Copyright (c) 2007-2008 Oliver Neukum
* Copyright (c) 2006-2009 Jiri Kosina
*/ */
/* /*
...@@ -489,7 +489,8 @@ static void hid_ctrl(struct urb *urb) ...@@ -489,7 +489,8 @@ static void hid_ctrl(struct urb *urb)
wake_up(&usbhid->wait); wake_up(&usbhid->wait);
} }
void __usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir) static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *report,
unsigned char dir)
{ {
int head; int head;
struct usbhid_device *usbhid = hid->driver_data; struct usbhid_device *usbhid = hid->driver_data;
...@@ -885,11 +886,6 @@ static int usbhid_parse(struct hid_device *hid) ...@@ -885,11 +886,6 @@ static int usbhid_parse(struct hid_device *hid)
goto err; goto err;
} }
dbg_hid("report descriptor (size %u, read %d) = ", rsize, n);
for (n = 0; n < rsize; n++)
dbg_hid_line(" %02x", (unsigned char) rdesc[n]);
dbg_hid_line("\n");
ret = hid_parse_report(hid, rdesc, rsize); ret = hid_parse_report(hid, rdesc, rsize);
kfree(rdesc); kfree(rdesc);
if (ret) { if (ret) {
...@@ -985,7 +981,6 @@ static int usbhid_start(struct hid_device *hid) ...@@ -985,7 +981,6 @@ static int usbhid_start(struct hid_device *hid)
INIT_WORK(&usbhid->restart_work, __usbhid_restart_queues); INIT_WORK(&usbhid->restart_work, __usbhid_restart_queues);
setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid); setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid);
spin_lock_init(&usbhid->lock);
spin_lock_init(&usbhid->lock); spin_lock_init(&usbhid->lock);
usbhid->intf = intf; usbhid->intf = intf;
...@@ -1004,7 +999,6 @@ static int usbhid_start(struct hid_device *hid) ...@@ -1004,7 +999,6 @@ static int usbhid_start(struct hid_device *hid)
usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP); usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
usbhid_init_reports(hid); usbhid_init_reports(hid);
hid_dump_device(hid);
set_bit(HID_STARTED, &usbhid->iofl); set_bit(HID_STARTED, &usbhid->iofl);
......
...@@ -201,7 +201,7 @@ int usbhid_quirks_init(char **quirks_param) ...@@ -201,7 +201,7 @@ int usbhid_quirks_init(char **quirks_param)
u32 quirks; u32 quirks;
int n = 0, m; int n = 0, m;
for (; quirks_param[n] && n < MAX_USBHID_BOOT_QUIRKS; n++) { for (; n < MAX_USBHID_BOOT_QUIRKS && quirks_param[n]; n++) {
m = sscanf(quirks_param[n], "0x%hx:0x%hx:0x%x", m = sscanf(quirks_param[n], "0x%hx:0x%hx:0x%x",
&idVendor, &idProduct, &quirks); &idVendor, &idProduct, &quirks);
......
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
#define HIDDEV_MINOR_BASE 96 #define HIDDEV_MINOR_BASE 96
#define HIDDEV_MINORS 16 #define HIDDEV_MINORS 16
#endif #endif
#define HIDDEV_BUFFER_SIZE 64 #define HIDDEV_BUFFER_SIZE 2048
struct hiddev { struct hiddev {
int exist; int exist;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
#define __HID_DEBUG_H #define __HID_DEBUG_H
/* /*
* Copyright (c) 2007 Jiri Kosina * Copyright (c) 2007-2009 Jiri Kosina
*/ */
/* /*
...@@ -22,24 +22,44 @@ ...@@ -22,24 +22,44 @@
* *
*/ */
#ifdef CONFIG_HID_DEBUG #define HID_DEBUG_BUFSIZE 512
void hid_dump_input(struct hid_usage *, __s32); #ifdef CONFIG_DEBUG_FS
void hid_dump_device(struct hid_device *);
void hid_dump_field(struct hid_field *, int); void hid_dump_input(struct hid_device *, struct hid_usage *, __s32);
void hid_resolv_usage(unsigned); void hid_dump_device(struct hid_device *, struct seq_file *);
void hid_resolv_event(__u8, __u16); void hid_dump_field(struct hid_field *, int, struct seq_file *);
char *hid_resolv_usage(unsigned, struct seq_file *);
void hid_debug_register(struct hid_device *, const char *);
void hid_debug_unregister(struct hid_device *);
void hid_debug_init(void);
void hid_debug_exit(void);
void hid_debug_event(struct hid_device *, char *);
#else
#define hid_dump_input(a,b) do { } while (0) struct hid_debug_list {
#define hid_dump_device(c) do { } while (0) char *hid_debug_buf;
#define hid_dump_field(a,b) do { } while (0) int head;
#define hid_resolv_usage(a) do { } while (0) int tail;
#define hid_resolv_event(a,b) do { } while (0) struct fasync_struct *fasync;
struct hid_device *hdev;
struct list_head node;
struct mutex read_mutex;
};
#endif /* CONFIG_HID_DEBUG */ #else
#define hid_dump_input(a,b,c) do { } while (0)
#define hid_dump_device(a,b) do { } while (0)
#define hid_dump_field(a,b,c) do { } while (0)
#define hid_resolv_usage(a,b) do { } while (0)
#define hid_debug_register(a, b) do { } while (0)
#define hid_debug_unregister(a) do { } while (0)
#define hid_debug_init() do { } while (0)
#define hid_debug_exit() do { } while (0)
#define hid_debug_event(a,b) do { } while (0)
#endif
#endif #endif
...@@ -500,6 +500,14 @@ struct hid_device { /* device report descriptor */ ...@@ -500,6 +500,14 @@ struct hid_device { /* device report descriptor */
/* handler for raw output data, used by hidraw */ /* handler for raw output data, used by hidraw */
int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t); int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t);
/* debugging support via debugfs */
unsigned short debug;
struct dentry *debug_dir;
struct dentry *debug_rdesc;
struct dentry *debug_events;
struct list_head debug_list;
wait_queue_head_t debug_wait;
}; };
static inline void *hid_get_drvdata(struct hid_device *hdev) static inline void *hid_get_drvdata(struct hid_device *hdev)
...@@ -657,9 +665,7 @@ struct hid_ll_driver { ...@@ -657,9 +665,7 @@ struct hid_ll_driver {
/* HID core API */ /* HID core API */
#ifdef CONFIG_HID_DEBUG
extern int hid_debug; extern int hid_debug;
#endif
extern int hid_add_device(struct hid_device *); extern int hid_add_device(struct hid_device *);
extern void hid_destroy_device(struct hid_device *); extern void hid_destroy_device(struct hid_device *);
...@@ -815,21 +821,9 @@ int hid_pidff_init(struct hid_device *hid); ...@@ -815,21 +821,9 @@ int hid_pidff_init(struct hid_device *hid);
#define hid_pidff_init NULL #define hid_pidff_init NULL
#endif #endif
#ifdef CONFIG_HID_DEBUG
#define dbg_hid(format, arg...) if (hid_debug) \ #define dbg_hid(format, arg...) if (hid_debug) \
printk(KERN_DEBUG "%s: " format ,\ printk(KERN_DEBUG "%s: " format ,\
__FILE__ , ## arg) __FILE__ , ## arg)
#define dbg_hid_line(format, arg...) if (hid_debug) \
printk(format, ## arg)
#else
static inline int __attribute__((format(printf, 1, 2)))
dbg_hid(const char *fmt, ...)
{
return 0;
}
#define dbg_hid_line dbg_hid
#endif /* HID_DEBUG */
#define err_hid(format, arg...) printk(KERN_ERR "%s: " format "\n" , \ #define err_hid(format, arg...) printk(KERN_ERR "%s: " format "\n" , \
__FILE__ , ## arg) __FILE__ , ## arg)
#endif /* HID_FF */ #endif /* HID_FF */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment