GIT 0cd96882913b76b3514dfa23102cde5a1bdc3b5e git+ssh://master.kernel.org/pub/scm/linux/kernel/git/jikos/hid.git#mm commit 65df79792f8b1e6a0a7ace4a1f5bec84a406fa99 Author: Alan Stern Date: Wed May 30 11:11:12 2007 -0400 USB HID: avoid flush_scheduled_work() This patch (as914) replaces a call to flush_scheduled_work() with cancel_work_sync(), in order to help avoid potential deadlocks. Signed-off-by: Alan Stern Signed-off-by: Jiri Kosina commit 5c95bffff3bbaacd09d4373f27a3af2d232126f0 Author: Jiri Kosina Date: Wed May 30 15:07:13 2007 +0200 HID: make debugging output runtime-configurable There have been many reports recently about broken HID devices, the diagnosis of which required users to recompile their kernels in order to be able to provide debugging output needed for coding a quirk for a particular device. This patch makes CONFIG_HID_DEBUG default y if !EMBEDDED and makes it possible to control debugging output produced by HID code by supplying 'debug=1' module parameter. Signed-off-by: Jiri Kosina commit c706b74ef2440d6173b46e96f23eaf755056f6c0 Author: Jan Engelhardt Date: Mon Apr 30 13:27:48 2007 +0200 HID: Use menuconfig objects Make a "menuconfig" out of the Kconfig objects "menu, ..., endmenu", so that the user can disable all the options in that menu at once instead of having to disable each option separately. Signed-off-by: Jan Engelhardt Signed-off-by: Jiri Kosina commit 863ba86ec99134dca10d9ad9c1deca048dd7faae Author: Jiri Kosina Date: Sat May 19 16:28:04 2007 +0200 HID: force hid-input for Microsoft SideWinder GameVoice device Microsoft SideWinder GameVoice driver is a trivial device with a few buttons (0x09 HID usage) and an audio connector, which just forwards the audio input into oridinary sound card present in the computer. Despite this fact, the only interface of this device reports itself as a Telephony/Headset type of HID device. This is apparently incorrect - the device itself doesn't provide any audio/telephony functionality. This is achieved in userland application which only needs to receive the button events from the HID driver. This patch establishes a new quirk which forces hid-input to claim a device it will otherwise leave untouched. Reported-by: Tomas Carnecky Signed-off-by: Jiri Kosina commit c9398fc88e53370add64a5dd6c78dc80d0f106e6 Author: Jiri Kosina Date: Mon May 14 10:20:29 2007 +0200 HID: input mapping for Chicony KU-0418 tactical pad Chicony KU-0418 (aka Saitek PZ08AU gaming keyboard) has a separate "tactical pad" with 11 non-functional buttons - they generate usage codes from 0xff00 (MSVENDOR) usage page. Special case handling for this keyboard added, so no later clash with MSVENDOR mappings is going to occur. Pointed out in bugzilla #7352 Signed-off-by: Jiri Kosina commit 0dff414f76d64be373d85f10d864bc00ef16e674 Author: Jiri Kosina Date: Mon May 14 09:57:40 2007 +0200 HID: add hidraw interface hidraw is an interface that is going to obsolete hiddev one day. Many userland applications are using libusb instead of using kernel-provided hiddev interface. This is caused by various reasons - the HID parser in kernel doesn't handle all the HID hardware on the planet properly, some devices might require its own specific quirks/drivers, etc. hiddev interface tries to do its best to parse all the received reports properly, and presents only parsed usages into userspace. This is however often not enough, and that's the reason why many userland applications just don't use hiddev at all, and rather use libusb to read raw USB events and process them on their own. Another drawback of hiddev is that it is USB-specific. hidraw interface provides userspace readers with really raw HID reports, no matter what the low-level transport layer is (USB/BT), and gives the userland applications all the freedom to process the HID reports in a way they wish to. Signed-off-by: Jiri Kosina commit 933ef2374b8002e3cb08f1b11a6087494de3abca Author: Jiri Kosina Date: Mon May 14 09:54:30 2007 +0200 USB HID: provide hook for hidraw write() Add hook in usbhid for write() callback from hidraw. Sends the report to the device through control pipe. Signed-off-by: Jiri Kosina drivers/hid/Kconfig | 29 +++ drivers/hid/Makefile | 2 drivers/hid/hid-core.c | 109 ++++++----- drivers/hid/hid-debug.c | 15 + drivers/hid/hid-input.c | 51 +++-- drivers/hid/hidraw.c | 395 +++++++++++++++++++++++++++++++++++++++ drivers/hid/usbhid/hid-core.c | 78 +++++--- drivers/hid/usbhid/hid-lgff.c | 10 - drivers/hid/usbhid/hid-quirks.c | 12 + drivers/hid/usbhid/hid-tmff.c | 2 drivers/hid/usbhid/hid-zpff.c | 8 - drivers/hid/usbhid/hiddev.c | 2 drivers/hid/usbhid/usbkbd.c | 6 - include/linux/hid.h | 26 ++- include/linux/hidraw.h | 86 ++++++++ 15 files changed, 716 insertions(+), 115 deletions(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 8fbe9fd..bee43f8 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -1,8 +1,12 @@ # # HID driver configuration # -menu "HID Devices" +menuconfig HID_SUPPORT + bool "HID Devices" depends on INPUT + default y + +if HID_SUPPORT config HID tristate "Generic HID support" @@ -24,6 +28,7 @@ config HID config HID_DEBUG bool "HID debugging support" + default y if !EMBEDDED depends on HID ---help--- This option lets the HID layer output diagnostics about its internal @@ -36,7 +41,25 @@ config HID_DEBUG If unsure, say N -source "drivers/hid/usbhid/Kconfig" +config HIDRAW + bool "/dev/hidraw raw HID device support" + depends on HID + ---help--- + Say Y here if you want to support HID devices (from the USB + specification standpoint) that aren't strictly user interface + devices, like monitor controls and Uninterruptable Power Supplies. -endmenu + This module supports these devices separately using a separate + event interface on /dev/hidraw. + + There is also a /dev/hiddev configuration option in the USB HID + configuration menu. In comparison to hiddev, this device does not process + the hid events at all (no parsing, no lookups). This lets applications + to work on raw hid events when they want to, and avoid using transport-specific + userspace libhid/libusb libraries. + + If unsure, say Y. + +source "drivers/hid/usbhid/Kconfig" +endif # HID_SUPPORT diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 68d1376..1ac5103 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -4,7 +4,9 @@ # hid-objs := hid-core.o hid-input.o obj-$(CONFIG_HID) += hid.o + hid-$(CONFIG_HID_DEBUG) += hid-debug.o +hid-$(CONFIG_HIDRAW) += hidraw.o obj-$(CONFIG_USB_HID) += usbhid/ obj-$(CONFIG_USB_MOUSE) += usbhid/ diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 6ec04e7..2884b03 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -30,6 +30,7 @@ #include #include #include #include +#include /* * Version Information @@ -40,6 +41,13 @@ #define DRIVER_AUTHOR "Andreas Gal, Vojt #define DRIVER_DESC "HID core driver" #define DRIVER_LICENSE "GPL" +#ifdef CONFIG_HID_DEBUG +int hid_debug = 0; +module_param_named(debug, hid_debug, bool, 0600); +MODULE_PARM_DESC(debug, "Turn HID debugging mode on and off"); +EXPORT_SYMBOL_GPL(hid_debug); +#endif + /* * Register a new report for a device. */ @@ -78,7 +86,7 @@ static struct hid_field *hid_register_fi struct hid_field *field; if (report->maxfield == HID_MAX_FIELDS) { - dbg("too many fields in report"); + dbg_hid("too many fields in report\n"); return NULL; } @@ -106,7 +114,7 @@ static int open_collection(struct hid_pa usage = parser->local.usage[0]; if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) { - dbg("collection stack overflow"); + dbg_hid("collection stack overflow\n"); return -1; } @@ -114,7 +122,7 @@ static int open_collection(struct hid_pa collection = kmalloc(sizeof(struct hid_collection) * parser->device->collection_size * 2, GFP_KERNEL); if (collection == NULL) { - dbg("failed to reallocate collection array"); + dbg_hid("failed to reallocate collection array\n"); return -1; } memcpy(collection, parser->device->collection, @@ -150,7 +158,7 @@ static int open_collection(struct hid_pa static int close_collection(struct hid_parser *parser) { if (!parser->collection_stack_ptr) { - dbg("collection stack underflow"); + dbg_hid("collection stack underflow\n"); return -1; } parser->collection_stack_ptr--; @@ -178,7 +186,7 @@ static unsigned hid_lookup_collection(st static int hid_add_usage(struct hid_parser *parser, unsigned usage) { if (parser->local.usage_index >= HID_MAX_USAGES) { - dbg("usage index exceeded"); + dbg_hid("usage index exceeded\n"); return -1; } parser->local.usage[parser->local.usage_index] = usage; @@ -202,12 +210,12 @@ static int hid_add_field(struct hid_pars int i; if (!(report = hid_register_report(parser->device, report_type, parser->global.report_id))) { - dbg("hid_register_report failed"); + dbg_hid("hid_register_report failed\n"); return -1; } if (parser->global.logical_maximum < parser->global.logical_minimum) { - dbg("logical range invalid %d %d", parser->global.logical_minimum, parser->global.logical_maximum); + dbg_hid("logical range invalid %d %d\n", parser->global.logical_minimum, parser->global.logical_maximum); return -1; } @@ -287,7 +295,7 @@ static int hid_parser_global(struct hid_ case HID_GLOBAL_ITEM_TAG_PUSH: if (parser->global_stack_ptr == HID_GLOBAL_STACK_SIZE) { - dbg("global enviroment stack overflow"); + dbg_hid("global enviroment stack overflow\n"); return -1; } @@ -298,7 +306,7 @@ static int hid_parser_global(struct hid_ case HID_GLOBAL_ITEM_TAG_POP: if (!parser->global_stack_ptr) { - dbg("global enviroment stack underflow"); + dbg_hid("global enviroment stack underflow\n"); return -1; } @@ -342,27 +350,27 @@ static int hid_parser_global(struct hid_ case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: if ((parser->global.report_size = item_udata(item)) > 32) { - dbg("invalid report_size %d", parser->global.report_size); + dbg_hid("invalid report_size %d\n", parser->global.report_size); return -1; } return 0; case HID_GLOBAL_ITEM_TAG_REPORT_COUNT: if ((parser->global.report_count = item_udata(item)) > HID_MAX_USAGES) { - dbg("invalid report_count %d", parser->global.report_count); + dbg_hid("invalid report_count %d\n", parser->global.report_count); return -1; } return 0; case HID_GLOBAL_ITEM_TAG_REPORT_ID: if ((parser->global.report_id = item_udata(item)) == 0) { - dbg("report_id 0 is invalid"); + dbg_hid("report_id 0 is invalid\n"); return -1; } return 0; default: - dbg("unknown global tag 0x%x", item->tag); + dbg_hid("unknown global tag 0x%x\n", item->tag); return -1; } } @@ -377,7 +385,7 @@ static int hid_parser_local(struct hid_p unsigned n; if (item->size == 0) { - dbg("item data expected for local item"); + dbg_hid("item data expected for local item\n"); return -1; } @@ -395,14 +403,14 @@ static int hid_parser_local(struct hid_p * items and the first delimiter set. */ if (parser->local.delimiter_depth != 0) { - dbg("nested delimiters"); + dbg_hid("nested delimiters\n"); return -1; } parser->local.delimiter_depth++; parser->local.delimiter_branch++; } else { if (parser->local.delimiter_depth < 1) { - dbg("bogus close delimiter"); + dbg_hid("bogus close delimiter\n"); return -1; } parser->local.delimiter_depth--; @@ -412,7 +420,7 @@ static int hid_parser_local(struct hid_p case HID_LOCAL_ITEM_TAG_USAGE: if (parser->local.delimiter_branch > 1) { - dbg("alternative usage ignored"); + dbg_hid("alternative usage ignored\n"); return 0; } @@ -424,7 +432,7 @@ static int hid_parser_local(struct hid_p case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: if (parser->local.delimiter_branch > 1) { - dbg("alternative usage ignored"); + dbg_hid("alternative usage ignored\n"); return 0; } @@ -437,7 +445,7 @@ static int hid_parser_local(struct hid_p case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM: if (parser->local.delimiter_branch > 1) { - dbg("alternative usage ignored"); + dbg_hid("alternative usage ignored\n"); return 0; } @@ -446,14 +454,14 @@ static int hid_parser_local(struct hid_p for (n = parser->local.usage_minimum; n <= data; n++) if (hid_add_usage(parser, n)) { - dbg("hid_add_usage failed\n"); + dbg_hid("hid_add_usage failed\n"); return -1; } return 0; default: - dbg("unknown local item tag 0x%x", item->tag); + dbg_hid("unknown local item tag 0x%x\n", item->tag); return 0; } return 0; @@ -487,7 +495,7 @@ static int hid_parser_main(struct hid_pa ret = hid_add_field(parser, HID_FEATURE_REPORT, data); break; default: - dbg("unknown main item tag 0x%x", item->tag); + dbg_hid("unknown main item tag 0x%x\n", item->tag); ret = 0; } @@ -502,7 +510,7 @@ static int hid_parser_main(struct hid_pa static int hid_parser_reserved(struct hid_parser *parser, struct hid_item *item) { - dbg("reserved item type, tag 0x%x", item->tag); + dbg_hid("reserved item type, tag 0x%x\n", item->tag); return 0; } @@ -667,14 +675,14 @@ struct hid_device *hid_parse_report(__u8 while ((start = fetch_item(start, end, &item)) != NULL) { if (item.format != HID_ITEM_FORMAT_SHORT) { - dbg("unexpected long global item"); + dbg_hid("unexpected long global item\n"); hid_free_device(device); vfree(parser); return NULL; } if (dispatch_type[item.type](parser, &item)) { - dbg("item %u %u %u %u parsing failed\n", + dbg_hid("item %u %u %u %u parsing failed\n", item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag); hid_free_device(device); vfree(parser); @@ -683,13 +691,13 @@ struct hid_device *hid_parse_report(__u8 if (start == end) { if (parser->collection_stack_ptr) { - dbg("unbalanced collection at end of report description"); + dbg_hid("unbalanced collection at end of report description\n"); hid_free_device(device); vfree(parser); return NULL; } if (parser->local.delimiter_depth) { - dbg("unbalanced delimiter at end of report description"); + dbg_hid("unbalanced delimiter at end of report description\n"); hid_free_device(device); vfree(parser); return NULL; @@ -699,7 +707,7 @@ struct hid_device *hid_parse_report(__u8 } } - dbg("item fetching failed at offset %d\n", (int)(end - start)); + dbg_hid("item fetching failed at offset %d\n", (int)(end - start)); hid_free_device(device); vfree(parser); return NULL; @@ -915,13 +923,13 @@ int hid_set_field(struct hid_field *fiel hid_dump_input(field->usage + offset, value); if (offset >= field->report_count) { - dbg("offset (%d) exceeds report_count (%d)", offset, field->report_count); + dbg_hid("offset (%d) exceeds report_count (%d)\n", offset, field->report_count); hid_dump_field(field, 8); return -1; } if (field->logical_minimum < 0) { if (value != snto32(s32ton(value, size), size)) { - dbg("value %d is out of range", value); + dbg_hid("value %d is out of range\n", value); return -1; } } @@ -934,19 +942,17 @@ int hid_input_report(struct hid_device * { struct hid_report_enum *report_enum = hid->report_enum + type; struct hid_report *report; - int n, rsize; + int n, rsize, i; if (!hid) return -ENODEV; if (!size) { - dbg("empty report"); + dbg_hid("empty report\n"); return -1; } -#ifdef CONFIG_HID_DEBUG - printk(KERN_DEBUG __FILE__ ": report (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un"); -#endif + dbg_hid("report (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un"); n = 0; /* Normally report number is 0 */ if (report_enum->numbered) { /* Device uses numbered reports, data[0] is report number */ @@ -954,30 +960,28 @@ #endif size--; } -#ifdef CONFIG_HID_DEBUG - { - int i; - printk(KERN_DEBUG __FILE__ ": report %d (size %u) = ", n, size); - for (i = 0; i < size; i++) - printk(" %02x", data[i]); - printk("\n"); - } -#endif + /* dump the report descriptor */ + dbg_hid("report %d (size %u) = ", n, size); + for (i = 0; i < size; i++) + dbg_hid_line(" %02x", data[i]); + dbg_hid_line("\n"); if (!(report = report_enum->report_id_hash[n])) { - dbg("undefined report_id %d received", n); + dbg_hid("undefined report_id %d received\n", n); return -1; } rsize = ((report->size - 1) >> 3) + 1; if (size < rsize) { - dbg("report %d is too short, (%d < %d)", report->id, size, rsize); + dbg_hid("report %d is too short, (%d < %d)\n", report->id, size, rsize); memset(data + size, 0, rsize - size); } if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event) hid->hiddev_report_event(hid, report); + if (hid->claimed & HID_CLAIMED_HIDRAW) + hidraw_report_event(hid, data, size); for (n = 0; n < report->maxfield; n++) hid_input_field(hid, report->field[n], data, interrupt); @@ -989,5 +993,18 @@ #endif } EXPORT_SYMBOL_GPL(hid_input_report); +static int __init hid_init(void) +{ + return hidraw_init(); +} + +static void __exit hid_exit(void) +{ + hidraw_exit(); +} + +module_init(hid_init); +module_exit(hid_exit); + MODULE_LICENSE(DRIVER_LICENSE); diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 83c4126..a13757b 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -347,6 +347,9 @@ static void resolv_usage_page(unsigned p void hid_resolv_usage(unsigned usage) { const struct hid_usage_entry *p; + if (!hid_debug) + return; + resolv_usage_page(usage >> 16); printk("."); for (p = hid_usage_table; p->description; p++) @@ -369,6 +372,9 @@ __inline__ static void tab(int n) { void hid_dump_field(struct hid_field *field, int n) { int j; + if (!hid_debug) + return; + if (field->physical) { tab(n); printk("Physical("); @@ -466,6 +472,9 @@ void hid_dump_device(struct hid_device * unsigned i,k; static char *table[] = {"INPUT", "OUTPUT", "FEATURE"}; + if (!hid_debug) + return; + for (i = 0; i < HID_REPORT_TYPES; i++) { report_enum = device->report_enum + i; list = report_enum->report_list.next; @@ -489,6 +498,9 @@ void hid_dump_device(struct hid_device * EXPORT_SYMBOL_GPL(hid_dump_device); void hid_dump_input(struct hid_usage *usage, __s32 value) { + if (!hid_debug) + return; + printk("hid-debug: input "); hid_resolv_usage(usage->hid); printk(" = %d\n", value); @@ -758,6 +770,9 @@ static char **names[EV_MAX + 1] = { void hid_resolv_event(__u8 type, __u16 code) { + if (!hid_debug) + return; + printk("%s.%s", events[type] ? events[type] : "?", names[type] ? (names[type][code] ? names[type][code] : "?") : "?"); } diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 7f81789..6621d56 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -308,9 +308,7 @@ static int hidinput_setkeycode(struct in clear_bit(old_keycode, dev->keybit); set_bit(usage->code, dev->keybit); -#ifdef CONFIG_HID_DEBUG - printk (KERN_DEBUG "Assigned keycode %d to HID usage code %x\n", keycode, scancode); -#endif + dbg_hid(KERN_DEBUG "Assigned keycode %d to HID usage code %x\n", keycode, scancode); /* Set the keybit for the old keycode if the old keycode is used * by another key */ if (hidinput_find_key (hid, 0, old_keycode)) @@ -333,11 +331,9 @@ static void hidinput_configure_usage(str field->hidinput = hidinput; -#ifdef CONFIG_HID_DEBUG - printk(KERN_DEBUG "Mapping: "); + dbg_hid("Mapping: "); hid_resolv_usage(usage->hid); - printk(" ---> "); -#endif + dbg_hid_line(" ---> "); if (field->flags & HID_MAIN_ITEM_CONSTANT) goto ignore; @@ -688,7 +684,28 @@ #endif break; case HID_UP_MSVENDOR: - goto ignore; + + /* special case - Chicony Chicony KU-0418 tactical pad */ + if (device->vendor == 0x04f2 && device->product == 0x0418) { + set_bit(EV_REP, input->evbit); + switch(usage->hid & HID_USAGE) { + case 0xff01: map_key_clear(BTN_1); break; + case 0xff02: map_key_clear(BTN_2); break; + case 0xff03: map_key_clear(BTN_3); break; + case 0xff04: map_key_clear(BTN_4); break; + case 0xff05: map_key_clear(BTN_5); break; + case 0xff06: map_key_clear(BTN_6); break; + case 0xff07: map_key_clear(BTN_7); break; + case 0xff08: map_key_clear(BTN_8); break; + case 0xff09: map_key_clear(BTN_9); break; + case 0xff0a: map_key_clear(BTN_A); break; + case 0xff0b: map_key_clear(BTN_B); break; + default: goto ignore; + } + } else { + goto ignore; + } + break; case HID_UP_CUSTOM: /* Reported on Logitech and Powerbook USB keyboards */ @@ -819,15 +836,13 @@ #endif } hid_resolv_event(usage->type, usage->code); -#ifdef CONFIG_HID_DEBUG - printk("\n"); -#endif + + dbg_hid_line("\n"); + return; ignore: -#ifdef CONFIG_HID_DEBUG - printk("IGNORED\n"); -#endif + dbg_hid_line("IGNORED\n"); return; } @@ -896,12 +911,12 @@ void hidinput_hid_event(struct hid_devic } if (usage->hid == (HID_UP_PID | 0x83UL)) { /* Simultaneous Effects Max */ - dbg("Maximum Effects - %d",value); + dbg_hid("Maximum Effects - %d\n",value); return; } if (usage->hid == (HID_UP_PID | 0x7fUL)) { - dbg("PID Pool Report\n"); + dbg_hid("PID Pool Report\n"); return; } @@ -976,7 +991,7 @@ int hidinput_connect(struct hid_device * if (IS_INPUT_APPLICATION(hid->collection[i].usage)) break; - if (i == hid->maxcollection) + if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDINPUT) == 0) return -1; if (hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS) @@ -994,7 +1009,7 @@ int hidinput_connect(struct hid_device * if (!hidinput || !input_dev) { kfree(hidinput); input_free_device(input_dev); - err("Out of memory during hid input probe"); + err_hid("Out of memory during hid input probe"); return -1; } diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c new file mode 100644 index 0000000..20ebba1 --- /dev/null +++ b/drivers/hid/hidraw.c @@ -0,0 +1,395 @@ +/* + * HID raw devices, giving access to raw HID events. + * + * In comparison to hiddev, this device does not process the + * hid events at all (no parsing, no lookups). This lets applications + * to work on raw hid events as they want to, and avoids a need to + * use a transport-specific userspace libhid/libusb libraries. + * + * Copyright (c) 2007 Jiri Kosina + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static int hidraw_major; +static struct cdev hidraw_cdev; +static struct class *hidraw_class; +static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES]; +static DEFINE_SPINLOCK(minors_lock); + +static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +{ + struct hidraw_list *list = file->private_data; + int ret = 0, len; + char *report; + DECLARE_WAITQUEUE(wait, current); + + while (ret == 0) { + + mutex_lock(&list->read_mutex); + + if (list->head == list->tail) { + add_wait_queue(&list->hidraw->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->hidraw->exist) { + ret = -EIO; + break; + } + + /* allow O_NONBLOCK to work well 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->hidraw->wait, &wait); + } + + if (ret) + goto out; + + report = list->buffer[list->tail].value; + len = list->buffer[list->tail].len > count ? + count : list->buffer[list->tail].len; + + if (copy_to_user(buffer, list->buffer[list->tail].value, len)) { + ret = -EFAULT; + goto out; + } + ret += len; + + kfree(list->buffer[list->tail].value); + list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1); + } +out: + mutex_unlock(&list->read_mutex); + return ret; +} + +/* the first byte is expected to be a report number */ +static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +{ + unsigned int minor = iminor(file->f_path.dentry->d_inode); + struct hid_device *dev = hidraw_table[minor]->hid; + __u8 *buf; + int ret = 0; + + if (!dev->hid_output_raw_report) + return -ENODEV; + + if (count > HID_MIN_BUFFER_SIZE) { + printk(KERN_WARNING "hidraw: pid %d passed too large report\n", + current->pid); + return -EINVAL; + } + + if (count < 2) { + printk(KERN_WARNING "hidraw: pid %d passed too short report\n", + current->pid); + return -EINVAL; + } + + buf = kmalloc(count * sizeof(__u8), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, buffer, count)) { + ret = -EFAULT; + goto out; + } + + ret = dev->hid_output_raw_report(dev, buf, count); +out: + kfree(buf); + return ret; +} + +static unsigned int hidraw_poll(struct file *file, poll_table *wait) +{ + struct hidraw_list *list = file->private_data; + + poll_wait(file, &list->hidraw->wait, wait); + if (list->head != list->tail) + return POLLIN | POLLRDNORM; + if (!list->hidraw->exist) + return POLLERR | POLLHUP; + return 0; +} + +static int hidraw_open(struct inode *inode, struct file *file) +{ + unsigned int minor = iminor(inode); + struct hidraw *dev; + struct hidraw_list *list; + int err = 0; + + if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) { + err = -ENOMEM; + goto out; + } + + spin_lock(&minors_lock); + if (!hidraw_table[minor]) { + printk(KERN_EMERG "hidraw device with minor %d doesn't exist\n", + minor); + kfree(list); + err = -ENODEV; + goto out_unlock; + } + + list->hidraw = hidraw_table[minor]; + mutex_init(&list->read_mutex); + list_add_tail(&list->node, &hidraw_table[minor]->list); + file->private_data = list; + + dev = hidraw_table[minor]; + if (!dev->open++) + dev->hid->hid_open(dev->hid); + +out_unlock: + spin_unlock(&minors_lock); +out: + return err; + +} + +static int hidraw_release(struct inode * inode, struct file * file) +{ + unsigned int minor = iminor(inode); + struct hidraw *dev; + struct hidraw_list *list = file->private_data; + + if (!hidraw_table[minor]) { + printk(KERN_EMERG "hidraw device with minor %d doesn't exist\n", + minor); + return -ENODEV; + } + + list_del(&list->node); + dev = hidraw_table[minor]; + if (!dev->open--) { + if (list->hidraw->exist) + dev->hid->hid_close(dev->hid); + else + kfree(list->hidraw); + } + + return 0; +} + +static int hidraw_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + unsigned int minor = iminor(inode); + struct hidraw *dev = hidraw_table[minor]; + void __user *user_arg = (void __user*) arg; + + switch (cmd) { + case HIDIOCGRDESCSIZE: + if (put_user(dev->hid->rsize, (int __user *)arg)) + return -EFAULT; + return 0; + + case HIDIOCGRDESC: + { + __u32 len; + + if (get_user(len, (int __user *)arg)) + return -EFAULT; + if (copy_to_user(*((__u8 **)(user_arg + + sizeof(__u32))), + dev->hid->rdesc, len)) + return -EFAULT; + return 0; + } + case HIDIOCGRAWINFO: + { + struct hidraw_devinfo dinfo; + + dinfo.bustype = dev->hid->bus; + dinfo.vendor = dev->hid->vendor; + dinfo.product = dev->hid->product; + if (copy_to_user(user_arg, &dinfo, sizeof(dinfo))) + return -EFAULT; + + return 0; + } + default: + printk(KERN_EMERG "hidraw: unsupported ioctl() %x\n", + cmd); + } + return -EINVAL; +} + +static const struct file_operations hidraw_ops = { + .owner = THIS_MODULE, + .read = hidraw_read, + .write = hidraw_write, + .poll = hidraw_poll, + .open = hidraw_open, + .release = hidraw_release, + .ioctl = hidraw_ioctl, +}; + +void hidraw_report_event(struct hid_device *hid, u8 *data, int len) +{ + struct hidraw *dev = hid->hidraw; + struct hidraw_list *list; + + list_for_each_entry(list, &dev->list, node) { + list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC); + list->buffer[list->head].len = len; + list->head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1); + kill_fasync(&list->fasync, SIGIO, POLL_IN); + } + + wake_up_interruptible(&dev->wait); +} +EXPORT_SYMBOL_GPL(hidraw_report_event); + +int hidraw_connect(struct hid_device *hid) +{ + int minor, result = -EINVAL; + struct hidraw *dev; + + /* TODO currently we accept any HID device. This should later + * probably be fixed to accept only those devices which provide + * non-input applications + */ + + if (!(dev = kzalloc(sizeof(struct hidraw), GFP_KERNEL))) + return -1; + + spin_lock(&minors_lock); + + for (minor = 0; minor < HIDRAW_MAX_DEVICES; minor++) { + if (hidraw_table[minor]) + continue; + hidraw_table[minor] = dev; + result = 0; + break; + } + + spin_unlock(&minors_lock); + + if (result) + goto out; + + dev->dev = device_create(hidraw_class, NULL, MKDEV(hidraw_major, minor), + "%s%d", "hidraw", minor); + + if (IS_ERR(dev->dev)) { + spin_lock(&minors_lock); + hidraw_table[minor] = NULL; + spin_unlock(&minors_lock); + result = PTR_ERR(dev->dev); + goto out; + } + + init_waitqueue_head(&dev->wait); + INIT_LIST_HEAD(&dev->list); + + dev->hid = hid; + dev->minor = minor; + + dev->exist = 1; + hid->hidraw = dev; + +out: + return result; + +} +EXPORT_SYMBOL_GPL(hidraw_connect); + +void hidraw_disconnect(struct hid_device *hid) +{ + struct hidraw *hidraw = hid->hidraw; + + hidraw->exist = 0; + + spin_lock(&minors_lock); + hidraw_table[hidraw->minor] = NULL; + spin_unlock(&minors_lock); + + device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor)); + + if (hidraw->open) { + hid->hid_close(hid); + wake_up_interruptible(&hidraw->wait); + } else { + kfree(hidraw); + } +} +EXPORT_SYMBOL_GPL(hidraw_disconnect); + +int __init hidraw_init(void) +{ + int result; + dev_t dev_id; + + result = alloc_chrdev_region(&dev_id, HIDRAW_FIRST_MINOR, + HIDRAW_MAX_DEVICES, "hidraw"); + + hidraw_major = MAJOR(dev_id); + + if (result < 0) { + printk(KERN_WARNING "hidraw: can't get major number\n"); + result = 0; + goto out; + } + + hidraw_class = class_create(THIS_MODULE, "hidraw"); + if (IS_ERR(hidraw_class)) { + result = PTR_ERR(hidraw_class); + unregister_chrdev(hidraw_major, "hidraw"); + goto out; + } + + cdev_init(&hidraw_cdev, &hidraw_ops); + cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES); +out: + return result; +} + +void __exit hidraw_exit(void) +{ + dev_t dev_id = MKDEV(hidraw_major, 0); + + cdev_del(&hidraw_cdev); + class_destroy(hidraw_class); + unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES); + +} diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index d91b9da..3faafe2 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -32,6 +32,7 @@ #include #include #include #include +#include #include "usbhid.h" /* @@ -127,7 +128,7 @@ static void hid_reset(struct work_struct hid_io_error(hid); break; default: - err("can't reset device, %s-%s/input%d, status %d", + err_hid("can't reset device, %s-%s/input%d, status %d", hid_to_usb_dev(hid)->bus->bus_name, hid_to_usb_dev(hid)->devpath, usbhid->ifnum, rc); @@ -220,7 +221,7 @@ static void hid_irq_in(struct urb *urb) if (status) { clear_bit(HID_IN_RUNNING, &usbhid->iofl); if (status != -EPERM) { - err("can't resubmit intr, %s-%s/input%d, status %d", + err_hid("can't resubmit intr, %s-%s/input%d, status %d", hid_to_usb_dev(hid)->bus->bus_name, hid_to_usb_dev(hid)->devpath, usbhid->ifnum, status); @@ -240,10 +241,10 @@ static int hid_submit_out(struct hid_dev usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0); usbhid->urbout->dev = hid_to_usb_dev(hid); - dbg("submitting out urb"); + dbg_hid("submitting out urb\n"); if (usb_submit_urb(usbhid->urbout, GFP_ATOMIC)) { - err("usb_submit_urb(out) failed"); + err_hid("usb_submit_urb(out) failed"); return -1; } @@ -287,12 +288,12 @@ static int hid_submit_ctrl(struct hid_de usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum); usbhid->cr->wLength = cpu_to_le16(len); - dbg("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u", + dbg_hid("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u\n", usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report", usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength); if (usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC)) { - err("usb_submit_urb(ctrl) failed"); + err_hid("usb_submit_urb(ctrl) failed"); return -1; } @@ -474,7 +475,7 @@ int usbhid_wait_io(struct hid_device *hi if (!wait_event_timeout(hid->wait, (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl) && !test_bit(HID_OUT_RUNNING, &usbhid->iofl)), 10*HZ)) { - dbg("timeout waiting for ctrl or out queue to clear"); + dbg_hid("timeout waiting for ctrl or out queue to clear\n"); return -1; } @@ -622,6 +623,28 @@ static int hid_alloc_buffers(struct usb_ return 0; } +static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count) +{ + struct usbhid_device *usbhid = hid->driver_data; + struct usb_device *dev = hid_to_usb_dev(hid); + struct usb_interface *intf = usbhid->intf; + struct usb_host_interface *interface = intf->cur_altsetting; + int ret; + + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + HID_REQ_SET_REPORT, + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + cpu_to_le16(((HID_OUTPUT_REPORT + 1) << 8) | *buf), + interface->desc.bInterfaceNumber, buf + 1, count - 1, + USB_CTRL_SET_TIMEOUT); + + /* count also the report id */ + if (ret > 0) + ret++; + + return ret; +} + static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid) { struct usbhid_device *usbhid = hid->driver_data; @@ -667,7 +690,7 @@ static void hid_fixup_sony_ps3_controlle USB_CTRL_GET_TIMEOUT); if (result < 0) - err("%s failed: %d\n", __func__, result); + err_hid("%s failed: %d\n", __func__, result); kfree(buf); } @@ -746,7 +769,7 @@ static struct hid_device *usb_hid_config if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) && (!interface->desc.bNumEndpoints || usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) { - dbg("class descriptor not present\n"); + dbg_hid("class descriptor not present\n"); return NULL; } @@ -755,19 +778,19 @@ static struct hid_device *usb_hid_config rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength); if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { - dbg("weird size of report descriptor (%u)", rsize); + dbg_hid("weird size of report descriptor (%u)\n", rsize); return NULL; } if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) { - dbg("couldn't allocate rdesc memory"); + dbg_hid("couldn't allocate rdesc memory\n"); return NULL; } hid_set_idle(dev, interface->desc.bInterfaceNumber, 0, 0); if ((n = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) { - dbg("reading report descriptor failed"); + dbg_hid("reading report descriptor failed\n"); kfree(rdesc); return NULL; } @@ -781,15 +804,13 @@ static struct hid_device *usb_hid_config if (quirks & HID_QUIRK_SWAPPED_MIN_MAX) hid_fixup_cypress_descriptor(rdesc, rsize); -#ifdef CONFIG_HID_DEBUG - printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, n); + dbg_hid("report descriptor (size %u, read %d) = ", rsize, n); for (n = 0; n < rsize; n++) - printk(" %02x", (unsigned char) rdesc[n]); - printk("\n"); -#endif + dbg_hid_line(" %02x", (unsigned char) rdesc[n]); + dbg_hid_line("\n"); if (!(hid = hid_parse_report(rdesc, n))) { - dbg("parsing report descriptor failed"); + dbg_hid("parsing report descriptor failed\n"); kfree(rdesc); return NULL; } @@ -861,7 +882,7 @@ #endif } if (!usbhid->urbin) { - err("couldn't find an input interrupt endpoint"); + err_hid("couldn't find an input interrupt endpoint"); goto fail; } @@ -926,6 +947,7 @@ #ifdef CONFIG_USB_HIDDEV hid->hiddev_hid_event = hiddev_hid_event; hid->hiddev_report_event = hiddev_report_event; #endif + hid->hid_output_raw_report = usbhid_output_raw_report; return hid; fail: @@ -956,12 +978,14 @@ static void hid_disconnect(struct usb_in usb_kill_urb(usbhid->urbctrl); del_timer_sync(&usbhid->io_retry); - flush_scheduled_work(); + cancel_work_sync(&usbhid->reset_work); if (hid->claimed & HID_CLAIMED_INPUT) hidinput_disconnect(hid); if (hid->claimed & HID_CLAIMED_HIDDEV) hiddev_disconnect(hid); + if (hid->claimed & HID_CLAIMED_HIDRAW) + hidraw_disconnect(hid); usb_free_urb(usbhid->urbin); usb_free_urb(usbhid->urbctrl); @@ -978,7 +1002,7 @@ static int hid_probe(struct usb_interfac int i; char *c; - dbg("HID probe called for ifnum %d", + dbg_hid("HID probe called for ifnum %d\n", intf->altsetting->desc.bInterfaceNumber); if (!(hid = usb_hid_configure(intf))) @@ -993,11 +1017,13 @@ static int hid_probe(struct usb_interfac hid->claimed |= HID_CLAIMED_INPUT; if (!hiddev_connect(hid)) hid->claimed |= HID_CLAIMED_HIDDEV; + if (!hidraw_connect(hid)) + hid->claimed |= HID_CLAIMED_HIDRAW; usb_set_intfdata(intf, hid); if (!hid->claimed) { - printk ("HID device not claimed by input or hiddev\n"); + printk ("HID device claimed by neither input, hiddev nor hidraw\n"); hid_disconnect(intf); return -ENODEV; } @@ -1013,10 +1039,16 @@ static int hid_probe(struct usb_interfac if (hid->claimed & HID_CLAIMED_INPUT) printk("input"); - if (hid->claimed == (HID_CLAIMED_INPUT | HID_CLAIMED_HIDDEV)) + if ((hid->claimed & HID_CLAIMED_INPUT) && ((hid->claimed & HID_CLAIMED_HIDDEV) || + hid->claimed & HID_CLAIMED_HIDRAW)) printk(","); if (hid->claimed & HID_CLAIMED_HIDDEV) printk("hiddev%d", hid->minor); + if ((hid->claimed & HID_CLAIMED_INPUT) && (hid->claimed & HID_CLAIMED_HIDDEV) && + (hid->claimed & HID_CLAIMED_HIDRAW)) + printk(","); + if (hid->claimed & HID_CLAIMED_HIDRAW) + printk("hidraw%d", ((struct hidraw*)hid->hidraw)->minor); c = "Device"; for (i = 0; i < hid->maxcollection; i++) { diff --git a/drivers/hid/usbhid/hid-lgff.c b/drivers/hid/usbhid/hid-lgff.c index c5cd410..4b7ab6a 100644 --- a/drivers/hid/usbhid/hid-lgff.c +++ b/drivers/hid/usbhid/hid-lgff.c @@ -78,7 +78,7 @@ #define CLAMP(x) if (x < 0) x = 0; if (x report->field[0]->value[1] = 0x08; report->field[0]->value[2] = x; report->field[0]->value[3] = y; - dbg("(x, y)=(%04x, %04x)", x, y); + dbg_hid("(x, y)=(%04x, %04x)\n", x, y); usbhid_submit_report(hid, report, USB_DIR_OUT); break; @@ -93,7 +93,7 @@ #define CLAMP(x) if (x < 0) x = 0; if (x report->field[0]->value[1] = 0x00; report->field[0]->value[2] = left; report->field[0]->value[3] = right; - dbg("(left, right)=(%04x, %04x)", left, right); + dbg_hid("(left, right)=(%04x, %04x)\n", left, right); usbhid_submit_report(hid, report, USB_DIR_OUT); break; } @@ -113,20 +113,20 @@ int hid_lgff_init(struct hid_device* hid /* Find the report to use */ if (list_empty(report_list)) { - err("No output report found"); + err_hid("No output report found"); return -1; } /* Check that the report looks ok */ report = list_entry(report_list->next, struct hid_report, list); if (!report) { - err("NULL output report"); + err_hid("NULL output report"); return -1; } field = report->field[0]; if (!field) { - err("NULL field"); + err_hid("NULL field"); return -1; } diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index f6c4145..ac445ce 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -209,6 +209,9 @@ #define USB_VENDOR_ID_MGE 0x0463 #define USB_DEVICE_ID_MGE_UPS 0xffff #define USB_DEVICE_ID_MGE_UPS1 0x0001 +#define USB_VENDOR_ID_MICROSOFT 0x045e +#define USB_DEVICE_ID_SIDEWINDER_GV 0x003b + #define USB_VENDOR_ID_NEC 0x073e #define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301 @@ -290,6 +293,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE, HID_QUIRK_DUPLICATE_USAGES }, { USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM, HID_QUIRK_HIDDEV }, + { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV, HID_QUIRK_HIDINPUT }, { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10, HID_QUIRK_IGNORE }, @@ -493,7 +497,7 @@ static struct hid_blacklist *usbhid_exis } if (bl_entry != NULL) - dbg("Found dynamic quirk 0x%x for USB HID vendor 0x%hx prod 0x%hx\n", + dbg_hid("Found dynamic quirk 0x%x for USB HID vendor 0x%hx prod 0x%hx\n", bl_entry->quirks, bl_entry->idVendor, bl_entry->idProduct); @@ -521,13 +525,13 @@ int usbhid_modify_dquirk(const u16 idVen int list_edited = 0; if (!idVendor) { - dbg("Cannot add a quirk with idVendor = 0"); + dbg_hid("Cannot add a quirk with idVendor = 0\n"); return -EINVAL; } q_new = kmalloc(sizeof(struct quirks_list_struct), GFP_KERNEL); if (!q_new) { - dbg("Could not allocate quirks_list_struct"); + dbg_hid("Could not allocate quirks_list_struct\n"); return -ENOMEM; } @@ -643,7 +647,7 @@ static const struct hid_blacklist *usbhi bl_entry = &hid_blacklist[n]; if (bl_entry != NULL) - dbg("Found squirk 0x%x for USB HID vendor 0x%hx prod 0x%hx\n", + dbg_hid("Found squirk 0x%x for USB HID vendor 0x%hx prod 0x%hx\n", bl_entry->quirks, bl_entry->idVendor, bl_entry->idProduct); return bl_entry; diff --git a/drivers/hid/usbhid/hid-tmff.c b/drivers/hid/usbhid/hid-tmff.c index ab5ba6e..555bb48 100644 --- a/drivers/hid/usbhid/hid-tmff.c +++ b/drivers/hid/usbhid/hid-tmff.c @@ -70,7 +70,7 @@ static int hid_tmff_play(struct input_de tmff->rumble->value[0] = left; tmff->rumble->value[1] = right; - dbg("(left,right)=(%08x, %08x)", left, right); + dbg_hid("(left,right)=(%08x, %08x)\n", left, right); usbhid_submit_report(hid, tmff->report, USB_DIR_OUT); return 0; diff --git a/drivers/hid/usbhid/hid-zpff.c b/drivers/hid/usbhid/hid-zpff.c index a7fbffc..5a68827 100644 --- a/drivers/hid/usbhid/hid-zpff.c +++ b/drivers/hid/usbhid/hid-zpff.c @@ -21,10 +21,6 @@ */ -/* #define DEBUG */ - -#define debug(format, arg...) pr_debug("hid-zpff: " format "\n" , ## arg) - #include #include #include @@ -49,14 +45,14 @@ static int hid_zpff_play(struct input_de left = effect->u.rumble.strong_magnitude; right = effect->u.rumble.weak_magnitude; - debug("called with 0x%04x 0x%04x", left, right); + dbg_hid("called with 0x%04x 0x%04x\n", left, right); left = left * 0x7f / 0xffff; right = right * 0x7f / 0xffff; zpff->report->field[2]->value[0] = left; zpff->report->field[3]->value[0] = right; - debug("running with 0x%02x 0x%02x", left, right); + dbg_hid("running with 0x%02x 0x%02x\n", left, right); usbhid_submit_report(hid, zpff->report, USB_DIR_OUT); return 0; diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 488d61b..e793127 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -779,7 +779,7 @@ int hiddev_connect(struct hid_device *hi retval = usb_register_dev(usbhid->intf, &hiddev_class); if (retval) { - err("Not able to get a minor for this device."); + err_hid("Not able to get a minor for this device."); kfree(hiddev); return -1; } diff --git a/drivers/hid/usbhid/usbkbd.c b/drivers/hid/usbhid/usbkbd.c index 1309787..b76b02f 100644 --- a/drivers/hid/usbhid/usbkbd.c +++ b/drivers/hid/usbhid/usbkbd.c @@ -125,7 +125,7 @@ static void usb_kbd_irq(struct urb *urb) resubmit: i = usb_submit_urb (urb, GFP_ATOMIC); if (i) - err ("can't resubmit intr, %s-%s/input0, status %d", + err_hid ("can't resubmit intr, %s-%s/input0, status %d", kbd->usbdev->bus->bus_name, kbd->usbdev->devpath, i); } @@ -151,7 +151,7 @@ static int usb_kbd_event(struct input_de *(kbd->leds) = kbd->newleds; kbd->led->dev = kbd->usbdev; if (usb_submit_urb(kbd->led, GFP_ATOMIC)) - err("usb_submit_urb(leds) failed"); + err_hid("usb_submit_urb(leds) failed"); return 0; } @@ -169,7 +169,7 @@ static void usb_kbd_led(struct urb *urb) *(kbd->leds) = kbd->newleds; kbd->led->dev = kbd->usbdev; if (usb_submit_urb(kbd->led, GFP_ATOMIC)) - err("usb_submit_urb(leds) failed"); + err_hid("usb_submit_urb(leds) failed"); } static int usb_kbd_open(struct input_dev *dev) diff --git a/include/linux/hid.h b/include/linux/hid.h index 827ee74..fe38e75 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -276,6 +276,7 @@ #define HID_QUIRK_LOGITECH_DESCRIPTOR 0 #define HID_QUIRK_DUPLICATE_USAGES 0x00200000 #define HID_QUIRK_RESET_LEDS 0x00400000 #define HID_QUIRK_SWAPPED_MIN_MAX 0x00800000 +#define HID_QUIRK_HIDINPUT 0x01000000 /* * This is the global environment of the parser. This information is @@ -394,6 +395,7 @@ struct hid_control_fifo { #define HID_CLAIMED_INPUT 1 #define HID_CLAIMED_HIDDEV 2 +#define HID_CLAIMED_HIDRAW 4 #define HID_CTRL_RUNNING 1 #define HID_OUT_RUNNING 2 @@ -429,6 +431,7 @@ struct hid_device { /* device repo struct list_head inputs; /* The list of inputs */ void *hiddev; /* The hiddev structure */ + void *hidraw; int minor; /* Hiddev minor number */ wait_queue_head_t wait; /* For sleeping */ @@ -449,6 +452,9 @@ struct hid_device { /* device repo void (*hiddev_hid_event) (struct hid_device *, struct hid_field *field, struct hid_usage *, __s32); void (*hiddev_report_event) (struct hid_device *, struct hid_report *); + + /* handler for raw output data, used by hidraw */ + int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t); #ifdef CONFIG_USB_HIDINPUT_POWERBOOK unsigned long pb_pressed_fn[NBITS(KEY_MAX)]; unsigned long pb_pressed_numlock[NBITS(KEY_MAX)]; @@ -488,6 +494,11 @@ struct hid_descriptor { #define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001)) /* HID core API */ + +#ifdef CONFIG_HID_DEBUG +extern int hid_debug; +#endif + extern void hidinput_hid_event(struct hid_device *, struct hid_field *, struct hid_usage *, __s32); extern void hidinput_report_event(struct hid_device *hid, struct hid_report *report); extern int hidinput_connect(struct hid_device *); @@ -523,14 +534,19 @@ #endif #else static inline int hid_ff_init(struct hid_device *hid) { return -1; } #endif -#ifdef DEBUG -#define dbg(format, arg...) printk(KERN_DEBUG "%s: " format "\n" , \ - __FILE__ , ## arg) + +#ifdef CONFIG_HID_DEBUG +#define dbg_hid(format, arg...) if (hid_debug) \ + printk(KERN_DEBUG "%s: " format ,\ + __FILE__ , ## arg) +#define dbg_hid_line(format, arg...) if (hid_debug) \ + printk(format, ## arg) #else -#define dbg(format, arg...) do {} while (0) +#define dbg_hid(format, arg...) do {} while (0) +#define dbg_hid_line dbg_hid #endif -#define err(format, arg...) printk(KERN_ERR "%s: " format "\n" , \ +#define err_hid(format, arg...) printk(KERN_ERR "%s: " format "\n" , \ __FILE__ , ## arg) #endif diff --git a/include/linux/hidraw.h b/include/linux/hidraw.h new file mode 100644 index 0000000..6676cd5 --- /dev/null +++ b/include/linux/hidraw.h @@ -0,0 +1,86 @@ +#ifndef _HIDRAW_H +#define _HIDRAW_H + +/* + * Copyright (c) 2007 Jiri Kosina + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +struct hidraw_report_descriptor { + __u32 size; + __u8 *value; +}; + +struct hidraw_devinfo { + __u32 bustype; + __s16 vendor; + __s16 product; +}; + +/* ioctl interface */ +#define HIDIOCGRDESCSIZE _IOR('H', 0x01, int) +#define HIDIOCGRDESC _IOR('H', 0x02, struct hidraw_report_descriptor) +#define HIDIOCGRAWINFO _IOR('H', 0x03, struct hidraw_devinfo) + +#define HIDRAW_FIRST_MINOR 0 +#define HIDRAW_MAX_DEVICES 64 +/* number of reports to buffer */ +#define HIDRAW_BUFFER_SIZE 64 + + +/* kernel-only API declarations */ +#ifdef __KERNEL__ + +#include + +struct hidraw { + unsigned int minor; + int exist; + int open; + wait_queue_head_t wait; + struct hid_device *hid; + struct device *dev; + struct list_head list; +}; + +struct hidraw_report { + __u8 *value; + int len; +}; + +struct hidraw_list { + struct hidraw_report buffer[HIDRAW_BUFFER_SIZE]; + int head; + int tail; + struct fasync_struct *fasync; + struct hidraw *hidraw; + struct list_head node; + struct mutex read_mutex; +}; + +#ifdef CONFIG_HIDRAW +int hidraw_init(void); +void hidraw_exit(void); +void hidraw_report_event(struct hid_device *, u8 *, int); +int hidraw_connect(struct hid_device *); +void hidraw_disconnect(struct hid_device *); +#else +static inline int hidraw_init(void) { return 0; } +static inline void hidraw_exit(void) { } +static inline void hidraw_report_event(struct hid_device *hid, u8 *data, int len) { } +static inline int hidraw_connect(struct hid_device *hid) { return -1; } +static inline void hidraw_disconnect(struct hid_device *hid) { } +#endif + +#endif + +#endif