diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/hid/hid-debug.c | 40 | ||||
-rw-r--r-- | drivers/hid/hid-input.c | 92 | ||||
-rw-r--r-- | drivers/hid/usbhid/Kconfig | 11 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-core.c | 16 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-ff.c | 5 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-plff.c | 24 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-quirks.c | 33 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-tmff.c | 161 | ||||
-rw-r--r-- | drivers/hid/usbhid/hiddev.c | 12 |
9 files changed, 308 insertions, 86 deletions
diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index a13757b7898..5c24fe46d8e 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", @@ -697,7 +697,8 @@ 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_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", @@ -715,7 +716,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 +724,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 +740,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 +753,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 +778,3 @@ void hid_resolv_event(__u8 type, __u16 code) { names[type] ? (names[type][code] ? names[type][code] : "?") : "?"); } EXPORT_SYMBOL_GPL(hid_resolv_event); - diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 8edbd30cf79..0c3e12c1794 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_KB(x) (x->vendor == 0x045e && (x->product == 0x00db || x->product == 0x00f9)) + #ifdef CONFIG_USB_HIDINPUT_POWERBOOK struct hidinput_key_translation { @@ -295,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; @@ -310,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); @@ -326,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; } @@ -351,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: @@ -595,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; @@ -611,9 +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; @@ -720,8 +736,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 +760,26 @@ 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_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); + 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 { goto ignore; } @@ -888,6 +931,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"); @@ -991,6 +1039,29 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct return; } + /* 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) { + 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) { + input_event(input, usage->type, key, 1); + last_key = key; + } else { + 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); if ((field->flags & HID_MAIN_ITEM_RELATIVE) && (usage->type == EV_KEY)) @@ -1051,6 +1122,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/Kconfig b/drivers/hid/usbhid/Kconfig index 1b4b572f899..c557d7040a6 100644 --- a/drivers/hid/usbhid/Kconfig +++ b/drivers/hid/usbhid/Kconfig @@ -71,19 +71,20 @@ 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 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-core.c b/drivers/hid/usbhid/hid-core.c index 3a956319585..b38e559b7a4 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -513,7 +513,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; @@ -523,8 +532,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); + } } /* @@ -1082,6 +1093,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) diff --git a/drivers/hid/usbhid/hid-ff.c b/drivers/hid/usbhid/hid-ff.c index 23431fbbc3d..22329feb3b5 100644 --- a/drivers/hid/usbhid/hid-ff.c +++ b/drivers/hid/usbhid/hid-ff.c @@ -62,11 +62,14 @@ 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 }, { 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-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 <anssi.hannula@gmail.com> */ @@ -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 <anssi.hannula@gmail.com>\n"); + printk(KERN_INFO "hid-plff: Force feedback for PantherLord/GreenAsia " + "devices by Anssi Hannula <anssi.hannula@gmail.com>\n"); return 0; } diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 6b21a214f41..41a59a80e7e 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 @@ -86,6 +87,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 @@ -104,12 +108,17 @@ #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 #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 @@ -373,6 +382,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 }, @@ -387,11 +397,16 @@ 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 }, { 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 }, @@ -507,6 +522,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 }, @@ -614,6 +630,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 }, @@ -927,6 +945,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) { @@ -941,6 +971,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); } /** 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 <zinx@epicsol.org>"); + error = input_ff_create_memless(input_dev, tmff, hid_tmff_play); + if (error) + goto fail; + info("Force feedback for ThrustMaster devices by Zinx Verituse <zinx@epicsol.org>"); return 0; + + fail: + kfree(tmff); + return error; } 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 <linux/usb.h> #include <linux/hid.h> #include <linux/hiddev.h> +#include <linux/compat.h> #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 = { |