From 1fe8736da695c2b14961438c73d5600538bd92d9 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Thu, 9 Aug 2007 13:24:11 +0200 Subject: HID: add support for Microsoft Natural Ergonomic Keyboard 4000 This keyboard emits a few usages that are not handled properly by hid-input. The usages from MSVENDOR page are colliding with Chicony Tactical Pad device, so we have to distinguish in runtime. Ugly ... Also, the buttons 1-5 have to be handled in a non-standard way, as they are emitted by the keyboard in a bitfield-like fashion, but the field is not presented as bit-field by the keyboard. The keys can't be pressed simultaneously, so the handling we have is correct. This patch also extends hid_keyboard[] with KPLeftParenthesis and KPRightParenthesis as defined by Keyboard page in HUT 1.12. The corresponding usages are also emitted by this keyboard. Signed-off-by: Jiri Kosina --- drivers/hid/hid-debug.c | 2 +- drivers/hid/hid-input.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index a13757b7898..27e4cf00383 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -697,7 +697,7 @@ static char *keys[KEY_MAX + 1] = { [KEY_DEL_LINE] = "DeleteLine", [KEY_SEND] = "Send", [KEY_REPLY] = "Reply", [KEY_FORWARDMAIL] = "ForwardMail", [KEY_SAVE] = "Save", - [KEY_DOCUMENTS] = "Documents", + [KEY_DOCUMENTS] = "Documents", [KEY_SPELLCHECK] = "SpellCheck", [KEY_FN] = "Fn", [KEY_FN_ESC] = "Fn+ESC", [KEY_FN_1] = "Fn+1", [KEY_FN_2] = "Fn+2", [KEY_FN_B] = "Fn+B", [KEY_FN_D] = "Fn+D", diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 8edbd30cf79..00f326012a3 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -53,7 +53,7 @@ static const unsigned char hid_keyboard[256] = { 115,114,unk,unk,unk,121,unk, 89, 93,124, 92, 94, 95,unk,unk,unk, 122,123, 90, 91, 85,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, - unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, + unk,unk,unk,unk,unk,unk,179,180,unk,unk,unk,unk,unk,unk,unk,unk, unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113, @@ -86,6 +86,10 @@ static const struct { #define map_abs_clear(c) do { map_abs(c); clear_bit(c, bit); } while (0) #define map_key_clear(c) do { map_key(c); clear_bit(c, bit); } while (0) +/* hardware needing special handling due to colliding MSVENDOR page usages */ +#define IS_CHICONY_TACTICAL_PAD(x) (x->vendor == 0x04f2 && device->product == 0x0418) +#define IS_MS_NEK4K(x) (x->vendor == 0x045e && x->product == 0x00db) + #ifdef CONFIG_USB_HIDINPUT_POWERBOOK struct hidinput_key_translation { @@ -614,6 +618,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x19e: map_key_clear(KEY_COFFEE); break; case 0x1a6: map_key_clear(KEY_HELP); break; case 0x1a7: map_key_clear(KEY_DOCUMENTS); break; + case 0x1ab: map_key_clear(KEY_SPELLCHECK); break; case 0x1bc: map_key_clear(KEY_MESSENGER); break; case 0x1bd: map_key_clear(KEY_INFO); break; case 0x201: map_key_clear(KEY_NEW); break; @@ -720,8 +725,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case HID_UP_MSVENDOR: - /* special case - Chicony Chicony KU-0418 tactical pad */ - if (device->vendor == 0x04f2 && device->product == 0x0418) { + /* Unfortunately, there are multiple devices which + * emit usages from MSVENDOR page that require different + * handling. If this list grows too much in the future, + * more general handling will have to be introduced here + * (i.e. another blacklist). + */ + + /* Chicony Chicony KU-0418 tactical pad */ + if (IS_CHICONY_TACTICAL_PAD(device)) { set_bit(EV_REP, input->evbit); switch(usage->hid & HID_USAGE) { case 0xff01: map_key_clear(BTN_1); break; @@ -737,6 +749,20 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0xff0b: map_key_clear(BTN_B); break; default: goto ignore; } + + /* Microsoft Natural Ergonomic Keyboard 4000 */ + } else if (IS_MS_NEK4K(device)) { + switch(usage->hid & HID_USAGE) { + case 0xff05: + set_bit(EV_REP, input->evbit); + map_key_clear(BTN_0); + set_bit(BTN_1, input->keybit); + set_bit(BTN_2, input->keybit); + set_bit(BTN_3, input->keybit); + set_bit(BTN_4, input->keybit); + set_bit(BTN_5, input->keybit); + default: goto ignore; + } } else { goto ignore; } @@ -991,6 +1017,26 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct return; } + /* Handling MS NEK4K special buttons */ + if (IS_MS_NEK4K(hid) && usage->hid == (HID_UP_MSVENDOR | 0xff05)) { + int key = 0; + static int last_key = 0; + switch (value) { + case 0x01: key = BTN_1; break; + case 0x02: key = BTN_2; break; + case 0x04: key = BTN_3; break; + case 0x08: key = BTN_4; break; + case 0x10: key = BTN_5; break; + default: break; + } + if (key) { + input_event(input, usage->type, key, 1); + last_key = key; + } else { + input_event(input, usage->type, last_key, 0); + } + } + input_event(input, usage->type, usage->code, value); if ((field->flags & HID_MAIN_ITEM_RELATIVE) && (usage->type == EV_KEY)) -- cgit v1.2.3 From 933e3187d0042d9381d932757dc1f931d984e56d Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 11 Jul 2007 14:48:58 +0200 Subject: HID: minimal autosuspend support for USB HID devices Autosuspend for USB HID devices remains problematic as far as mice and keyboards are concerned. While I am working on a grand solution, here's a minimalist patch that works for those devices not continously in use. Signed-off-by: Oliver Neukum Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 0a1f2b52a12..a34e0f098f6 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -512,7 +512,16 @@ static int hid_get_class_descriptor(struct usb_device *dev, int ifnum, int usbhid_open(struct hid_device *hid) { - ++hid->open; + struct usbhid_device *usbhid = hid->driver_data; + int res; + + if (!hid->open++) { + res = usb_autopm_get_interface(usbhid->intf); + if (res < 0) { + hid->open--; + return -EIO; + } + } if (hid_start_in(hid)) hid_io_error(hid); return 0; @@ -522,8 +531,10 @@ void usbhid_close(struct hid_device *hid) { struct usbhid_device *usbhid = hid->driver_data; - if (!--hid->open) + if (!--hid->open) { usb_kill_urb(usbhid->urbin); + usb_autopm_put_interface(usbhid->intf); + } } /* @@ -1048,6 +1059,7 @@ static struct usb_driver hid_driver = { .pre_reset = hid_pre_reset, .post_reset = hid_post_reset, .id_table = hid_usb_ids, + .supports_autosuspend = 1, }; static int __init hid_init(void) -- cgit v1.2.3 From b27c9590ca0f44681fe2504a7ec427ff1bb77e82 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 30 Jul 2007 14:56:26 +0200 Subject: HID: add support for Thrustmaster FGT Force Feedback wheel Rework thrustmaster force-feedback module to support devices having different types of force feedback effects. Add signatures of Thrustmaster FGT Rumble Force and Thrustmaster FGT Force Feedback wheels to the list of devices dupported by the module. Parts of the patch were lifted off a simalar patch by Anssi Hannula Signed-off-by: Dmitry Torokhov Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/Kconfig | 5 +- drivers/hid/usbhid/hid-ff.c | 2 + drivers/hid/usbhid/hid-tmff.c | 161 +++++++++++++++++++++++++++++++----------- 3 files changed, 124 insertions(+), 44 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/usbhid/Kconfig b/drivers/hid/usbhid/Kconfig index 1b4b572f899..b27023f23c8 100644 --- a/drivers/hid/usbhid/Kconfig +++ b/drivers/hid/usbhid/Kconfig @@ -79,11 +79,12 @@ config PANTHERLORD_FF to enable force feedback support for it. config THRUSTMASTER_FF - bool "ThrustMaster FireStorm Dual Power 2 support (EXPERIMENTAL)" + bool "ThrustMaster devices support (EXPERIMENTAL)" depends on HID_FF && EXPERIMENTAL select INPUT_FF_MEMLESS if USB_HID help - Say Y here if you have a THRUSTMASTER FireStore Dual Power 2, + Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or + a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel, and want to enable force feedback support for it. Note: if you say N here, this device will still be supported, but without force feedback. diff --git a/drivers/hid/usbhid/hid-ff.c b/drivers/hid/usbhid/hid-ff.c index 23431fbbc3d..5dacd8ec807 100644 --- a/drivers/hid/usbhid/hid-ff.c +++ b/drivers/hid/usbhid/hid-ff.c @@ -67,6 +67,8 @@ static struct hid_ff_initializer inits[] = { #ifdef CONFIG_THRUSTMASTER_FF { 0x44f, 0xb300, hid_tmff_init }, { 0x44f, 0xb304, hid_tmff_init }, + { 0x44f, 0xb651, hid_tmff_init }, /* FGT Rumble Force Wheel */ + { 0x44f, 0xb654, hid_tmff_init }, /* FGT Force Feedback Wheel */ #endif #ifdef CONFIG_ZEROPLUS_FF { 0xc12, 0x0005, hid_zpff_init }, diff --git a/drivers/hid/usbhid/hid-tmff.c b/drivers/hid/usbhid/hid-tmff.c index 555bb48b429..69882a726e9 100644 --- a/drivers/hid/usbhid/hid-tmff.c +++ b/drivers/hid/usbhid/hid-tmff.c @@ -36,16 +36,39 @@ #include "usbhid.h" /* Usages for thrustmaster devices I know about */ -#define THRUSTMASTER_USAGE_RUMBLE_LR (HID_UP_GENDESK | 0xbb) +#define THRUSTMASTER_USAGE_FF (HID_UP_GENDESK | 0xbb) +struct dev_type { + u16 idVendor; + u16 idProduct; + const signed short *ff; +}; + +static const signed short ff_rumble[] = { + FF_RUMBLE, + -1 +}; + +static const signed short ff_joystick[] = { + FF_CONSTANT, + -1 +}; + +static const struct dev_type devices[] = { + { 0x44f, 0xb300, ff_rumble }, + { 0x44f, 0xb304, ff_rumble }, + { 0x44f, 0xb651, ff_rumble }, /* FGT Rumble Force Wheel */ + { 0x44f, 0xb654, ff_joystick }, /* FGT Force Feedback Wheel */ +}; struct tmff_device { struct hid_report *report; - struct hid_field *rumble; + struct hid_field *ff_field; }; /* Changes values from 0 to 0xffff into values from minimum to maximum */ -static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum) +static inline int hid_tmff_scale_u16(unsigned int in, + int minimum, int maximum) { int ret; @@ -57,22 +80,57 @@ static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum) return ret; } +/* Changes values from -0x80 to 0x7f into values from minimum to maximum */ +static inline int hid_tmff_scale_s8(int in, + int minimum, int maximum) +{ + int ret; + + ret = (((in + 0x80) * (maximum - minimum)) / 0xff) + minimum; + if (ret < minimum) + return minimum; + if (ret > maximum) + return maximum; + return ret; +} + static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect) { struct hid_device *hid = input_get_drvdata(dev); struct tmff_device *tmff = data; + struct hid_field *ff_field = tmff->ff_field; + int x, y; int left, right; /* Rumbling */ - left = hid_tmff_scale(effect->u.rumble.weak_magnitude, - tmff->rumble->logical_minimum, tmff->rumble->logical_maximum); - right = hid_tmff_scale(effect->u.rumble.strong_magnitude, - tmff->rumble->logical_minimum, tmff->rumble->logical_maximum); - - tmff->rumble->value[0] = left; - tmff->rumble->value[1] = right; - dbg_hid("(left,right)=(%08x, %08x)\n", left, right); - usbhid_submit_report(hid, tmff->report, USB_DIR_OUT); - + switch (effect->type) { + case FF_CONSTANT: + x = hid_tmff_scale_s8(effect->u.ramp.start_level, + ff_field->logical_minimum, + ff_field->logical_maximum); + y = hid_tmff_scale_s8(effect->u.ramp.end_level, + ff_field->logical_minimum, + ff_field->logical_maximum); + + dbg_hid("(x, y)=(%04x, %04x)\n", x, y); + ff_field->value[0] = x; + ff_field->value[1] = y; + usbhid_submit_report(hid, tmff->report, USB_DIR_OUT); + break; + + case FF_RUMBLE: + left = hid_tmff_scale_u16(effect->u.rumble.weak_magnitude, + ff_field->logical_minimum, + ff_field->logical_maximum); + right = hid_tmff_scale_u16(effect->u.rumble.strong_magnitude, + ff_field->logical_minimum, + ff_field->logical_maximum); + + dbg_hid("(left,right)=(%08x, %08x)\n", left, right); + ff_field->value[0] = left; + ff_field->value[1] = right; + usbhid_submit_report(hid, tmff->report, USB_DIR_OUT); + break; + } return 0; } @@ -82,14 +140,16 @@ int hid_tmff_init(struct hid_device *hid) struct list_head *pos; struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); struct input_dev *input_dev = hidinput->input; + const signed short *ff_bits = ff_joystick; int error; + int i; tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL); if (!tmff) return -ENOMEM; /* Find the report to use */ - __list_for_each(pos, &hid->report_enum[HID_OUTPUT_REPORT].report_list) { + list_for_each(pos, &hid->report_enum[HID_OUTPUT_REPORT].report_list) { struct hid_report *report = (struct hid_report *)pos; int fieldnum; @@ -100,48 +160,65 @@ int hid_tmff_init(struct hid_device *hid) continue; switch (field->usage[0].hid) { - case THRUSTMASTER_USAGE_RUMBLE_LR: - if (field->report_count < 2) { - warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR with report_count < 2"); - continue; - } + case THRUSTMASTER_USAGE_FF: + if (field->report_count < 2) { + warn("ignoring FF field with report_count < 2"); + continue; + } - if (field->logical_maximum == field->logical_minimum) { - warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR with logical_maximum == logical_minimum"); - continue; - } + if (field->logical_maximum == field->logical_minimum) { + warn("ignoring FF field with logical_maximum == logical_minimum"); + continue; + } - if (tmff->report && tmff->report != report) { - warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR in other report"); - continue; - } + if (tmff->report && tmff->report != report) { + warn("ignoring FF field in other report"); + continue; + } - if (tmff->rumble && tmff->rumble != field) { - warn("ignoring duplicate THRUSTMASTER_USAGE_RUMBLE_LR"); - continue; + if (tmff->ff_field && tmff->ff_field != field) { + warn("ignoring duplicate FF field"); + continue; + } + + tmff->report = report; + tmff->ff_field = field; + + for (i = 0; i < ARRAY_SIZE(devices); i++) { + if (input_dev->id.vendor == devices[i].idVendor && + input_dev->id.product == devices[i].idProduct) { + ff_bits = devices[i].ff; + break; } + } - tmff->report = report; - tmff->rumble = field; + for (i = 0; ff_bits[i] >= 0; i++) + set_bit(ff_bits[i], input_dev->ffbit); - set_bit(FF_RUMBLE, input_dev->ffbit); - break; + break; - default: - warn("ignoring unknown output usage %08x", field->usage[0].hid); - continue; + default: + warn("ignoring unknown output usage %08x", field->usage[0].hid); + continue; } } } - error = input_ff_create_memless(input_dev, tmff, hid_tmff_play); - if (error) { - kfree(tmff); - return error; + if (!tmff->report) { + err("cant find FF field in output reports\n"); + error = -ENODEV; + goto fail; } - info("Force feedback for ThrustMaster rumble pad devices by Zinx Verituse "); + error = input_ff_create_memless(input_dev, tmff, hid_tmff_play); + if (error) + goto fail; + info("Force feedback for ThrustMaster devices by Zinx Verituse "); return 0; + + fail: + kfree(tmff); + return error; } -- cgit v1.2.3 From 77b296629d6e938e68de40195a9d500f209f6d10 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Thu, 9 Aug 2007 14:04:56 +0200 Subject: HID: fix whitespace damage Fixes some trivial whitespace damage in hid-input.c Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 00f326012a3..de8dbec6aae 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -299,7 +299,7 @@ static int hidinput_getkeycode(struct input_dev *dev, int scancode, { struct hid_device *hid = dev->private; struct hid_usage *usage; - + usage = hidinput_find_key(hid, scancode, 0); if (usage) { *keycode = usage->code; @@ -314,15 +314,15 @@ static int hidinput_setkeycode(struct input_dev *dev, int scancode, struct hid_device *hid = dev->private; struct hid_usage *usage; int old_keycode; - + if (keycode < 0 || keycode > KEY_MAX) return -EINVAL; - + usage = hidinput_find_key(hid, scancode, 0); if (usage) { old_keycode = usage->code; usage->code = keycode; - + clear_bit(old_keycode, dev->keybit); set_bit(usage->code, dev->keybit); dbg_hid(KERN_DEBUG "Assigned keycode %d to HID usage code %x\n", keycode, scancode); @@ -330,10 +330,10 @@ static int hidinput_setkeycode(struct input_dev *dev, int scancode, * by another key */ if (hidinput_find_key (hid, 0, old_keycode)) set_bit(old_keycode, dev->keybit); - + return 0; } - + return -EINVAL; } -- cgit v1.2.3 From 82eb121989c06e3de33b56ade1fa52c9e86c014e Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Sat, 11 Aug 2007 23:39:42 +0200 Subject: HID: ignore all non-LED usages in output fields in hid-input We have to ignore all non-LED usages in output fields if the report descriptor of the device specifies any. If we don't do so, the devices which contain the same usages both in input and output reports with different parameters will mess things up. In hid-input, we currently care only for the input usages, with exception for LEDs. All other output usages should be properly handled by appropriate force-feedback driver. Fixes auto-calibration for Saitek Cyborg Evo Force joystick. Reported-by: Renato Golin Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index de8dbec6aae..f80b57142cd 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -355,6 +355,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel if (field->flags & HID_MAIN_ITEM_CONSTANT) goto ignore; + /* only LED usages are supported in output fields */ + if (field->report_type == HID_OUTPUT_REPORT && + (usage->hid & HID_USAGE_PAGE) != HID_UP_LED) { + dbg_hid_line(" [non-LED output field] "); + goto ignore; + } + switch (usage->hid & HID_USAGE_PAGE) { case HID_UP_UNDEFINED: -- cgit v1.2.3 From c01d50d181f074a60bf3ed54eb055ce1679afb98 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 20 Aug 2007 12:13:34 +0200 Subject: HID: Report usage codes of keys as EV_MSC scancode events Current HID layer does not report usage codes to the input layer. This feature was previously removed, because it caused unnecessary storm of events in cases of positioning devices, etc. This patch adds reporting of usage codes as EV_MSC events only for key events. We issue the EV_MSC event only if the state of the key corresponding to the given code has changed, so that we don't report usages that are sent in every report even if the state hasn't changed (for example Shift/Caps Lock/... states as sent by various keyboards). This functionality is required at least by KeyTouch in order to provide convenient means for remapping the usage codes. Cc: Marvin Raaijmakers Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index f80b57142cd..36be431888f 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -921,6 +921,11 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel set_bit(KEY_VOLUMEDOWN, input->keybit); } + if (usage->type == EV_KEY) { + set_bit(EV_MSC, input->evbit); + set_bit(MSC_SCAN, input->mscbit); + } + hid_resolv_event(usage->type, usage->code); dbg_hid_line("\n"); @@ -1043,6 +1048,9 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct input_event(input, usage->type, last_key, 0); } } + /* report the usage code as scancode if the key status has changed */ + if (usage->type == EV_KEY && !!test_bit(usage->code, input->key) != value) + input_event(input, EV_MSC, MSC_SCAN, usage->hid); input_event(input, usage->type, usage->code, value); -- cgit v1.2.3 From c4025aded3eab4a50d5070d96ae3a46a25bdc59d Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Wed, 29 Aug 2007 15:53:03 +0200 Subject: USBHID: add CM109 device to blacklist There is a separate driver cm109 for handling this device. Signed-off-by: Alfred E. Heggestad Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-quirks.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 6b21a214f41..334c5fce987 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -86,6 +86,9 @@ #define USB_VENDOR_ID_CIDC 0x1677 +#define USB_VENDOR_ID_CMEDIA 0x0d8c +#define USB_DEVICE_ID_CM109 0x000e + #define USB_VENDOR_ID_CODEMERCS 0x07c0 #define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500 #define USB_DEVICE_ID_CODEMERCS_IOW_LAST 0x15ff @@ -387,6 +390,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_LCM, HID_QUIRK_IGNORE}, { USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_CIDC, 0x0103, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM109, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE, HID_QUIRK_IGNORE }, -- cgit v1.2.3 From 0ce91cf9ce23127932e8b5241d8a7d2bf62df017 Mon Sep 17 00:00:00 2001 From: Stelian Pop Date: Tue, 11 Sep 2007 23:34:55 +0200 Subject: HID: enable hiddev for the SantaRosa MacBookPro IR receiver The infrared remote receiver found in the SantaRosa MacBookPro laptops (MacBookPro3,1) need to be forced to expose a HIDDEV interface (instead of HIDINPUT) so that lirc can access it using the 'macmini' driver. The patch below adds the required quirk for forcing the HIDDEV interface to be activated (HID_QUIRK_HIDDEV) and introduces a new quirk which forces the HIDINPUT interface to be ignored (HID_QUIRK_IGNORE_HIDINPUT). Note that Apple calls this receiver 'IRController4' (info taken from Apple's driver Info.plist). Older Mac{Book,Mini,Pro}s seem to all use the 'IRController1' device (USB id 05ac:8240) which doesn't need those quirks. Signed-off-by: Stelian Pop Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 3 +++ drivers/hid/usbhid/hid-quirks.c | 2 ++ 2 files changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 36be431888f..bd5a29fe85f 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1112,6 +1112,9 @@ int hidinput_connect(struct hid_device *hid) int i, j, k; int max_report_type = HID_OUTPUT_REPORT; + if (hid->quirks & HID_QUIRK_IGNORE_HIDINPUT) + return -1; + INIT_LIST_HEAD(&hid->inputs); for (i = 0; i < hid->maxcollection; i++) diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 334c5fce987..54189b4fe22 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -61,6 +61,7 @@ #define USB_DEVICE_ID_APPLE_GEYSER4_JIS 0x021c #define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b +#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242 #define USB_VENDOR_ID_ASUS 0x0b05 #define USB_DEVICE_ID_ASUS_LCM 0x1726 @@ -376,6 +377,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_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4, HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV, HID_QUIRK_HIDINPUT }, { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01, HID_QUIRK_IGNORE }, -- cgit v1.2.3 From d500d11615db64b99c24f92c07427879581fde4b Mon Sep 17 00:00:00 2001 From: Mike Crowe Date: Fri, 14 Sep 2007 10:18:07 +0200 Subject: USBHID: Add HID_QUIRK_NOGET for ELO Touch Screen 2700 display Use HID_QUIRK_NOGET for the ELO TS2700 touch screen USB HID device in order to avoid a timeout during initialisation. Signed-off-by: Mike Crowe Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-quirks.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 54189b4fe22..91abac6a30c 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -108,6 +108,9 @@ #define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100 #define USB_DEVICE_ID_DELORME_EM_LT20 0x0200 +#define USB_VENDOR_ID_ELO 0x04E7 +#define USB_DEVICE_ID_ELO_TS2700 0x0020 + #define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f #define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100 @@ -513,6 +516,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET }, { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL, HID_QUIRK_NOGET }, { USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_NOGET }, { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, -- cgit v1.2.3 From 5edc41ee8717ef44f6f96347000c8f2c825c823e Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Wed, 19 Sep 2007 16:13:20 +0200 Subject: HID: use hid-plff driver for GreenAsia 0e8f:0003 devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add 0e8f:0003 into the list of devices supported by the hid-plff force feedback driver. These devices identify themselves as "GreenAsia Inc. USB Joystick " and can be either adapters or actual game controllers. The testing was done with a Köng Gaming gamepad. Signed-off-by: Anssi Hannula Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/Kconfig | 6 +++--- drivers/hid/usbhid/hid-ff.c | 3 ++- drivers/hid/usbhid/hid-plff.c | 24 +++++++++++++++++------- 3 files changed, 22 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/usbhid/Kconfig b/drivers/hid/usbhid/Kconfig index b27023f23c8..c557d7040a6 100644 --- a/drivers/hid/usbhid/Kconfig +++ b/drivers/hid/usbhid/Kconfig @@ -71,12 +71,12 @@ config LOGITECH_FF force feedback. config PANTHERLORD_FF - bool "PantherLord USB/PS2 2in1 Adapter support" + bool "PantherLord/GreenAsia based device support" depends on HID_FF select INPUT_FF_MEMLESS if USB_HID help - Say Y here if you have a PantherLord USB/PS2 2in1 Adapter and want - to enable force feedback support for it. + Say Y here if you have a PantherLord/GreenAsia based game controller + or adapter and want to enable force feedback support for it. config THRUSTMASTER_FF bool "ThrustMaster devices support (EXPERIMENTAL)" diff --git a/drivers/hid/usbhid/hid-ff.c b/drivers/hid/usbhid/hid-ff.c index 5dacd8ec807..22329feb3b5 100644 --- a/drivers/hid/usbhid/hid-ff.c +++ b/drivers/hid/usbhid/hid-ff.c @@ -62,7 +62,8 @@ static struct hid_ff_initializer inits[] = { { 0x46d, 0xca03, hid_lgff_init }, /* Logitech MOMO force wheel */ #endif #ifdef CONFIG_PANTHERLORD_FF - { 0x810, 0x0001, hid_plff_init }, + { 0x810, 0x0001, hid_plff_init }, /* "Twin USB Joystick" */ + { 0xe8f, 0x0003, hid_plff_init }, /* "GreenAsia Inc. USB Joystick " */ #endif #ifdef CONFIG_THRUSTMASTER_FF { 0x44f, 0xb300, hid_tmff_init }, diff --git a/drivers/hid/usbhid/hid-plff.c b/drivers/hid/usbhid/hid-plff.c index d6a8f2b49bd..9eb83cf9d22 100644 --- a/drivers/hid/usbhid/hid-plff.c +++ b/drivers/hid/usbhid/hid-plff.c @@ -1,5 +1,15 @@ /* - * Force feedback support for PantherLord USB/PS2 2in1 Adapter devices + * Force feedback support for PantherLord/GreenAsia based devices + * + * The devices are distributed under various names and the same USB device ID + * can be used in both adapters and actual game controllers. + * + * 0810:0001 "Twin USB Joystick" + * - tested with PantherLord USB/PS2 2in1 Adapter + * - contains two reports, one for each port (HID_QUIRK_MULTI_INPUT) + * + * 0e8f:0003 "GreenAsia Inc. USB Joystick " + * - tested with Köng Gaming gamepad * * Copyright (c) 2007 Anssi Hannula */ @@ -67,11 +77,11 @@ int hid_plff_init(struct hid_device *hid) struct input_dev *dev; int error; - /* The device contains 2 output reports (one for each - HID_QUIRK_MULTI_INPUT device), both containing 1 field, which - contains 4 ff00.0002 usages and 4 16bit absolute values. + /* The device contains one output report per physical device, all + containing 1 field, which contains 4 ff00.0002 usages and 4 16bit + absolute values. - The 2 input reports also contain a field which contains + The input reports also contain a field which contains 8 ff00.0001 usages and 8 boolean values. Their meaning is currently unknown. */ @@ -122,8 +132,8 @@ int hid_plff_init(struct hid_device *hid) usbhid_submit_report(hid, plff->report, USB_DIR_OUT); } - printk(KERN_INFO "hid-plff: Force feedback for PantherLord USB/PS2 " - "2in1 Adapters by Anssi Hannula \n"); + printk(KERN_INFO "hid-plff: Force feedback for PantherLord/GreenAsia " + "devices by Anssi Hannula \n"); return 0; } -- cgit v1.2.3 From f14d5d206cf84357a7072ddb2bbc7d3454639f11 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Tue, 25 Sep 2007 16:32:08 +0200 Subject: HID: fix input mapping for Microsoft Ergonomic Keyboard Special keys 1-5 on Microsoft Ergonomic Keyboard were mistakenly mapped to buttons, which doesn't make a lot of sense. Fix this mapping to KEY_F{13,18}. Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index bd5a29fe85f..8be28d205d6 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -762,12 +762,12 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel switch(usage->hid & HID_USAGE) { case 0xff05: set_bit(EV_REP, input->evbit); - map_key_clear(BTN_0); - set_bit(BTN_1, input->keybit); - set_bit(BTN_2, input->keybit); - set_bit(BTN_3, input->keybit); - set_bit(BTN_4, input->keybit); - set_bit(BTN_5, input->keybit); + map_key_clear(KEY_F13); + set_bit(KEY_F14, input->keybit); + set_bit(KEY_F15, input->keybit); + set_bit(KEY_F16, input->keybit); + set_bit(KEY_F17, input->keybit); + set_bit(KEY_F18, input->keybit); default: goto ignore; } } else { @@ -1034,11 +1034,11 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct int key = 0; static int last_key = 0; switch (value) { - case 0x01: key = BTN_1; break; - case 0x02: key = BTN_2; break; - case 0x04: key = BTN_3; break; - case 0x08: key = BTN_4; break; - case 0x10: key = BTN_5; break; + case 0x01: key = KEY_F14; break; + case 0x02: key = KEY_F15; break; + case 0x04: key = KEY_F16; break; + case 0x08: key = KEY_F17; break; + case 0x10: key = KEY_F18; break; default: break; } if (key) { -- cgit v1.2.3 From 0ce1ac3b3ca0f44682aed210ba6062bafc2e32fa Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 26 Sep 2007 16:29:53 +0200 Subject: HID: trivial fixes in hid-debug - added KERN_DEBUG to output lines - fixed preffered -> preferred typo - added const to char *'s Also, exported symbol hid_resolv_event is unused by the current kernel tree and perhaps should be removed. Signed-off-by: Joe Perches Signed-off-by: Jiri Kosina --- drivers/hid/hid-debug.c | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 27e4cf00383..5da9f676b4c 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -34,7 +34,7 @@ struct hid_usage_entry { unsigned page; unsigned usage; - char *description; + const char *description; }; static const struct hid_usage_entry hid_usage_table[] = { @@ -365,8 +365,8 @@ void hid_resolv_usage(unsigned usage) { } EXPORT_SYMBOL_GPL(hid_resolv_usage); -__inline__ static void tab(int n) { - while (n--) printk(" "); +static void tab(int n) { + printk(KERN_DEBUG "%*s", n, ""); } void hid_dump_field(struct hid_field *field, int n) { @@ -401,8 +401,8 @@ void hid_dump_field(struct hid_field *field, int n) { tab(n); printk("Unit Exponent(%d)\n", field->unit_exponent); } if (field->unit) { - char *systems[5] = { "None", "SI Linear", "SI Rotation", "English Linear", "English Rotation" }; - char *units[5][8] = { + static const char *systems[5] = { "None", "SI Linear", "SI Rotation", "English Linear", "English Rotation" }; + static const char *units[5][8] = { { "None", "None", "None", "None", "None", "None", "None", "None" }, { "None", "Centimeter", "Gram", "Seconds", "Kelvin", "Ampere", "Candela", "None" }, { "None", "Radians", "Gram", "Seconds", "Kelvin", "Ampere", "Candela", "None" }, @@ -457,7 +457,7 @@ void hid_dump_field(struct hid_field *field, int n) { printk("%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute "); printk("%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : ""); printk("%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : ""); - printk("%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPrefferedState " : ""); + printk("%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPreferredState " : ""); printk("%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : ""); printk("%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : ""); printk("%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : ""); @@ -470,7 +470,7 @@ void hid_dump_device(struct hid_device *device) { struct hid_report *report; struct list_head *list; unsigned i,k; - static char *table[] = {"INPUT", "OUTPUT", "FEATURE"}; + static const char *table[] = {"INPUT", "OUTPUT", "FEATURE"}; if (!hid_debug) return; @@ -501,13 +501,13 @@ void hid_dump_input(struct hid_usage *usage, __s32 value) { if (!hid_debug) return; - printk("hid-debug: input "); + printk(KERN_DEBUG "hid-debug: input "); hid_resolv_usage(usage->hid); printk(" = %d\n", value); } EXPORT_SYMBOL_GPL(hid_dump_input); -static char *events[EV_MAX + 1] = { +static const char *events[EV_MAX + 1] = { [EV_SYN] = "Sync", [EV_KEY] = "Key", [EV_REL] = "Relative", [EV_ABS] = "Absolute", [EV_MSC] = "Misc", [EV_LED] = "LED", @@ -516,10 +516,10 @@ static char *events[EV_MAX + 1] = { [EV_FF_STATUS] = "ForceFeedbackStatus", }; -static char *syncs[2] = { +static const char *syncs[2] = { [SYN_REPORT] = "Report", [SYN_CONFIG] = "Config", }; -static char *keys[KEY_MAX + 1] = { +static const char *keys[KEY_MAX + 1] = { [KEY_RESERVED] = "Reserved", [KEY_ESC] = "Esc", [KEY_1] = "1", [KEY_2] = "2", [KEY_3] = "3", [KEY_4] = "4", @@ -715,7 +715,7 @@ static char *keys[KEY_MAX + 1] = { [KEY_SWITCHVIDEOMODE] = "SwitchVideoMode", }; -static char *relatives[REL_MAX + 1] = { +static const char *relatives[REL_MAX + 1] = { [REL_X] = "X", [REL_Y] = "Y", [REL_Z] = "Z", [REL_RX] = "Rx", [REL_RY] = "Ry", [REL_RZ] = "Rz", @@ -723,7 +723,7 @@ static char *relatives[REL_MAX + 1] = { [REL_WHEEL] = "Wheel", [REL_MISC] = "Misc", }; -static char *absolutes[ABS_MAX + 1] = { +static const char *absolutes[ABS_MAX + 1] = { [ABS_X] = "X", [ABS_Y] = "Y", [ABS_Z] = "Z", [ABS_RX] = "Rx", [ABS_RY] = "Ry", [ABS_RZ] = "Rz", @@ -739,12 +739,12 @@ static char *absolutes[ABS_MAX + 1] = { [ABS_VOLUME] = "Volume", [ABS_MISC] = "Misc", }; -static char *misc[MSC_MAX + 1] = { +static const char *misc[MSC_MAX + 1] = { [MSC_SERIAL] = "Serial", [MSC_PULSELED] = "Pulseled", [MSC_GESTURE] = "Gesture", [MSC_RAW] = "RawData" }; -static char *leds[LED_MAX + 1] = { +static const char *leds[LED_MAX + 1] = { [LED_NUML] = "NumLock", [LED_CAPSL] = "CapsLock", [LED_SCROLLL] = "ScrollLock", [LED_COMPOSE] = "Compose", [LED_KANA] = "Kana", [LED_SLEEP] = "Sleep", @@ -752,16 +752,16 @@ static char *leds[LED_MAX + 1] = { [LED_MISC] = "Misc", }; -static char *repeats[REP_MAX + 1] = { +static const char *repeats[REP_MAX + 1] = { [REP_DELAY] = "Delay", [REP_PERIOD] = "Period" }; -static char *sounds[SND_MAX + 1] = { +static const char *sounds[SND_MAX + 1] = { [SND_CLICK] = "Click", [SND_BELL] = "Bell", [SND_TONE] = "Tone" }; -static char **names[EV_MAX + 1] = { +static const char **names[EV_MAX + 1] = { [EV_SYN] = syncs, [EV_KEY] = keys, [EV_REL] = relatives, [EV_ABS] = absolutes, [EV_MSC] = misc, [EV_LED] = leds, @@ -777,4 +777,3 @@ void hid_resolv_event(__u8 type, __u16 code) { names[type] ? (names[type][code] ? names[type][code] : "?") : "?"); } EXPORT_SYMBOL_GPL(hid_resolv_event); - -- cgit v1.2.3 From 08f06177f4089abeba904cc12a2a50cffa9ffce6 Mon Sep 17 00:00:00 2001 From: Tomoya Adachi Date: Wed, 3 Oct 2007 23:27:49 +0200 Subject: USBHID: report descriptor fix for MacBook JIS keyboard This patch fixes the problem, that Japanese MacBook doesn't recognize some keys like '\'(yen, or backslash), '|'(pipe), and '_'(underscore). It is due to that MacBook JIS keyboard (jp106) sends wrong report descriptor. It saids "logical maximum = 0x65", so Keyboard.0089 is mapped to Key.Unknown, while it should be accepted as Key.Yen. Signed-off-by: Tomoya Adachi Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-quirks.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 91abac6a30c..a0d6f081ee5 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -624,6 +624,8 @@ static const struct hid_rdesc_blacklist { { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER, HID_QUIRK_RDESC_LOGITECH }, { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2, HID_QUIRK_RDESC_LOGITECH }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS, HID_QUIRK_RDESC_MACBOOK_JIS }, + { USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_RDESC_PETALYNX }, { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1, HID_QUIRK_RDESC_SWAPPED_MIN_MAX }, @@ -937,6 +939,18 @@ static void usbhid_fixup_cypress_descriptor(unsigned char *rdesc, int rsize) printk(KERN_INFO "Fixing up Cypress report descriptor\n"); } +/* + * MacBook JIS keyboard has wrong logical maximum + */ +static void usbhid_fixup_macbook_descriptor(unsigned char *rdesc, int rsize) +{ + if (rsize >= 60 && rdesc[53] == 0x65 + && rdesc[59] == 0x65) { + printk(KERN_INFO "Fixing up MacBook JIS keyboard report descriptor\n"); + rdesc[53] = rdesc[59] = 0xe7; + } +} + static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned rsize) { @@ -951,6 +965,9 @@ static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned if (quirks & HID_QUIRK_RDESC_PETALYNX) usbhid_fixup_petalynx_descriptor(rdesc, rsize); + + if (quirks & HID_QUIRK_RDESC_MACBOOK_JIS) + usbhid_fixup_macbook_descriptor(rdesc, rsize); } /** -- cgit v1.2.3 From e2bca0749c3fef4e3eb39e8aaca7a0c923a70396 Mon Sep 17 00:00:00 2001 From: Khelben Blackstaff Date: Thu, 4 Oct 2007 10:32:03 +0200 Subject: Input: add KEY_LOGOFF HUT 1.12 defines Logoff usage 0x19c in Consumer page. There are keyboards out there emitting this usage code (for example Microsoft Wireless Laser Keyboard 6000). Add this key so that HID code could map usages to it. Signed-off-by: Khelben Blackstaff Signed-off-by: Dmitry Torokhov Signed-off-by: Jiri Kosina --- drivers/hid/hid-debug.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 5da9f676b4c..5c24fe46d8e 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -698,6 +698,7 @@ static const char *keys[KEY_MAX + 1] = { [KEY_SEND] = "Send", [KEY_REPLY] = "Reply", [KEY_FORWARDMAIL] = "ForwardMail", [KEY_SAVE] = "Save", [KEY_DOCUMENTS] = "Documents", [KEY_SPELLCHECK] = "SpellCheck", + [KEY_LOGOFF] = "Logoff", [KEY_FN] = "Fn", [KEY_FN_ESC] = "Fn+ESC", [KEY_FN_1] = "Fn+1", [KEY_FN_2] = "Fn+2", [KEY_FN_B] = "Fn+B", [KEY_FN_D] = "Fn+D", -- cgit v1.2.3 From 3cc5f916050be1d1910c5dd5732110b0a8f27ac8 Mon Sep 17 00:00:00 2001 From: Khelben Blackstaff Date: Thu, 4 Oct 2007 10:35:41 +0200 Subject: HID: add support for Microsoft Wireless Laser Keyboard 6000 This keyboard emits a few usages that are not handled properly by hid-input. Changed IS_MS_NEK4K macro to IS_MS_KB to reflect the addition of another keyboard. Signed-off-by: Khelben Blackstaff Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 8be28d205d6..0c3e12c1794 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -88,7 +88,7 @@ static const struct { /* hardware needing special handling due to colliding MSVENDOR page usages */ #define IS_CHICONY_TACTICAL_PAD(x) (x->vendor == 0x04f2 && device->product == 0x0418) -#define IS_MS_NEK4K(x) (x->vendor == 0x045e && x->product == 0x00db) +#define IS_MS_KB(x) (x->vendor == 0x045e && (x->product == 0x00db || x->product == 0x00f9)) #ifdef CONFIG_USB_HIDINPUT_POWERBOOK @@ -606,6 +606,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x0f6: map_key_clear(KEY_NEXT); break; case 0x0fa: map_key_clear(KEY_BACK); break; + case 0x182: map_key_clear(KEY_BOOKMARKS); break; case 0x183: map_key_clear(KEY_CONFIG); break; case 0x184: map_key_clear(KEY_WORDPROCESSOR); break; case 0x185: map_key_clear(KEY_EDITOR); break; @@ -622,10 +623,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x192: map_key_clear(KEY_CALC); break; case 0x194: map_key_clear(KEY_FILE); break; case 0x196: map_key_clear(KEY_WWW); break; + case 0x19c: map_key_clear(KEY_LOGOFF); break; case 0x19e: map_key_clear(KEY_COFFEE); break; case 0x1a6: map_key_clear(KEY_HELP); break; case 0x1a7: map_key_clear(KEY_DOCUMENTS); break; case 0x1ab: map_key_clear(KEY_SPELLCHECK); break; + case 0x1b6: map_key_clear(KEY_MEDIA); break; + case 0x1b7: map_key_clear(KEY_SOUND); break; case 0x1bc: map_key_clear(KEY_MESSENGER); break; case 0x1bd: map_key_clear(KEY_INFO); break; case 0x201: map_key_clear(KEY_NEW); break; @@ -758,8 +762,14 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel } /* Microsoft Natural Ergonomic Keyboard 4000 */ - } else if (IS_MS_NEK4K(device)) { + } else if (IS_MS_KB(device)) { switch(usage->hid & HID_USAGE) { + case 0xfd06: + map_key_clear(KEY_CHAT); + break; + case 0xfd07: + map_key_clear(KEY_PHONE); + break; case 0xff05: set_bit(EV_REP, input->evbit); map_key_clear(KEY_F13); @@ -1029,8 +1039,8 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct return; } - /* Handling MS NEK4K special buttons */ - if (IS_MS_NEK4K(hid) && usage->hid == (HID_UP_MSVENDOR | 0xff05)) { + /* Handling MS keyboards special buttons */ + if (IS_MS_KB(hid) && usage->hid == (HID_UP_MSVENDOR | 0xff05)) { int key = 0; static int last_key = 0; switch (value) { -- cgit v1.2.3 From 446943593e6e2cb7133cb6c969afd4626c96a916 Mon Sep 17 00:00:00 2001 From: Ilya Frolov Date: Thu, 11 Oct 2007 14:21:23 +0200 Subject: HID: Add GeneralTouch touchscreen to the blacklist GeneralTouch touchscreens are handled by usbtouchscreen driver, make sure HID ignores them. Signed-off-by: Ilya Frolov Signed-off-by: Dmitry Torokhov Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-quirks.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index a0d6f081ee5..41a59a80e7e 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -117,6 +117,8 @@ #define USB_VENDOR_ID_GAMERON 0x0810 #define USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR 0x0001 +#define USB_VENDOR_ID_GENERAL_TOUCH 0x0dfc + #define USB_VENDOR_ID_GLAB 0x06c2 #define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038 #define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039 @@ -401,6 +403,10 @@ static const struct hid_blacklist { { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GENERAL_TOUCH, 0x0001, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GENERAL_TOUCH, 0x0002, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GENERAL_TOUCH, 0x0003, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GENERAL_TOUCH, 0x0004, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT, HID_QUIRK_IGNORE }, -- cgit v1.2.3 From bb6c8d8fa9b5587eea18078ce0bcf6bb2905789f Mon Sep 17 00:00:00 2001 From: Philip Langdale Date: Sun, 14 Oct 2007 12:03:58 +0200 Subject: HID: hiddev: Add 32bit ioctl compatibilty The hiddev driver currently lacks 32bit ioctl compatibility, so if you're running with a 64bit kernel and 32bit userspace, it won't work. I'm pretty sure that the only thing missing is a compat_ioctl implementation as all structs have fixed size fields. With this change I can use revoco to configure my MX Revolution mouse. Signed-off-by: Philip Langdale Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hiddev.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index e793127f971..9837adcb17e 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "usbhid.h" #ifdef CONFIG_USB_DYNAMIC_MINORS @@ -738,6 +739,14 @@ inval: return -EINVAL; } +#ifdef CONFIG_COMPAT +static long hiddev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct inode *inode = file->f_path.dentry->d_inode; + return hiddev_ioctl(inode, file, cmd, compat_ptr(arg)); +} +#endif + static const struct file_operations hiddev_fops = { .owner = THIS_MODULE, .read = hiddev_read, @@ -747,6 +756,9 @@ static const struct file_operations hiddev_fops = { .release = hiddev_release, .ioctl = hiddev_ioctl, .fasync = hiddev_fasync, +#ifdef CONFIG_COMPAT + .compat_ioctl = hiddev_compat_ioctl, +#endif }; static struct usb_class_driver hiddev_class = { -- cgit v1.2.3 From efc493f9d5463d933a64a2758fbe6d9bb8300cbb Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 14 May 2007 09:54:30 +0200 Subject: 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/usbhid/hid-core.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 0a1f2b52a12..74b817d7968 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -628,6 +628,28 @@ static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid) 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; @@ -871,6 +893,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) 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: -- cgit v1.2.3 From 86166b7bcda0bcb53525114fa1c87ac432be478e Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 14 May 2007 09:57:40 +0200 Subject: 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 --- drivers/hid/Kconfig | 19 ++ drivers/hid/Makefile | 2 + drivers/hid/hid-core.c | 16 ++ drivers/hid/hidraw.c | 395 ++++++++++++++++++++++++++++++++++++++++++ drivers/hid/usbhid/hid-core.c | 15 +- 5 files changed, 445 insertions(+), 2 deletions(-) create mode 100644 drivers/hid/hidraw.c (limited to 'drivers') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 19667fcc722..cacf89e65af 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -46,6 +46,25 @@ config HID_DEBUG If unsure, say N +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. + + 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 68d1376a53f..1ac5103f7c9 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 317cf8a7b63..2884b036495 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -30,6 +30,7 @@ #include #include #include +#include /* * Version Information @@ -979,6 +980,8 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i 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); @@ -990,5 +993,18 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i } 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/hidraw.c b/drivers/hid/hidraw.c new file mode 100644 index 00000000000..20ebba1032b --- /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 74b817d7968..3a956319585 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "usbhid.h" /* @@ -932,6 +933,8 @@ static void hid_disconnect(struct usb_interface *intf) 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); @@ -964,11 +967,13 @@ static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id) 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; } @@ -984,10 +989,16 @@ static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id) 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++) { -- cgit v1.2.3 From 709d27c04f4eccbc99d57a5569bce028915a4345 Mon Sep 17 00:00:00 2001 From: Mariusz Kozlowski Date: Thu, 27 Sep 2007 11:24:55 +0200 Subject: HID: hidraw_connect() memleak fix It looks like hidraw_connect() is leaking memory in case of failure. Also it should return -ENOMEM when kzalloc fails. Signed-off-by: Mariusz Kozlowski Signed-off-by: Jiri Kosina --- drivers/hid/hidraw.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 20ebba1032b..8503197a813 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -282,7 +282,7 @@ EXPORT_SYMBOL_GPL(hidraw_report_event); int hidraw_connect(struct hid_device *hid) { - int minor, result = -EINVAL; + int minor, result; struct hidraw *dev; /* TODO currently we accept any HID device. This should later @@ -290,8 +290,11 @@ int hidraw_connect(struct hid_device *hid) * non-input applications */ - if (!(dev = kzalloc(sizeof(struct hidraw), GFP_KERNEL))) - return -1; + dev = kzalloc(sizeof(struct hidraw), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + result = -EINVAL; spin_lock(&minors_lock); @@ -305,8 +308,10 @@ int hidraw_connect(struct hid_device *hid) spin_unlock(&minors_lock); - if (result) + if (result) { + kfree(dev); goto out; + } dev->dev = device_create(hidraw_class, NULL, MKDEV(hidraw_major, minor), "%s%d", "hidraw", minor); @@ -316,6 +321,7 @@ int hidraw_connect(struct hid_device *hid) hidraw_table[minor] = NULL; spin_unlock(&minors_lock); result = PTR_ERR(dev->dev); + kfree(dev); goto out; } -- cgit v1.2.3