diff options
Diffstat (limited to 'drivers/hid')
30 files changed, 1968 insertions, 341 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 34402bb..2685bee 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -56,20 +56,20 @@ menu "Special HID drivers" depends on HID config HID_3M_PCT - tristate "3M PCT" + tristate "3M PCT touchscreen" depends on USB_HID ---help--- Support for 3M PCT touch screens. config HID_A4TECH - tristate "A4 tech" if EMBEDDED + tristate "A4 tech mice" if EMBEDDED depends on USB_HID default !EMBEDDED ---help--- Support for A4 tech X5 and WOP-35 / Trust 450L mice. config HID_ACRUX_FF - tristate "ACRUX force feedback support" + tristate "ACRUX force feedback" depends on USB_HID select INPUT_FF_MEMLESS ---help--- @@ -77,7 +77,7 @@ config HID_ACRUX_FF game controllers. config HID_APPLE - tristate "Apple" if EMBEDDED + tristate "Apple {i,Power,Mac}Books" if EMBEDDED depends on (USB_HID || BT_HIDP) default !EMBEDDED ---help--- @@ -88,7 +88,7 @@ config HID_APPLE MacBooks, MacBook Pros and Apple Aluminum. config HID_BELKIN - tristate "Belkin" if EMBEDDED + tristate "Belkin Flip KVM and Wireless keyboard" if EMBEDDED depends on USB_HID default !EMBEDDED ---help--- @@ -101,14 +101,14 @@ config HID_CANDO Support for Cando dual touch panel. config HID_CHERRY - tristate "Cherry" if EMBEDDED + tristate "Cherry Cymotion keyboard" if EMBEDDED depends on USB_HID default !EMBEDDED ---help--- Support for Cherry Cymotion keyboard. config HID_CHICONY - tristate "Chicony" if EMBEDDED + tristate "Chicony Tactical pad" if EMBEDDED depends on USB_HID default !EMBEDDED ---help--- @@ -130,20 +130,20 @@ config HID_PRODIKEYS and some additional multimedia keys. config HID_CYPRESS - tristate "Cypress" if EMBEDDED + tristate "Cypress mouse and barcode readers" if EMBEDDED depends on USB_HID default !EMBEDDED ---help--- Support for cypress mouse and barcode readers. config HID_DRAGONRISE - tristate "DragonRise Inc. support" + tristate "DragonRise Inc. game controller" depends on USB_HID ---help--- Say Y here if you have DragonRise Inc.game controllers. config DRAGONRISE_FF - bool "DragonRise Inc. force feedback support" + bool "DragonRise Inc. force feedback" depends on HID_DRAGONRISE select INPUT_FF_MEMLESS ---help--- @@ -157,20 +157,20 @@ config HID_EGALAX Support for the eGalax dual-touch panel. config HID_ELECOM - tristate "ELECOM" + tristate "ELECOM BM084 bluetooth mouse" depends on BT_HIDP ---help--- Support for the ELECOM BM084 (bluetooth mouse). config HID_EZKEY - tristate "Ezkey" if EMBEDDED + tristate "Ezkey BTC 8193 keyboard" if EMBEDDED depends on USB_HID default !EMBEDDED ---help--- Support for Ezkey BTC 8193 keyboard. config HID_KYE - tristate "Kye" if EMBEDDED + tristate "Kye/Genius Ergo Mouse" if EMBEDDED depends on USB_HID default !EMBEDDED ---help--- @@ -183,26 +183,26 @@ config HID_UCLOGIC Support for UC-Logic tablets. config HID_GYRATION - tristate "Gyration" + tristate "Gyration remote control" depends on USB_HID ---help--- Support for Gyration remote control. config HID_TWINHAN - tristate "Twinhan" + tristate "Twinhan IR remote control" depends on USB_HID ---help--- Support for Twinhan IR remote control. config HID_KENSINGTON - tristate "Kensington" if EMBEDDED + tristate "Kensington Slimblade Trackball" if EMBEDDED depends on USB_HID default !EMBEDDED ---help--- Support for Kensington Slimblade Trackball. config HID_LOGITECH - tristate "Logitech" if EMBEDDED + tristate "Logitech devices" if EMBEDDED depends on USB_HID default !EMBEDDED ---help--- @@ -226,12 +226,12 @@ config LOGITECH_FF force feedback. config LOGIRUMBLEPAD2_FF - bool "Logitech Rumblepad 2 force feedback support" + bool "Logitech RumblePad/Rumblepad 2 force feedback support" depends on HID_LOGITECH select INPUT_FF_MEMLESS help Say Y here if you want to enable force feedback support for Logitech - Rumblepad 2 devices. + RumblePad and Rumblepad 2 devices. config LOGIG940_FF bool "Logitech Flight System G940 force feedback support" @@ -241,6 +241,14 @@ config LOGIG940_FF Say Y here if you want to enable force feedback support for Logitech Flight System G940 devices. +config LOGIWII_FF + bool "Logitech Speed Force Wireless force feedback support" + depends on HID_LOGITECH + select INPUT_FF_MEMLESS + help + Say Y here if you want to enable force feedback support for Logitech + Speed Force Wireless (Wii) devices. + config HID_MAGICMOUSE tristate "Apple MagicMouse multi-touch support" depends on BT_HIDP @@ -251,39 +259,39 @@ config HID_MAGICMOUSE Apple Wireless "Magic" Mouse. config HID_MICROSOFT - tristate "Microsoft" if EMBEDDED + tristate "Microsoft non-fully HID-compliant devices" if EMBEDDED depends on USB_HID default !EMBEDDED ---help--- Support for Microsoft devices that are not fully compliant with HID standard. config HID_MOSART - tristate "MosArt" + tristate "MosArt dual-touch panels" depends on USB_HID ---help--- Support for MosArt dual-touch panels. config HID_MONTEREY - tristate "Monterey" if EMBEDDED + tristate "Monterey Genius KB29E keyboard" if EMBEDDED depends on USB_HID default !EMBEDDED ---help--- Support for Monterey Genius KB29E. config HID_NTRIG - tristate "NTrig" + tristate "N-Trig touch screen" depends on USB_HID ---help--- Support for N-Trig touch screen. config HID_ORTEK - tristate "Ortek" + tristate "Ortek WKB-2000 wireless keyboard and mouse trackpad" depends on USB_HID ---help--- Support for Ortek WKB-2000 wireless keyboard + mouse trackpad. config HID_PANTHERLORD - tristate "Pantherlord support" + tristate "Pantherlord/GreenAsia game controller" depends on USB_HID ---help--- Say Y here if you have a PantherLord/GreenAsia based game controller @@ -298,7 +306,7 @@ config PANTHERLORD_FF or adapter and want to enable force feedback support for it. config HID_PETALYNX - tristate "Petalynx" + tristate "Petalynx Maxter remote control" depends on USB_HID ---help--- Support for Petalynx Maxter remote control. @@ -362,7 +370,7 @@ config HID_PICOLCD_LEDS Provide access to PicoLCD's GPO pins via leds class. config HID_QUANTA - tristate "Quanta Optical Touch" + tristate "Quanta Optical Touch panels" depends on USB_HID ---help--- Support for Quanta Optical Touch dual-touch panels. @@ -382,32 +390,39 @@ config HID_ROCCAT_KONE ---help--- Support for Roccat Kone mouse. +config HID_ROCCAT_PYRA + tristate "Roccat Pyra mouse support" + depends on USB_HID + select HID_ROCCAT + ---help--- + Support for Roccat Pyra mouse. + config HID_SAMSUNG - tristate "Samsung" + tristate "Samsung InfraRed remote control or keyboards" depends on USB_HID ---help--- Support for Samsung InfraRed remote control or keyboards. config HID_SONY - tristate "Sony" + tristate "Sony PS3 controller" depends on USB_HID ---help--- Support for Sony PS3 controller. config HID_STANTUM - tristate "Stantum" + tristate "Stantum multitouch panel" depends on USB_HID ---help--- Support for Stantum multitouch panel. config HID_SUNPLUS - tristate "Sunplus" + tristate "Sunplus wireless desktop" depends on USB_HID ---help--- Support for Sunplus wireless desktop. config HID_GREENASIA - tristate "GreenAsia (Product ID 0x12) support" + tristate "GreenAsia (Product ID 0x12) game controller support" depends on USB_HID ---help--- Say Y here if you have a GreenAsia (Product ID 0x12) based game diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 8c4a510..62ab138 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -21,6 +21,9 @@ endif ifdef CONFIG_LOGIG940_FF hid-logitech-objs += hid-lg3ff.o endif +ifdef CONFIG_LOGIWII_FF + hid-logitech-objs += hid-lg4ff.o +endif obj-$(CONFIG_HID_3M_PCT) += hid-3m-pct.o obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o @@ -52,6 +55,7 @@ obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o obj-$(CONFIG_HID_ROCCAT_KONE) += hid-roccat-kone.o +obj-$(CONFIG_HID_ROCCAT_PYRA) += hid-roccat-pyra.o obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o obj-$(CONFIG_HID_SONY) += hid-sony.o diff --git a/drivers/hid/hid-3m-pct.c b/drivers/hid/hid-3m-pct.c index 2a0d56b..02d8cd3 100644 --- a/drivers/hid/hid-3m-pct.c +++ b/drivers/hid/hid-3m-pct.c @@ -2,6 +2,8 @@ * HID driver for 3M PCT multitouch panels * * Copyright (c) 2009-2010 Stephane Chatty <chatty@enac.fr> + * Copyright (c) 2010 Henrik Rydberg <rydberg@euromail.se> + * Copyright (c) 2010 Canonical, Ltd. * */ @@ -24,15 +26,26 @@ MODULE_LICENSE("GPL"); #include "hid-ids.h" +#define MAX_SLOTS 60 +#define MAX_TRKID USHRT_MAX +#define MAX_EVENTS 360 + +/* estimated signal-to-noise ratios */ +#define SN_MOVE 2048 +#define SN_WIDTH 128 + struct mmm_finger { __s32 x, y, w, h; - __u8 rank; + __u16 id; + bool prev_touch; bool touch, valid; }; struct mmm_data { - struct mmm_finger f[10]; - __u8 curid, num; + struct mmm_finger f[MAX_SLOTS]; + __u16 id; + __u8 curid; + __u8 nexp, nreal; bool touch, valid; }; @@ -40,6 +53,10 @@ static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) { + int f1 = field->logical_minimum; + int f2 = field->logical_maximum; + int df = f2 - f1; + switch (usage->hid & HID_USAGE_PAGE) { case HID_UP_BUTTON: @@ -50,18 +67,20 @@ static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_GD_X: hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_POSITION_X); + input_set_abs_params(hi->input, ABS_MT_POSITION_X, + f1, f2, df / SN_MOVE, 0); /* touchscreen emulation */ input_set_abs_params(hi->input, ABS_X, - field->logical_minimum, - field->logical_maximum, 0, 0); + f1, f2, df / SN_MOVE, 0); return 1; case HID_GD_Y: hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_POSITION_Y); + input_set_abs_params(hi->input, ABS_MT_POSITION_Y, + f1, f2, df / SN_MOVE, 0); /* touchscreen emulation */ input_set_abs_params(hi->input, ABS_Y, - field->logical_minimum, - field->logical_maximum, 0, 0); + f1, f2, df / SN_MOVE, 0); return 1; } return 0; @@ -81,21 +100,31 @@ static int mmm_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_DG_TIPSWITCH: /* touchscreen emulation */ hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); + input_set_capability(hi->input, EV_KEY, BTN_TOUCH); return 1; case HID_DG_WIDTH: hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_TOUCH_MAJOR); + input_set_abs_params(hi->input, ABS_MT_TOUCH_MAJOR, + f1, f2, df / SN_WIDTH, 0); return 1; case HID_DG_HEIGHT: hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_TOUCH_MINOR); + input_set_abs_params(hi->input, ABS_MT_TOUCH_MINOR, + f1, f2, df / SN_WIDTH, 0); input_set_abs_params(hi->input, ABS_MT_ORIENTATION, - 1, 1, 0, 0); + 0, 1, 0, 0); return 1; case HID_DG_CONTACTID: - field->logical_maximum = 59; + field->logical_maximum = MAX_TRKID; hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_TRACKING_ID); + input_set_abs_params(hi->input, ABS_MT_TRACKING_ID, + 0, MAX_TRKID, 0, 0); + if (!hi->input->mt) + input_mt_create_slots(hi->input, MAX_SLOTS); + input_set_events_per_packet(hi->input, MAX_EVENTS); return 1; } /* let hid-input decide for the others */ @@ -113,10 +142,10 @@ static int mmm_input_mapped(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) { + /* tell hid-input to skip setup of these event types */ if (usage->type == EV_KEY || usage->type == EV_ABS) - clear_bit(usage->code, *bit); - - return 0; + set_bit(usage->type, hi->input->evbit); + return -1; } /* @@ -126,70 +155,49 @@ static int mmm_input_mapped(struct hid_device *hdev, struct hid_input *hi, static void mmm_filter_event(struct mmm_data *md, struct input_dev *input) { struct mmm_finger *oldest = 0; - bool pressed = false, released = false; int i; - - /* - * we need to iterate on all fingers to decide if we have a press - * or a release event in our touchscreen emulation. - */ - for (i = 0; i < 10; ++i) { + for (i = 0; i < MAX_SLOTS; ++i) { struct mmm_finger *f = &md->f[i]; if (!f->valid) { /* this finger is just placeholder data, ignore */ - } else if (f->touch) { + continue; + } + input_mt_slot(input, i); + if (f->touch) { /* this finger is on the screen */ int wide = (f->w > f->h); - input_event(input, EV_ABS, ABS_MT_TRACKING_ID, i); + /* divided by two to match visual scale of touch */ + int major = max(f->w, f->h) >> 1; + int minor = min(f->w, f->h) >> 1; + + if (!f->prev_touch) + f->id = md->id++; + input_event(input, EV_ABS, ABS_MT_TRACKING_ID, f->id); input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x); input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y); input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide); - input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, - wide ? f->w : f->h); - input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, - wide ? f->h : f->w); - input_mt_sync(input); - /* - * touchscreen emulation: maintain the age rank - * of this finger, decide if we have a press - */ - if (f->rank == 0) { - f->rank = ++(md->num); - if (f->rank == 1) - pressed = true; - } - if (f->rank == 1) + input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); + input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); + /* touchscreen emulation: pick the oldest contact */ + if (!oldest || ((f->id - oldest->id) & (SHRT_MAX + 1))) oldest = f; } else { /* this finger took off the screen */ - /* touchscreen emulation: maintain age rank of others */ - int j; - - for (j = 0; j < 10; ++j) { - struct mmm_finger *g = &md->f[j]; - if (g->rank > f->rank) { - g->rank--; - if (g->rank == 1) - oldest = g; - } - } - f->rank = 0; - --(md->num); - if (md->num == 0) - released = true; + input_event(input, EV_ABS, ABS_MT_TRACKING_ID, -1); } + f->prev_touch = f->touch; f->valid = 0; } /* touchscreen emulation */ if (oldest) { - if (pressed) - input_event(input, EV_KEY, BTN_TOUCH, 1); + input_event(input, EV_KEY, BTN_TOUCH, 1); input_event(input, EV_ABS, ABS_X, oldest->x); input_event(input, EV_ABS, ABS_Y, oldest->y); - } else if (released) { + } else { input_event(input, EV_KEY, BTN_TOUCH, 0); } + input_sync(input); } /* @@ -223,10 +231,12 @@ static int mmm_event(struct hid_device *hid, struct hid_field *field, md->f[md->curid].h = value; break; case HID_DG_CONTACTID: + value = clamp_val(value, 0, MAX_SLOTS - 1); if (md->valid) { md->curid = value; md->f[value].touch = md->touch; md->f[value].valid = 1; + md->nreal++; } break; case HID_GD_X: @@ -238,7 +248,12 @@ static int mmm_event(struct hid_device *hid, struct hid_field *field, md->f[md->curid].y = value; break; case HID_DG_CONTACTCOUNT: - mmm_filter_event(md, input); + if (value) + md->nexp = value; + if (md->nreal >= md->nexp) { + mmm_filter_event(md, input); + md->nreal = 0; + } break; } } @@ -255,6 +270,8 @@ static int mmm_probe(struct hid_device *hdev, const struct hid_device_id *id) int ret; struct mmm_data *md; + hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC; + md = kzalloc(sizeof(struct mmm_data), GFP_KERNEL); if (!md) { dev_err(&hdev->dev, "cannot allocate 3M data\n"); diff --git a/drivers/hid/hid-a4tech.c b/drivers/hid/hid-a4tech.c index 3a2b223..1666c16 100644 --- a/drivers/hid/hid-a4tech.c +++ b/drivers/hid/hid-a4tech.c @@ -133,6 +133,8 @@ static const struct hid_device_id a4_devices[] = { .driver_data = A4_2WHEEL_MOUSE_HACK_7 }, { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D), .driver_data = A4_2WHEEL_MOUSE_HACK_B8 }, + { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_RP_649), + .driver_data = A4_2WHEEL_MOUSE_HACK_B8 }, { } }; MODULE_DEVICE_TABLE(hid, a4_devices); diff --git a/drivers/hid/hid-cando.c b/drivers/hid/hid-cando.c index 4267a6f..5925bdc 100644 --- a/drivers/hid/hid-cando.c +++ b/drivers/hid/hid-cando.c @@ -237,6 +237,8 @@ static const struct hid_device_id cando_devices[] = { USB_DEVICE_ID_CANDO_MULTI_TOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_MULTI_TOUCH_11_6) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CANDO, + USB_DEVICE_ID_CANDO_MULTI_TOUCH_15_6) }, { } }; MODULE_DEVICE_TABLE(hid, cando_devices); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 7f26ffe..9eb5c02 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1241,6 +1241,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M2256) }, { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) }, { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) }, + { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_RP_649) }, #if defined(CONFIG_HID_ACRUX_FF) || defined(CONFIG_HID_ACRUX_FF_MODULE) { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) }, #endif @@ -1248,6 +1249,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI) }, @@ -1285,10 +1287,14 @@ static const struct hid_device_id hid_blacklist[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_T91MT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUSTEK_MULTITOUCH_YFO) }, { HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM) }, { HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_MULTI_TOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_MULTI_TOUCH_11_6) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_MULTI_TOUCH_15_6) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION_SOLAR) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) }, @@ -1299,6 +1305,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) }, { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, { HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) }, { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) }, @@ -1322,6 +1329,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) }, @@ -1331,6 +1339,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR) }, @@ -1366,12 +1375,15 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) }, { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM, USB_DEVICE_ID_MTP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM, USB_DEVICE_ID_MTP_STM) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_SITRONIX, USB_DEVICE_ID_MTP_SITRONIX) }, { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) }, @@ -1581,7 +1593,6 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24) }, { HID_USB_DEVICE(USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1) }, { HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232) }, - { HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_T91MT)}, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM)}, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM2)}, { HID_USB_DEVICE(USB_VENDOR_ID_AVERMEDIA, USB_DEVICE_ID_AVER_FM_MR800) }, @@ -1662,6 +1673,7 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006) }, { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1007) }, { HID_USB_DEVICE(USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA) }, + { HID_USB_DEVICE(USB_VENDOR_ID_JESS, USB_DEVICE_ID_JESS_YUREX) }, { HID_USB_DEVICE(USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO) }, { HID_USB_DEVICE(USB_VENDOR_ID_KWORLD, USB_DEVICE_ID_KWORLD_RADIO_FM700) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560) }, diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 850d02a..75c5e23 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -570,6 +570,8 @@ void hid_debug_event(struct hid_device *hdev, char *buf) buf[i]; list->tail = (list->tail + i) % HID_DEBUG_BUFSIZE; } + + wake_up_interruptible(&hdev->debug_wait); } EXPORT_SYMBOL_GPL(hid_debug_event); @@ -1051,6 +1053,7 @@ static const struct file_operations hid_debug_events_fops = { .read = hid_debug_events_read, .poll = hid_debug_events_poll, .release = hid_debug_events_release, + .llseek = noop_llseek, }; diff --git a/drivers/hid/hid-egalax.c b/drivers/hid/hid-egalax.c index f44bdc0..54b017a 100644 --- a/drivers/hid/hid-egalax.c +++ b/drivers/hid/hid-egalax.c @@ -31,7 +31,7 @@ struct egalax_data { bool first; /* is this the first finger in the frame? */ bool valid; /* valid finger data, or just placeholder? */ bool activity; /* at least one active finger previously? */ - __u16 lastx, lasty; /* latest valid (x, y) in the frame */ + __u16 lastx, lasty, lastz; /* latest valid (x, y, z) in the frame */ }; static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi, @@ -79,6 +79,10 @@ static int egalax_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_DG_TIPPRESSURE: hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_PRESSURE); + /* touchscreen emulation */ + input_set_abs_params(hi->input, ABS_PRESSURE, + field->logical_minimum, + field->logical_maximum, 0, 0); return 1; } return 0; @@ -109,8 +113,8 @@ static void egalax_filter_event(struct egalax_data *td, struct input_dev *input) if (td->valid) { /* emit multitouch events */ input_event(input, EV_ABS, ABS_MT_TRACKING_ID, td->id); - input_event(input, EV_ABS, ABS_MT_POSITION_X, td->x); - input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y); + input_event(input, EV_ABS, ABS_MT_POSITION_X, td->x >> 3); + input_event(input, EV_ABS, ABS_MT_POSITION_Y, td->y >> 3); input_event(input, EV_ABS, ABS_MT_PRESSURE, td->z); input_mt_sync(input); @@ -121,6 +125,7 @@ static void egalax_filter_event(struct egalax_data *td, struct input_dev *input) */ td->lastx = td->x; td->lasty = td->y; + td->lastz = td->z; } /* @@ -129,8 +134,9 @@ static void egalax_filter_event(struct egalax_data *td, struct input_dev *input) * the oldest on the panel, the one we want for single touch */ if (!td->first && td->activity) { - input_event(input, EV_ABS, ABS_X, td->lastx); - input_event(input, EV_ABS, ABS_Y, td->lasty); + input_event(input, EV_ABS, ABS_X, td->lastx >> 3); + input_event(input, EV_ABS, ABS_Y, td->lasty >> 3); + input_event(input, EV_ABS, ABS_PRESSURE, td->lastz); } if (!td->valid) { @@ -159,6 +165,13 @@ static int egalax_event(struct hid_device *hid, struct hid_field *field, { struct egalax_data *td = hid_get_drvdata(hid); + /* Note, eGalax has two product lines: the first is resistive and + * uses a standard parallel multitouch protocol (product ID == + * 48xx). The second is capacitive and uses an unusual "serial" + * protocol with a different message for each multitouch finger + * (product ID == 72xx). We do not yet generate a correct event + * sequence for the capacitive/serial protocol. + */ if (hid->claimed & HID_CLAIMED_INPUT) { struct input_dev *input = field->hidinput->input; @@ -246,6 +259,8 @@ static void egalax_remove(struct hid_device *hdev) static const struct hid_device_id egalax_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) }, { } }; MODULE_DEVICE_TABLE(hid, egalax_devices); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index b887e73..afbd820 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -25,6 +25,7 @@ #define USB_VENDOR_ID_A4TECH 0x09da #define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006 #define USB_DEVICE_ID_A4TECH_X5_005D 0x000a +#define USB_DEVICE_ID_A4TECH_RP_649 0x001a #define USB_VENDOR_ID_AASHIMA 0x06d6 #define USB_DEVICE_ID_AASHIMA_GAMEPAD 0x0025 @@ -63,6 +64,7 @@ #define USB_VENDOR_ID_APPLE 0x05ac #define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304 #define USB_DEVICE_ID_APPLE_MAGICMOUSE 0x030d +#define USB_DEVICE_ID_APPLE_MAGICTRACKPAD 0x030e #define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI 0x020e #define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f #define USB_DEVICE_ID_APPLE_GEYSER_ANSI 0x0214 @@ -105,6 +107,7 @@ #define USB_VENDOR_ID_ASUS 0x0486 #define USB_DEVICE_ID_ASUS_T91MT 0x0185 +#define USB_DEVICE_ID_ASUSTEK_MULTITOUCH_YFO 0x0186 #define USB_VENDOR_ID_ASUSTEK 0x0b05 #define USB_DEVICE_ID_ASUSTEK_LCM 0x1726 @@ -128,10 +131,12 @@ #define USB_VENDOR_ID_BTC 0x046e #define USB_DEVICE_ID_BTC_EMPREX_REMOTE 0x5578 +#define USB_DEVICE_ID_BTC_EMPREX_REMOTE_2 0x5577 #define USB_VENDOR_ID_CANDO 0x2087 #define USB_DEVICE_ID_CANDO_MULTI_TOUCH 0x0a01 #define USB_DEVICE_ID_CANDO_MULTI_TOUCH_11_6 0x0b03 +#define USB_DEVICE_ID_CANDO_MULTI_TOUCH_15_6 0x0f01 #define USB_VENDOR_ID_CH 0x068e #define USB_DEVICE_ID_CH_PRO_PEDALS 0x00f2 @@ -139,6 +144,7 @@ #define USB_DEVICE_ID_CH_FLIGHT_SIM_ECLIPSE_YOKE 0x0051 #define USB_DEVICE_ID_CH_FLIGHT_SIM_YOKE 0x00ff #define USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK 0x00d3 +#define USB_DEVICE_ID_CH_AXIS_295 0x001c #define USB_VENDOR_ID_CHERRY 0x046a #define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023 @@ -149,6 +155,7 @@ #define USB_VENDOR_ID_CHICONY 0x04f2 #define USB_DEVICE_ID_CHICONY_TACTICAL_PAD 0x0418 +#define USB_DEVICE_ID_CHICONY_MULTI_TOUCH 0xb19d #define USB_VENDOR_ID_CIDC 0x1677 @@ -188,6 +195,7 @@ #define USB_VENDOR_ID_DWAV 0x0eef #define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER 0x0001 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH 0x480d +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1 0x720c #define USB_VENDOR_ID_ELECOM 0x056e #define USB_DEVICE_ID_ELECOM_BM084 0x0061 @@ -299,6 +307,9 @@ #define USB_VENDOR_ID_IMATION 0x0718 #define USB_DEVICE_ID_DISC_STAKKA 0xd000 +#define USB_VENDOR_ID_JESS 0x0c45 +#define USB_DEVICE_ID_JESS_YUREX 0x1010 + #define USB_VENDOR_ID_KBGEAR 0x084e #define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001 @@ -335,6 +346,7 @@ #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110 #define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f +#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD 0xc20a #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD 0xc211 #define USB_DEVICE_ID_LOGITECH_EXTREME_3D 0xc215 #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2 0xc218 @@ -346,6 +358,7 @@ #define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG 0xc293 #define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295 #define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299 +#define USB_DEVICE_ID_LOGITECH_WII_WHEEL 0xc29c #define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a #define USB_DEVICE_ID_S510_RECEIVER 0xc50c #define USB_DEVICE_ID_S510_RECEIVER_2 0xc517 @@ -458,6 +471,8 @@ #define USB_VENDOR_ID_ROCCAT 0x1e7d #define USB_DEVICE_ID_ROCCAT_KONE 0x2ced +#define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24 +#define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS 0x2cf6 #define USB_VENDOR_ID_SAITEK 0x06a3 #define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 @@ -477,6 +492,12 @@ #define USB_VENDOR_ID_STANTUM 0x1f87 #define USB_DEVICE_ID_MTP 0x0002 +#define USB_VENDOR_ID_STANTUM_STM 0x0483 +#define USB_DEVICE_ID_MTP_STM 0x3261 + +#define USB_VENDOR_ID_STANTUM_SITRONIX 0x1403 +#define USB_DEVICE_ID_MTP_SITRONIX 0x5001 + #define USB_VENDOR_ID_SUN 0x0430 #define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab @@ -499,12 +520,14 @@ #define USB_VENDOR_ID_TURBOX 0x062a #define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201 +#define USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART 0x7100 #define USB_VENDOR_ID_TWINHAN 0x6253 #define USB_DEVICE_ID_TWINHAN_IR_REMOTE 0x0100 #define USB_VENDOR_ID_UCLOGIC 0x5543 #define USB_DEVICE_ID_UCLOGIC_TABLET_PF1209 0x0042 +#define USB_DEVICE_ID_UCLOGIC_TABLET_KNA5 0x6001 #define USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U 0x0003 #define USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U 0x0004 #define USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U 0x0005 diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 0c2908f..834ef47 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -149,6 +149,83 @@ static int hidinput_setkeycode(struct input_dev *dev, } +/** + * hidinput_calc_abs_res - calculate an absolute axis resolution + * @field: the HID report field to calculate resolution for + * @code: axis code + * + * The formula is: + * (logical_maximum - logical_minimum) + * resolution = ---------------------------------------------------------- + * (physical_maximum - physical_minimum) * 10 ^ unit_exponent + * + * as seen in the HID specification v1.11 6.2.2.7 Global Items. + * + * Only exponent 1 length units are processed. Centimeters are converted to + * inches. Degrees are converted to radians. + */ +static __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code) +{ + __s32 unit_exponent = field->unit_exponent; + __s32 logical_extents = field->logical_maximum - + field->logical_minimum; + __s32 physical_extents = field->physical_maximum - + field->physical_minimum; + __s32 prev; + + /* Check if the extents are sane */ + if (logical_extents <= 0 || physical_extents <= 0) + return 0; + + /* + * Verify and convert units. + * See HID specification v1.11 6.2.2.7 Global Items for unit decoding + */ + if (code == ABS_X || code == ABS_Y || code == ABS_Z) { + if (field->unit == 0x11) { /* If centimeters */ + /* Convert to inches */ + prev = logical_extents; + logical_extents *= 254; + if (logical_extents < prev) + return 0; + unit_exponent += 2; + } else if (field->unit != 0x13) { /* If not inches */ + return 0; + } + } else if (code == ABS_RX || code == ABS_RY || code == ABS_RZ) { + if (field->unit == 0x14) { /* If degrees */ + /* Convert to radians */ + prev = logical_extents; + logical_extents *= 573; + if (logical_extents < prev) + return 0; + unit_exponent += 1; + } else if (field->unit != 0x12) { /* If not radians */ + return 0; + } + } else { + return 0; + } + + /* Apply negative unit exponent */ + for (; unit_exponent < 0; unit_exponent++) { + prev = logical_extents; + logical_extents *= 10; + if (logical_extents < prev) + return 0; + } + /* Apply positive unit exponent */ + for (; unit_exponent > 0; unit_exponent--) { + prev = physical_extents; + physical_extents *= 10; + if (physical_extents < prev) + return 0; + } + + /* Calculate resolution */ + return logical_extents / physical_extents; +} + static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field, struct hid_usage *usage) { @@ -541,6 +618,9 @@ mapped: input_set_abs_params(input, usage->code, a, b, (b - a) >> 8, (b - a) >> 4); else input_set_abs_params(input, usage->code, a, b, 0, 0); + input_abs_set_res(input, usage->code, + hidinput_calc_abs_res(field, usage->code)); + /* use a larger default input buffer for MT devices */ if (usage->code == ABS_MT_POSITION_X && input->hint_events_per_packet == 0) input_set_events_per_packet(input, 60); @@ -663,6 +743,9 @@ void hidinput_report_event(struct hid_device *hid, struct hid_report *report) { struct hid_input *hidinput; + if (hid->quirks & HID_QUIRK_NO_INPUT_SYNC) + return; + list_for_each_entry(hidinput, &hid->inputs, list) input_sync(hidinput->input); } diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index 68c0b68..6d3cd7e 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -7,6 +7,7 @@ * Copyright (c) 2006-2007 Jiri Kosina * Copyright (c) 2007 Paul Walmsley * Copyright (c) 2008 Jiri Slaby + * Copyright (c) 2010 Hendrik Iben */ /* @@ -19,6 +20,9 @@ #include <linux/device.h> #include <linux/hid.h> #include <linux/module.h> +#include <linux/random.h> +#include <linux/sched.h> +#include <linux/wait.h> #include "hid-ids.h" #include "hid-lg.h" @@ -35,6 +39,7 @@ #define LG_FF2 0x400 #define LG_RDESC_REL_ABS 0x800 #define LG_FF3 0x1000 +#define LG_FF4 0x2000 /* * Certain Logitech keyboards send in report #3 keys which are far @@ -60,6 +65,16 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, "report descriptor\n"); rdesc[33] = rdesc[50] = 0x02; } + if ((quirks & LG_FF4) && rsize >= 101 && + rdesc[41] == 0x95 && rdesc[42] == 0x0B && + rdesc[47] == 0x05 && rdesc[48] == 0x09) { + dev_info(&hdev->dev, "fixing up Logitech Speed Force Wireless " + "button descriptor\n"); + rdesc[41] = 0x05; + rdesc[42] = 0x09; + rdesc[47] = 0x95; + rdesc[48] = 0x0B; + } return rdesc; } @@ -286,12 +301,33 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_free; } + if (quirks & LG_FF4) { + unsigned char buf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); + + if (ret >= 0) { + /* insert a little delay of 10 jiffies ~ 40ms */ + wait_queue_head_t wait; + init_waitqueue_head (&wait); + wait_event_interruptible_timeout(wait, 0, 10); + + /* Select random Address */ + buf[1] = 0xB2; + get_random_bytes(&buf[2], 2); + + ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); + } + } + if (quirks & LG_FF) lgff_init(hdev); if (quirks & LG_FF2) lg2ff_init(hdev); if (quirks & LG_FF3) lg3ff_init(hdev); + if (quirks & LG_FF4) + lg4ff_init(hdev); return 0; err_free: @@ -326,6 +362,8 @@ static const struct hid_device_id lg_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL), .driver_data = LG_NOGET | LG_FF }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD), + .driver_data = LG_FF2 }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD), .driver_data = LG_FF }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2), @@ -340,6 +378,8 @@ static const struct hid_device_id lg_devices[] = { .driver_data = LG_FF }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL), .driver_data = LG_FF }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL), + .driver_data = LG_FF4 }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ), .driver_data = LG_FF }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2), diff --git a/drivers/hid/hid-lg.h b/drivers/hid/hid-lg.h index ce2ac86..b0100ba 100644 --- a/drivers/hid/hid-lg.h +++ b/drivers/hid/hid-lg.h @@ -19,4 +19,10 @@ int lg3ff_init(struct hid_device *hdev); static inline int lg3ff_init(struct hid_device *hdev) { return -1; } #endif +#ifdef CONFIG_LOGIWII_FF +int lg4ff_init(struct hid_device *hdev); +#else +static inline int lg4ff_init(struct hid_device *hdev) { return -1; } +#endif + #endif diff --git a/drivers/hid/hid-lg2ff.c b/drivers/hid/hid-lg2ff.c index d888f1e..4258253 100644 --- a/drivers/hid/hid-lg2ff.c +++ b/drivers/hid/hid-lg2ff.c @@ -1,5 +1,5 @@ /* - * Force feedback support for Logitech Rumblepad 2 + * Force feedback support for Logitech RumblePad and Rumblepad 2 * * Copyright (c) 2008 Anssi Hannula <anssi.hannula@gmail.com> */ @@ -110,7 +110,7 @@ int lg2ff_init(struct hid_device *hid) usbhid_submit_report(hid, report, USB_DIR_OUT); - dev_info(&hid->dev, "Force feedback for Logitech Rumblepad 2 by " + dev_info(&hid->dev, "Force feedback for Logitech RumblePad/Rumblepad 2 by " "Anssi Hannula <anssi.hannula@gmail.com>\n"); return 0; diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c new file mode 100644 index 0000000..7eef5a2 --- /dev/null +++ b/drivers/hid/hid-lg4ff.c @@ -0,0 +1,136 @@ +/* + * Force feedback support for Logitech Speed Force Wireless + * + * http://wiibrew.org/wiki/Logitech_USB_steering_wheel + * + * Copyright (c) 2010 Simon Wood <simon@mungewell.org> + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include <linux/input.h> +#include <linux/usb.h> +#include <linux/hid.h> + +#include "usbhid/usbhid.h" +#include "hid-lg.h" + +struct lg4ff_device { + struct hid_report *report; +}; + +static const signed short ff4_wheel_ac[] = { + FF_CONSTANT, + FF_AUTOCENTER, + -1 +}; + +static int hid_lg4ff_play(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct hid_device *hid = input_get_drvdata(dev); + struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct hid_report *report = list_entry(report_list->next, struct hid_report, list); + int x; + +#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff + + switch (effect->type) { + case FF_CONSTANT: + x = effect->u.ramp.start_level + 0x80; /* 0x80 is no force */ + CLAMP(x); + report->field[0]->value[0] = 0x11; /* Slot 1 */ + report->field[0]->value[1] = 0x10; + report->field[0]->value[2] = x; + report->field[0]->value[3] = 0x00; + report->field[0]->value[4] = 0x00; + report->field[0]->value[5] = 0x08; + report->field[0]->value[6] = 0x00; + dbg_hid("Autocenter, x=0x%02X\n", x); + + usbhid_submit_report(hid, report, USB_DIR_OUT); + break; + } + return 0; +} + +static void hid_lg4ff_set_autocenter(struct input_dev *dev, u16 magnitude) +{ + struct hid_device *hid = input_get_drvdata(dev); + struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct hid_report *report = list_entry(report_list->next, struct hid_report, list); + __s32 *value = report->field[0]->value; + + *value++ = 0xfe; + *value++ = 0x0d; + *value++ = 0x07; + *value++ = 0x07; + *value++ = (magnitude >> 8) & 0xff; + *value++ = 0x00; + *value = 0x00; + + usbhid_submit_report(hid, report, USB_DIR_OUT); +} + + +int lg4ff_init(struct hid_device *hid) +{ + struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); + struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct input_dev *dev = hidinput->input; + struct hid_report *report; + struct hid_field *field; + const signed short *ff_bits = ff4_wheel_ac; + int error; + int i; + + /* Find the report to use */ + if (list_empty(report_list)) { + err_hid("No output report found"); + return -1; + } + + /* Check that the report looks ok */ + report = list_entry(report_list->next, struct hid_report, list); + if (!report) { + err_hid("NULL output report"); + return -1; + } + + field = report->field[0]; + if (!field) { + err_hid("NULL field"); + return -1; + } + + for (i = 0; ff_bits[i] >= 0; i++) + set_bit(ff_bits[i], dev->ffbit); + + error = input_ff_create_memless(dev, NULL, hid_lg4ff_play); + + if (error) + return error; + + if (test_bit(FF_AUTOCENTER, dev->ffbit)) + dev->ff->set_autocenter = hid_lg4ff_set_autocenter; + + dev_info(&hid->dev, "Force feedback for Logitech Speed Force Wireless by " + "Simon Wood <simon@mungewell.org>\n"); + return 0; +} + diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 319b0e5..e6dc151 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -2,6 +2,7 @@ * Apple "Magic" Wireless Mouse driver * * Copyright (c) 2010 Michael Poole <mdpoole@troilus.org> + * Copyright (c) 2010 Chase Douglas <chase.douglas@canonical.com> */ /* @@ -53,7 +54,9 @@ static bool report_undeciphered; module_param(report_undeciphered, bool, 0644); MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event"); -#define TOUCH_REPORT_ID 0x29 +#define TRACKPAD_REPORT_ID 0x28 +#define MOUSE_REPORT_ID 0x29 +#define DOUBLE_REPORT_ID 0xf7 /* These definitions are not precise, but they're close enough. (Bits * 0x03 seem to indicate the aspect ratio of the touch, bits 0x70 seem * to be some kind of bit mask -- 0x20 may be a near-field reading, @@ -67,15 +70,19 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie #define SCROLL_ACCEL_DEFAULT 7 +/* Single touch emulation should only begin when no touches are currently down. + * This is true when single_touch_id is equal to NO_TOUCHES. If multiple touches + * are down and the touch providing for single touch emulation is lifted, + * single_touch_id is equal to SINGLE_TOUCH_UP. While single touch emulation is + * occuring, single_touch_id corresponds with the tracking id of the touch used. + */ +#define NO_TOUCHES -1 +#define SINGLE_TOUCH_UP -2 + /** * struct magicmouse_sc - Tracks Magic Mouse-specific data. * @input: Input device through which we report events. * @quirks: Currently unused. - * @last_timestamp: Timestamp from most recent (18-bit) touch report - * (units of milliseconds over short windows, but seems to - * increase faster when there are no touches). - * @delta_time: 18-bit difference between the two most recent touch - * reports from the mouse. * @ntouches: Number of touches in most recent touch report. * @scroll_accel: Number of consecutive scroll motions. * @scroll_jiffies: Time of last scroll motion. @@ -86,8 +93,6 @@ struct magicmouse_sc { struct input_dev *input; unsigned long quirks; - int last_timestamp; - int delta_time; int ntouches; int scroll_accel; unsigned long scroll_jiffies; @@ -98,9 +103,9 @@ struct magicmouse_sc { short scroll_x; short scroll_y; u8 size; - u8 down; } touches[16]; int tracking_ids[16]; + int single_touch_id; }; static int magicmouse_firm_touch(struct magicmouse_sc *msc) @@ -166,18 +171,35 @@ static void magicmouse_emit_buttons(struct magicmouse_sc *msc, int state) static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tdata) { struct input_dev *input = msc->input; - __s32 x_y = tdata[0] << 8 | tdata[1] << 16 | tdata[2] << 24; - int misc = tdata[5] | tdata[6] << 8; - int id = (misc >> 6) & 15; - int x = x_y << 12 >> 20; - int y = -(x_y >> 20); - int down = (tdata[7] & TOUCH_STATE_MASK) != TOUCH_STATE_NONE; + int id, x, y, size, orientation, touch_major, touch_minor, state, down; + + if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { + id = (tdata[6] << 2 | tdata[5] >> 6) & 0xf; + x = (tdata[1] << 28 | tdata[0] << 20) >> 20; + y = -((tdata[2] << 24 | tdata[1] << 16) >> 20); + size = tdata[5] & 0x3f; + orientation = (tdata[6] >> 2) - 32; + touch_major = tdata[3]; + touch_minor = tdata[4]; + state = tdata[7] & TOUCH_STATE_MASK; + down = state != TOUCH_STATE_NONE; + } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ + id = (tdata[7] << 2 | tdata[6] >> 6) & 0xf; + x = (tdata[1] << 27 | tdata[0] << 19) >> 19; + y = -((tdata[3] << 30 | tdata[2] << 22 | tdata[1] << 14) >> 19); + size = tdata[6] & 0x3f; + orientation = (tdata[7] >> 2) - 32; + touch_major = tdata[4]; + touch_minor = tdata[5]; + state = tdata[8] & TOUCH_STATE_MASK; + down = state != TOUCH_STATE_NONE; + } /* Store tracking ID and other fields. */ msc->tracking_ids[raw_id] = id; msc->touches[id].x = x; msc->touches[id].y = y; - msc->touches[id].size = misc & 63; + msc->touches[id].size = size; /* If requested, emulate a scroll wheel by detecting small * vertical touch motions. @@ -188,7 +210,7 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda int step_y = msc->touches[id].scroll_y - y; /* Calculate and apply the scroll motion. */ - switch (tdata[7] & TOUCH_STATE_MASK) { + switch (state) { case TOUCH_STATE_START: msc->touches[id].scroll_x = x; msc->touches[id].scroll_y = y; @@ -222,21 +244,28 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda } } + if (down) { + msc->ntouches++; + if (msc->single_touch_id == NO_TOUCHES) + msc->single_touch_id = id; + } else if (msc->single_touch_id == id) + msc->single_touch_id = SINGLE_TOUCH_UP; + /* Generate the input events for this touch. */ if (report_touches && down) { - int orientation = (misc >> 10) - 32; - - msc->touches[id].down = 1; - input_report_abs(input, ABS_MT_TRACKING_ID, id); - input_report_abs(input, ABS_MT_TOUCH_MAJOR, tdata[3]); - input_report_abs(input, ABS_MT_TOUCH_MINOR, tdata[4]); + input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major << 2); + input_report_abs(input, ABS_MT_TOUCH_MINOR, touch_minor << 2); input_report_abs(input, ABS_MT_ORIENTATION, orientation); input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); - if (report_undeciphered) - input_event(input, EV_MSC, MSC_RAW, tdata[7]); + if (report_undeciphered) { + if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) + input_event(input, EV_MSC, MSC_RAW, tdata[7]); + else /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ + input_event(input, EV_MSC, MSC_RAW, tdata[8]); + } input_mt_sync(input); } @@ -247,39 +276,43 @@ static int magicmouse_raw_event(struct hid_device *hdev, { struct magicmouse_sc *msc = hid_get_drvdata(hdev); struct input_dev *input = msc->input; - int x, y, ts, ii, clicks, last_up; + int x = 0, y = 0, ii, clicks = 0, npoints; switch (data[0]) { - case 0x10: - if (size != 6) + case TRACKPAD_REPORT_ID: + /* Expect four bytes of prefix, and N*9 bytes of touch data. */ + if (size < 4 || ((size - 4) % 9) != 0) return 0; - x = (__s16)(data[2] | data[3] << 8); - y = (__s16)(data[4] | data[5] << 8); + npoints = (size - 4) / 9; + msc->ntouches = 0; + for (ii = 0; ii < npoints; ii++) + magicmouse_emit_touch(msc, ii, data + ii * 9 + 4); + + /* We don't need an MT sync here because trackpad emits a + * BTN_TOUCH event in a new frame when all touches are released. + */ + if (msc->ntouches == 0) + msc->single_touch_id = NO_TOUCHES; + clicks = data[1]; + + /* The following bits provide a device specific timestamp. They + * are unused here. + * + * ts = data[1] >> 6 | data[2] << 2 | data[3] << 10; + */ break; - case TOUCH_REPORT_ID: + case MOUSE_REPORT_ID: /* Expect six bytes of prefix, and N*8 bytes of touch data. */ if (size < 6 || ((size - 6) % 8) != 0) return 0; - ts = data[3] >> 6 | data[4] << 2 | data[5] << 10; - msc->delta_time = (ts - msc->last_timestamp) & 0x3ffff; - msc->last_timestamp = ts; - msc->ntouches = (size - 6) / 8; - for (ii = 0; ii < msc->ntouches; ii++) + npoints = (size - 6) / 8; + msc->ntouches = 0; + for (ii = 0; ii < npoints; ii++) magicmouse_emit_touch(msc, ii, data + ii * 8 + 6); - if (report_touches) { - last_up = 1; - for (ii = 0; ii < ARRAY_SIZE(msc->touches); ii++) { - if (msc->touches[ii].down) { - last_up = 0; - msc->touches[ii].down = 0; - } - } - if (last_up) { - input_mt_sync(input); - } - } + if (report_touches && msc->ntouches == 0) + input_mt_sync(input); /* When emulating three-button mode, it is important * to have the current touch information before @@ -288,68 +321,72 @@ static int magicmouse_raw_event(struct hid_device *hdev, x = (int)(((data[3] & 0x0c) << 28) | (data[1] << 22)) >> 22; y = (int)(((data[3] & 0x30) << 26) | (data[2] << 22)) >> 22; clicks = data[3]; + + /* The following bits provide a device specific timestamp. They + * are unused here. + * + * ts = data[3] >> 6 | data[4] << 2 | data[5] << 10; + */ + break; + case DOUBLE_REPORT_ID: + /* Sometimes the trackpad sends two touch reports in one + * packet. + */ + magicmouse_raw_event(hdev, report, data + 2, data[1]); + magicmouse_raw_event(hdev, report, data + 2 + data[1], + size - 2 - data[1]); break; - case 0x20: /* Theoretically battery status (0-100), but I have - * never seen it -- maybe it is only upon request. - */ - case 0x60: /* Unknown, maybe laser on/off. */ - case 0x61: /* Laser reflection status change. - * data[1]: 0 = spotted, 1 = lost - */ default: return 0; } - magicmouse_emit_buttons(msc, clicks & 3); - input_report_rel(input, REL_X, x); - input_report_rel(input, REL_Y, y); + if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { + magicmouse_emit_buttons(msc, clicks & 3); + input_report_rel(input, REL_X, x); + input_report_rel(input, REL_Y, y); + } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ + input_report_key(input, BTN_MOUSE, clicks & 1); + input_report_key(input, BTN_TOUCH, msc->ntouches > 0); + input_report_key(input, BTN_TOOL_FINGER, msc->ntouches == 1); + input_report_key(input, BTN_TOOL_DOUBLETAP, msc->ntouches == 2); + input_report_key(input, BTN_TOOL_TRIPLETAP, msc->ntouches == 3); + input_report_key(input, BTN_TOOL_QUADTAP, msc->ntouches == 4); + if (msc->single_touch_id >= 0) { + input_report_abs(input, ABS_X, + msc->touches[msc->single_touch_id].x); + input_report_abs(input, ABS_Y, + msc->touches[msc->single_touch_id].y); + } + } + input_sync(input); return 1; } -static int magicmouse_input_open(struct input_dev *dev) -{ - struct hid_device *hid = input_get_drvdata(dev); - - return hid->ll_driver->open(hid); -} - -static void magicmouse_input_close(struct input_dev *dev) -{ - struct hid_device *hid = input_get_drvdata(dev); - - hid->ll_driver->close(hid); -} - static void magicmouse_setup_input(struct input_dev *input, struct hid_device *hdev) { - input_set_drvdata(input, hdev); - input->event = hdev->ll_driver->hidinput_input_event; - input->open = magicmouse_input_open; - input->close = magicmouse_input_close; - - input->name = hdev->name; - input->phys = hdev->phys; - input->uniq = hdev->uniq; - input->id.bustype = hdev->bus; - input->id.vendor = hdev->vendor; - input->id.product = hdev->product; - input->id.version = hdev->version; - input->dev.parent = hdev->dev.parent; - __set_bit(EV_KEY, input->evbit); - __set_bit(BTN_LEFT, input->keybit); - __set_bit(BTN_RIGHT, input->keybit); - if (emulate_3button) - __set_bit(BTN_MIDDLE, input->keybit); - __set_bit(BTN_TOOL_FINGER, input->keybit); - - __set_bit(EV_REL, input->evbit); - __set_bit(REL_X, input->relbit); - __set_bit(REL_Y, input->relbit); - if (emulate_scroll_wheel) { - __set_bit(REL_WHEEL, input->relbit); - __set_bit(REL_HWHEEL, input->relbit); + + if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { + __set_bit(BTN_LEFT, input->keybit); + __set_bit(BTN_RIGHT, input->keybit); + if (emulate_3button) + __set_bit(BTN_MIDDLE, input->keybit); + + __set_bit(EV_REL, input->evbit); + __set_bit(REL_X, input->relbit); + __set_bit(REL_Y, input->relbit); + if (emulate_scroll_wheel) { + __set_bit(REL_WHEEL, input->relbit); + __set_bit(REL_HWHEEL, input->relbit); + } + } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ + __set_bit(BTN_MOUSE, input->keybit); + __set_bit(BTN_TOOL_FINGER, input->keybit); + __set_bit(BTN_TOOL_DOUBLETAP, input->keybit); + __set_bit(BTN_TOOL_TRIPLETAP, input->keybit); + __set_bit(BTN_TOOL_QUADTAP, input->keybit); + __set_bit(BTN_TOUCH, input->keybit); } if (report_touches) { @@ -359,16 +396,26 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 4, 0); input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255, 4, 0); input_set_abs_params(input, ABS_MT_ORIENTATION, -32, 31, 1, 0); - input_set_abs_params(input, ABS_MT_POSITION_X, -1100, 1358, - 4, 0); + /* Note: Touch Y position from the device is inverted relative * to how pointer motion is reported (and relative to how USB * HID recommends the coordinates work). This driver keeps * the origin at the same position, and just uses the additive * inverse of the reported Y. */ - input_set_abs_params(input, ABS_MT_POSITION_Y, -1589, 2047, - 4, 0); + if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { + input_set_abs_params(input, ABS_MT_POSITION_X, -1100, + 1358, 4, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, -1589, + 2047, 4, 0); + } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ + input_set_abs_params(input, ABS_X, -2909, 3167, 4, 0); + input_set_abs_params(input, ABS_Y, -2456, 2565, 4, 0); + input_set_abs_params(input, ABS_MT_POSITION_X, -2909, + 3167, 4, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, -2456, + 2565, 4, 0); + } } if (report_undeciphered) { @@ -377,12 +424,22 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h } } +static int magicmouse_input_mapping(struct hid_device *hdev, + struct hid_input *hi, struct hid_field *field, + struct hid_usage *usage, unsigned long **bit, int *max) +{ + struct magicmouse_sc *msc = hid_get_drvdata(hdev); + + if (!msc->input) + msc->input = hi->input; + + return 0; +} + static int magicmouse_probe(struct hid_device *hdev, const struct hid_device_id *id) { - __u8 feature_1[] = { 0xd7, 0x01 }; - __u8 feature_2[] = { 0xf8, 0x01, 0x32 }; - struct input_dev *input; + __u8 feature[] = { 0xd7, 0x01 }; struct magicmouse_sc *msc; struct hid_report *report; int ret; @@ -398,6 +455,8 @@ static int magicmouse_probe(struct hid_device *hdev, msc->quirks = id->driver_data; hid_set_drvdata(hdev, msc); + msc->single_touch_id = NO_TOUCHES; + ret = hid_parse(hdev); if (ret) { dev_err(&hdev->dev, "magicmouse hid parse failed\n"); @@ -410,10 +469,22 @@ static int magicmouse_probe(struct hid_device *hdev, goto err_free; } - /* we are handling the input ourselves */ - hidinput_disconnect(hdev); + /* We do this after hid-input is done parsing reports so that + * hid-input uses the most natural button and axis IDs. + */ + if (msc->input) + magicmouse_setup_input(msc->input, hdev); + + if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE) + report = hid_register_report(hdev, HID_INPUT_REPORT, + MOUSE_REPORT_ID); + else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ + report = hid_register_report(hdev, HID_INPUT_REPORT, + TRACKPAD_REPORT_ID); + report = hid_register_report(hdev, HID_INPUT_REPORT, + DOUBLE_REPORT_ID); + } - report = hid_register_report(hdev, HID_INPUT_REPORT, TOUCH_REPORT_ID); if (!report) { dev_err(&hdev->dev, "unable to register touch report\n"); ret = -ENOMEM; @@ -421,39 +492,15 @@ static int magicmouse_probe(struct hid_device *hdev, } report->size = 6; - ret = hdev->hid_output_raw_report(hdev, feature_1, sizeof(feature_1), + ret = hdev->hid_output_raw_report(hdev, feature, sizeof(feature), HID_FEATURE_REPORT); - if (ret != sizeof(feature_1)) { - dev_err(&hdev->dev, "unable to request touch data (1:%d)\n", - ret); - goto err_stop_hw; - } - ret = hdev->hid_output_raw_report(hdev, feature_2, - sizeof(feature_2), HID_FEATURE_REPORT); - if (ret != sizeof(feature_2)) { - dev_err(&hdev->dev, "unable to request touch data (2:%d)\n", + if (ret != sizeof(feature)) { + dev_err(&hdev->dev, "unable to request touch data (%d)\n", ret); goto err_stop_hw; } - input = input_allocate_device(); - if (!input) { - dev_err(&hdev->dev, "can't alloc input device\n"); - ret = -ENOMEM; - goto err_stop_hw; - } - magicmouse_setup_input(input, hdev); - - ret = input_register_device(input); - if (ret) { - dev_err(&hdev->dev, "input device registration failed\n"); - goto err_input; - } - msc->input = input; - return 0; -err_input: - input_free_device(input); err_stop_hw: hid_hw_stop(hdev); err_free: @@ -466,13 +513,14 @@ static void magicmouse_remove(struct hid_device *hdev) struct magicmouse_sc *msc = hid_get_drvdata(hdev); hid_hw_stop(hdev); - input_unregister_device(msc->input); kfree(msc); } static const struct hid_device_id magic_mice[] = { - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE), - .driver_data = 0 }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_MAGICMOUSE), .driver_data = 0 }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_MAGICTRACKPAD), .driver_data = 0 }, { } }; MODULE_DEVICE_TABLE(hid, magic_mice); @@ -483,6 +531,7 @@ static struct hid_driver magicmouse_driver = { .probe = magicmouse_probe, .remove = magicmouse_remove, .raw_event = magicmouse_raw_event, + .input_mapping = magicmouse_input_mapping, }; static int __init magicmouse_init(void) diff --git a/drivers/hid/hid-mosart.c b/drivers/hid/hid-mosart.c index e91437c..ac5421d 100644 --- a/drivers/hid/hid-mosart.c +++ b/drivers/hid/hid-mosart.c @@ -239,6 +239,7 @@ static void mosart_remove(struct hid_device *hdev) static const struct hid_device_id mosart_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_T91MT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUSTEK_MULTITOUCH_YFO) }, { } }; MODULE_DEVICE_TABLE(hid, mosart_devices); diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index fb69b8c..69169ef 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -90,6 +90,55 @@ struct ntrig_data { }; +/* + * This function converts the 4 byte raw firmware code into + * a string containing 5 comma separated numbers. + */ +static int ntrig_version_string(unsigned char *raw, char *buf) +{ + __u8 a = (raw[1] & 0x0e) >> 1; + __u8 b = (raw[0] & 0x3c) >> 2; + __u8 c = ((raw[0] & 0x03) << 3) | ((raw[3] & 0xe0) >> 5); + __u8 d = ((raw[3] & 0x07) << 3) | ((raw[2] & 0xe0) >> 5); + __u8 e = raw[2] & 0x07; + + /* + * As yet unmapped bits: + * 0b11000000 0b11110001 0b00011000 0b00011000 + */ + + return sprintf(buf, "%u.%u.%u.%u.%u", a, b, c, d, e); +} + +static void ntrig_report_version(struct hid_device *hdev) +{ + int ret; + char buf[20]; + struct usb_device *usb_dev = hid_to_usb_dev(hdev); + unsigned char *data = kmalloc(8, GFP_KERNEL); + + if (!data) + goto err_free; + + ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), + USB_REQ_CLEAR_FEATURE, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | + USB_DIR_IN, + 0x30c, 1, data, 8, + USB_CTRL_SET_TIMEOUT); + + if (ret == 8) { + ret = ntrig_version_string(&data[2], buf); + + dev_info(&hdev->dev, + "Firmware version: %s (%02x%02x %02x%02x)\n", + buf, data[2], data[3], data[4], data[5]); + } + +err_free: + kfree(data); +} + static ssize_t show_phys_width(struct device *dev, struct device_attribute *attr, char *buf) @@ -377,8 +426,8 @@ static struct attribute_group ntrig_attribute_group = { */ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi, - struct hid_field *field, struct hid_usage *usage, - unsigned long **bit, int *max) + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) { struct ntrig_data *nd = hid_get_drvdata(hdev); @@ -448,13 +497,13 @@ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi, /* width/height mapped on TouchMajor/TouchMinor/Orientation */ case HID_DG_WIDTH: hid_map_usage(hi, usage, bit, max, - EV_ABS, ABS_MT_TOUCH_MAJOR); + EV_ABS, ABS_MT_TOUCH_MAJOR); return 1; case HID_DG_HEIGHT: hid_map_usage(hi, usage, bit, max, - EV_ABS, ABS_MT_TOUCH_MINOR); + EV_ABS, ABS_MT_TOUCH_MINOR); input_set_abs_params(hi->input, ABS_MT_ORIENTATION, - 0, 1, 0, 0); + 0, 1, 0, 0); return 1; } return 0; @@ -468,8 +517,8 @@ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi, } static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi, - struct hid_field *field, struct hid_usage *usage, - unsigned long **bit, int *max) + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) { /* No special mappings needed for the pen and single touch */ if (field->physical) @@ -489,7 +538,7 @@ static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi, * and call input_mt_sync after each point if necessary */ static int ntrig_event (struct hid_device *hid, struct hid_field *field, - struct hid_usage *usage, __s32 value) + struct hid_usage *usage, __s32 value) { struct input_dev *input = field->hidinput->input; struct ntrig_data *nd = hid_get_drvdata(hid); @@ -848,6 +897,8 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) if (report) usbhid_submit_report(hdev, report, USB_DIR_OUT); + ntrig_report_version(hdev); + ret = sysfs_create_group(&hdev->dev.kobj, &ntrig_attribute_group); @@ -860,7 +911,7 @@ err_free: static void ntrig_remove(struct hid_device *hdev) { sysfs_remove_group(&hdev->dev.kobj, - &ntrig_attribute_group); + &ntrig_attribute_group); hid_hw_stop(hdev); kfree(hid_get_drvdata(hdev)); } diff --git a/drivers/hid/hid-picolcd.c b/drivers/hid/hid-picolcd.c index 346f0e3..bc2e077 100644 --- a/drivers/hid/hid-picolcd.c +++ b/drivers/hid/hid-picolcd.c @@ -547,11 +547,11 @@ static void picolcd_fb_destroy(struct fb_info *info) ref_cnt--; mutex_lock(&info->lock); (*ref_cnt)--; - may_release = !ref_cnt; + may_release = !*ref_cnt; mutex_unlock(&info->lock); if (may_release) { - framebuffer_release(info); vfree((u8 *)info->fix.smem_start); + framebuffer_release(info); } } diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c new file mode 100644 index 0000000..9bf2304 --- /dev/null +++ b/drivers/hid/hid-roccat-pyra.c @@ -0,0 +1,968 @@ +/* + * Roccat Pyra driver for Linux + * + * Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net> + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +/* + * Roccat Pyra is a mobile gamer mouse which comes in wired and wireless + * variant. Wireless variant is not tested. + * Userland tools can be found at http://sourceforge.net/projects/roccat + */ + +#include <linux/device.h> +#include <linux/input.h> +#include <linux/hid.h> +#include <linux/usb.h> +#include <linux/module.h> +#include <linux/slab.h> +#include "hid-ids.h" +#include "hid-roccat.h" +#include "hid-roccat-pyra.h" + +static void profile_activated(struct pyra_device *pyra, + unsigned int new_profile) +{ + pyra->actual_profile = new_profile; + pyra->actual_cpi = pyra->profile_settings[pyra->actual_profile].y_cpi; +} + +static int pyra_send_control(struct usb_device *usb_dev, int value, + enum pyra_control_requests request) +{ + int len; + struct pyra_control control; + + if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS || + request == PYRA_CONTROL_REQUEST_PROFILE_BUTTONS) && + (value < 0 || value > 4)) + return -EINVAL; + + control.command = PYRA_COMMAND_CONTROL; + control.value = value; + control.request = request; + + len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), + USB_REQ_SET_CONFIGURATION, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, + PYRA_USB_COMMAND_CONTROL, 0, (char *)&control, + sizeof(struct pyra_control), + USB_CTRL_SET_TIMEOUT); + + if (len != sizeof(struct pyra_control)) + return len; + + return 0; +} + +static int pyra_receive_control_status(struct usb_device *usb_dev) +{ + int len; + struct pyra_control control; + + do { + msleep(10); + + len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), + USB_REQ_CLEAR_FEATURE, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | + USB_DIR_IN, + PYRA_USB_COMMAND_CONTROL, 0, (char *)&control, + sizeof(struct pyra_control), + USB_CTRL_SET_TIMEOUT); + + /* requested too early, try again */ + } while (len == -EPROTO); + + if (len == sizeof(struct pyra_control) && + control.command == PYRA_COMMAND_CONTROL && + control.request == PYRA_CONTROL_REQUEST_STATUS && + control.value == 1) + return 0; + else { + dev_err(&usb_dev->dev, "receive control status: " + "unknown response 0x%x 0x%x\n", + control.request, control.value); + return -EINVAL; + } +} + +static int pyra_get_profile_settings(struct usb_device *usb_dev, + struct pyra_profile_settings *buf, int number) +{ + int retval; + + retval = pyra_send_control(usb_dev, number, + PYRA_CONTROL_REQUEST_PROFILE_SETTINGS); + + if (retval) + return retval; + + retval = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), + USB_REQ_CLEAR_FEATURE, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, + PYRA_USB_COMMAND_PROFILE_SETTINGS, 0, (char *)buf, + sizeof(struct pyra_profile_settings), + USB_CTRL_SET_TIMEOUT); + + if (retval != sizeof(struct pyra_profile_settings)) + return retval; + + return 0; +} + +static int pyra_get_profile_buttons(struct usb_device *usb_dev, + struct pyra_profile_buttons *buf, int number) +{ + int retval; + + retval = pyra_send_control(usb_dev, number, + PYRA_CONTROL_REQUEST_PROFILE_BUTTONS); + + if (retval) + return retval; + + retval = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), + USB_REQ_CLEAR_FEATURE, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, + PYRA_USB_COMMAND_PROFILE_BUTTONS, 0, (char *)buf, + sizeof(struct pyra_profile_buttons), + USB_CTRL_SET_TIMEOUT); + + if (retval != sizeof(struct pyra_profile_buttons)) + return retval; + + return 0; +} + +static int pyra_get_settings(struct usb_device *usb_dev, + struct pyra_settings *buf) +{ + int len; + len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), + USB_REQ_CLEAR_FEATURE, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, + PYRA_USB_COMMAND_SETTINGS, 0, buf, + sizeof(struct pyra_settings), USB_CTRL_SET_TIMEOUT); + if (len != sizeof(struct pyra_settings)) + return -EIO; + return 0; +} + +static int pyra_get_info(struct usb_device *usb_dev, struct pyra_info *buf) +{ + int len; + len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), + USB_REQ_CLEAR_FEATURE, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, + PYRA_USB_COMMAND_INFO, 0, buf, + sizeof(struct pyra_info), USB_CTRL_SET_TIMEOUT); + if (len != sizeof(struct pyra_info)) + return -EIO; + return 0; +} + +static int pyra_set_profile_settings(struct usb_device *usb_dev, + struct pyra_profile_settings const *settings) +{ + int len; + len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), + USB_REQ_SET_CONFIGURATION, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, + PYRA_USB_COMMAND_PROFILE_SETTINGS, 0, (char *)settings, + sizeof(struct pyra_profile_settings), + USB_CTRL_SET_TIMEOUT); + if (len != sizeof(struct pyra_profile_settings)) + return -EIO; + if (pyra_receive_control_status(usb_dev)) + return -EIO; + return 0; +} + +static int pyra_set_profile_buttons(struct usb_device *usb_dev, + struct pyra_profile_buttons const *buttons) +{ + int len; + len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), + USB_REQ_SET_CONFIGURATION, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, + PYRA_USB_COMMAND_PROFILE_BUTTONS, 0, (char *)buttons, + sizeof(struct pyra_profile_buttons), + USB_CTRL_SET_TIMEOUT); + if (len != sizeof(struct pyra_profile_buttons)) + return -EIO; + if (pyra_receive_control_status(usb_dev)) + return -EIO; + return 0; +} + +static int pyra_set_settings(struct usb_device *usb_dev, + struct pyra_settings const *settings) +{ + int len; + len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), + USB_REQ_SET_CONFIGURATION, + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, + PYRA_USB_COMMAND_SETTINGS, 0, (char *)settings, + sizeof(struct pyra_settings), USB_CTRL_SET_TIMEOUT); + if (len != sizeof(struct pyra_settings)) + return -EIO; + if (pyra_receive_control_status(usb_dev)) + return -EIO; + return 0; +} + +static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count, int number) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); + + if (off >= sizeof(struct pyra_profile_settings)) + return 0; + + if (off + count > sizeof(struct pyra_profile_settings)) + count = sizeof(struct pyra_profile_settings) - off; + + mutex_lock(&pyra->pyra_lock); + memcpy(buf, ((char const *)&pyra->profile_settings[number]) + off, + count); + mutex_unlock(&pyra->pyra_lock); + + return count; +} + +static ssize_t pyra_sysfs_read_profile1_settings(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return pyra_sysfs_read_profilex_settings(fp, kobj, + attr, buf, off, count, 0); +} + +static ssize_t pyra_sysfs_read_profile2_settings(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return pyra_sysfs_read_profilex_settings(fp, kobj, + attr, buf, off, count, 1); +} + +static ssize_t pyra_sysfs_read_profile3_settings(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return pyra_sysfs_read_profilex_settings(fp, kobj, + attr, buf, off, count, 2); +} + +static ssize_t pyra_sysfs_read_profile4_settings(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return pyra_sysfs_read_profilex_settings(fp, kobj, + attr, buf, off, count, 3); +} + +static ssize_t pyra_sysfs_read_profile5_settings(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return pyra_sysfs_read_profilex_settings(fp, kobj, + attr, buf, off, count, 4); +} + +static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count, int number) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); + + if (off >= sizeof(struct pyra_profile_buttons)) + return 0; + + if (off + count > sizeof(struct pyra_profile_buttons)) + count = sizeof(struct pyra_profile_buttons) - off; + + mutex_lock(&pyra->pyra_lock); + memcpy(buf, ((char const *)&pyra->profile_buttons[number]) + off, + count); + mutex_unlock(&pyra->pyra_lock); + + return count; +} + +static ssize_t pyra_sysfs_read_profile1_buttons(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return pyra_sysfs_read_profilex_buttons(fp, kobj, + attr, buf, off, count, 0); +} + +static ssize_t pyra_sysfs_read_profile2_buttons(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return pyra_sysfs_read_profilex_buttons(fp, kobj, + attr, buf, off, count, 1); +} + +static ssize_t pyra_sysfs_read_profile3_buttons(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return pyra_sysfs_read_profilex_buttons(fp, kobj, + attr, buf, off, count, 2); +} + +static ssize_t pyra_sysfs_read_profile4_buttons(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return pyra_sysfs_read_profilex_buttons(fp, kobj, + attr, buf, off, count, 3); +} + +static ssize_t pyra_sysfs_read_profile5_buttons(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + return pyra_sysfs_read_profilex_buttons(fp, kobj, + attr, buf, off, count, 4); +} + +static ssize_t pyra_sysfs_write_profile_settings(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval = 0; + int difference; + int profile_number; + struct pyra_profile_settings *profile_settings; + + if (off != 0 || count != sizeof(struct pyra_profile_settings)) + return -EINVAL; + + profile_number = ((struct pyra_profile_settings const *)buf)->number; + profile_settings = &pyra->profile_settings[profile_number]; + + mutex_lock(&pyra->pyra_lock); + difference = memcmp(buf, profile_settings, + sizeof(struct pyra_profile_settings)); + if (difference) { + retval = pyra_set_profile_settings(usb_dev, + (struct pyra_profile_settings const *)buf); + if (!retval) + memcpy(profile_settings, buf, + sizeof(struct pyra_profile_settings)); + } + mutex_unlock(&pyra->pyra_lock); + + if (retval) + return retval; + + return sizeof(struct pyra_profile_settings); +} + +static ssize_t pyra_sysfs_write_profile_buttons(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval = 0; + int difference; + int profile_number; + struct pyra_profile_buttons *profile_buttons; + + if (off != 0 || count != sizeof(struct pyra_profile_buttons)) + return -EINVAL; + + profile_number = ((struct pyra_profile_buttons const *)buf)->number; + profile_buttons = &pyra->profile_buttons[profile_number]; + + mutex_lock(&pyra->pyra_lock); + difference = memcmp(buf, profile_buttons, + sizeof(struct pyra_profile_buttons)); + if (difference) { + retval = pyra_set_profile_buttons(usb_dev, + (struct pyra_profile_buttons const *)buf); + if (!retval) + memcpy(profile_buttons, buf, + sizeof(struct pyra_profile_buttons)); + } + mutex_unlock(&pyra->pyra_lock); + + if (retval) + return retval; + + return sizeof(struct pyra_profile_buttons); +} + +static ssize_t pyra_sysfs_read_settings(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); + + if (off >= sizeof(struct pyra_settings)) + return 0; + + if (off + count > sizeof(struct pyra_settings)) + count = sizeof(struct pyra_settings) - off; + + mutex_lock(&pyra->pyra_lock); + memcpy(buf, ((char const *)&pyra->settings) + off, count); + mutex_unlock(&pyra->pyra_lock); + + return count; +} + +static ssize_t pyra_sysfs_write_settings(struct file *fp, + struct kobject *kobj, struct bin_attribute *attr, char *buf, + loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); + int retval = 0; + int difference; + + if (off != 0 || count != sizeof(struct pyra_settings)) + return -EINVAL; + + mutex_lock(&pyra->pyra_lock); + difference = memcmp(buf, &pyra->settings, sizeof(struct pyra_settings)); + if (difference) { + retval = pyra_set_settings(usb_dev, + (struct pyra_settings const *)buf); + if (!retval) + memcpy(&pyra->settings, buf, + sizeof(struct pyra_settings)); + } + mutex_unlock(&pyra->pyra_lock); + + if (retval) + return retval; + + profile_activated(pyra, pyra->settings.startup_profile); + + return sizeof(struct pyra_settings); +} + + +static ssize_t pyra_sysfs_show_actual_cpi(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); + return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_cpi); +} + +static ssize_t pyra_sysfs_show_actual_profile(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); + return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_profile); +} + +static ssize_t pyra_sysfs_show_firmware_version(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); + return snprintf(buf, PAGE_SIZE, "%d\n", pyra->firmware_version); +} + +static ssize_t pyra_sysfs_show_startup_profile(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); + return snprintf(buf, PAGE_SIZE, "%d\n", pyra->settings.startup_profile); +} + +static DEVICE_ATTR(actual_cpi, 0440, pyra_sysfs_show_actual_cpi, NULL); + +static DEVICE_ATTR(actual_profile, 0440, pyra_sysfs_show_actual_profile, NULL); + +static DEVICE_ATTR(firmware_version, 0440, + pyra_sysfs_show_firmware_version, NULL); + +static DEVICE_ATTR(startup_profile, 0440, + pyra_sysfs_show_startup_profile, NULL); + +static struct attribute *pyra_attributes[] = { + &dev_attr_actual_cpi.attr, + &dev_attr_actual_profile.attr, + &dev_attr_firmware_version.attr, + &dev_attr_startup_profile.attr, + NULL +}; + +static struct attribute_group pyra_attribute_group = { + .attrs = pyra_attributes +}; + +static struct bin_attribute pyra_profile_settings_attr = { + .attr = { .name = "profile_settings", .mode = 0220 }, + .size = sizeof(struct pyra_profile_settings), + .write = pyra_sysfs_write_profile_settings +}; + +static struct bin_attribute pyra_profile1_settings_attr = { + .attr = { .name = "profile1_settings", .mode = 0440 }, + .size = sizeof(struct pyra_profile_settings), + .read = pyra_sysfs_read_profile1_settings +}; + +static struct bin_attribute pyra_profile2_settings_attr = { + .attr = { .name = "profile2_settings", .mode = 0440 }, + .size = sizeof(struct pyra_profile_settings), + .read = pyra_sysfs_read_profile2_settings +}; + +static struct bin_attribute pyra_profile3_settings_attr = { + .attr = { .name = "profile3_settings", .mode = 0440 }, + .size = sizeof(struct pyra_profile_settings), + .read = pyra_sysfs_read_profile3_settings +}; + +static struct bin_attribute pyra_profile4_settings_attr = { + .attr = { .name = "profile4_settings", .mode = 0440 }, + .size = sizeof(struct pyra_profile_settings), + .read = pyra_sysfs_read_profile4_settings +}; + +static struct bin_attribute pyra_profile5_settings_attr = { + .attr = { .name = "profile5_settings", .mode = 0440 }, + .size = sizeof(struct pyra_profile_settings), + .read = pyra_sysfs_read_profile5_settings +}; + +static struct bin_attribute pyra_profile_buttons_attr = { + .attr = { .name = "profile_buttons", .mode = 0220 }, + .size = sizeof(struct pyra_profile_buttons), + .write = pyra_sysfs_write_profile_buttons +}; + +static struct bin_attribute pyra_profile1_buttons_attr = { + .attr = { .name = "profile1_buttons", .mode = 0440 }, + .size = sizeof(struct pyra_profile_buttons), + .read = pyra_sysfs_read_profile1_buttons +}; + +static struct bin_attribute pyra_profile2_buttons_attr = { + .attr = { .name = "profile2_buttons", .mode = 0440 }, + .size = sizeof(struct pyra_profile_buttons), + .read = pyra_sysfs_read_profile2_buttons +}; + +static struct bin_attribute pyra_profile3_buttons_attr = { + .attr = { .name = "profile3_buttons", .mode = 0440 }, + .size = sizeof(struct pyra_profile_buttons), + .read = pyra_sysfs_read_profile3_buttons +}; + +static struct bin_attribute pyra_profile4_buttons_attr = { + .attr = { .name = "profile4_buttons", .mode = 0440 }, + .size = sizeof(struct pyra_profile_buttons), + .read = pyra_sysfs_read_profile4_buttons +}; + +static struct bin_attribute pyra_profile5_buttons_attr = { + .attr = { .name = "profile5_buttons", .mode = 0440 }, + .size = sizeof(struct pyra_profile_buttons), + .read = pyra_sysfs_read_profile5_buttons +}; + +static struct bin_attribute pyra_settings_attr = { + .attr = { .name = "settings", .mode = 0660 }, + .size = sizeof(struct pyra_settings), + .read = pyra_sysfs_read_settings, + .write = pyra_sysfs_write_settings +}; + +static int pyra_create_sysfs_attributes(struct usb_interface *intf) +{ + int retval; + + retval = sysfs_create_group(&intf->dev.kobj, &pyra_attribute_group); + if (retval) + goto exit_1; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_profile_settings_attr); + if (retval) + goto exit_2; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_profile1_settings_attr); + if (retval) + goto exit_3; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_profile2_settings_attr); + if (retval) + goto exit_4; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_profile3_settings_attr); + if (retval) + goto exit_5; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_profile4_settings_attr); + if (retval) + goto exit_6; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_profile5_settings_attr); + if (retval) + goto exit_7; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_profile_buttons_attr); + if (retval) + goto exit_8; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_profile1_buttons_attr); + if (retval) + goto exit_9; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_profile2_buttons_attr); + if (retval) + goto exit_10; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_profile3_buttons_attr); + if (retval) + goto exit_11; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_profile4_buttons_attr); + if (retval) + goto exit_12; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_profile5_buttons_attr); + if (retval) + goto exit_13; + + retval = sysfs_create_bin_file(&intf->dev.kobj, + &pyra_settings_attr); + if (retval) + goto exit_14; + + return 0; + +exit_14: + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile5_buttons_attr); +exit_13: + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile4_buttons_attr); +exit_12: + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile3_buttons_attr); +exit_11: + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile2_buttons_attr); +exit_10: + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile1_buttons_attr); +exit_9: + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile_buttons_attr); +exit_8: + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile5_settings_attr); +exit_7: + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile4_settings_attr); +exit_6: + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile3_settings_attr); +exit_5: + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile2_settings_attr); +exit_4: + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile1_settings_attr); +exit_3: + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile_settings_attr); +exit_2: + sysfs_remove_group(&intf->dev.kobj, &pyra_attribute_group); +exit_1: + return retval; +} + +static void pyra_remove_sysfs_attributes(struct usb_interface *intf) +{ + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_settings_attr); + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile5_buttons_attr); + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile4_buttons_attr); + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile3_buttons_attr); + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile2_buttons_attr); + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile1_buttons_attr); + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile_buttons_attr); + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile5_settings_attr); + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile4_settings_attr); + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile3_settings_attr); + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile2_settings_attr); + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile1_settings_attr); + sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile_settings_attr); + sysfs_remove_group(&intf->dev.kobj, &pyra_attribute_group); +} + +static int pyra_init_pyra_device_struct(struct usb_device *usb_dev, + struct pyra_device *pyra) +{ + struct pyra_info *info; + int retval, i; + + mutex_init(&pyra->pyra_lock); + + info = kmalloc(sizeof(struct pyra_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + retval = pyra_get_info(usb_dev, info); + if (retval) { + kfree(info); + return retval; + } + pyra->firmware_version = info->firmware_version; + kfree(info); + + retval = pyra_get_settings(usb_dev, &pyra->settings); + if (retval) + return retval; + + for (i = 0; i < 5; ++i) { + retval = pyra_get_profile_settings(usb_dev, + &pyra->profile_settings[i], i); + if (retval) + return retval; + + retval = pyra_get_profile_buttons(usb_dev, + &pyra->profile_buttons[i], i); + if (retval) + return retval; + } + + profile_activated(pyra, pyra->settings.startup_profile); + + return 0; +} + +static int pyra_init_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct usb_device *usb_dev = interface_to_usbdev(intf); + struct pyra_device *pyra; + int retval; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + == USB_INTERFACE_PROTOCOL_MOUSE) { + + pyra = kzalloc(sizeof(*pyra), GFP_KERNEL); + if (!pyra) { + dev_err(&hdev->dev, "can't alloc device descriptor\n"); + return -ENOMEM; + } + hid_set_drvdata(hdev, pyra); + + retval = pyra_init_pyra_device_struct(usb_dev, pyra); + if (retval) { + dev_err(&hdev->dev, + "couldn't init struct pyra_device\n"); + goto exit_free; + } + + retval = roccat_connect(hdev); + if (retval < 0) { + dev_err(&hdev->dev, "couldn't init char dev\n"); + } else { + pyra->chrdev_minor = retval; + pyra->roccat_claimed = 1; + } + + retval = pyra_create_sysfs_attributes(intf); + if (retval) { + dev_err(&hdev->dev, "cannot create sysfs files\n"); + goto exit_free; + } + } else { + hid_set_drvdata(hdev, NULL); + } + + return 0; +exit_free: + kfree(pyra); + return retval; +} + +static void pyra_remove_specials(struct hid_device *hdev) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct pyra_device *pyra; + + if (intf->cur_altsetting->desc.bInterfaceProtocol + == USB_INTERFACE_PROTOCOL_MOUSE) { + pyra_remove_sysfs_attributes(intf); + pyra = hid_get_drvdata(hdev); + if (pyra->roccat_claimed) + roccat_disconnect(pyra->chrdev_minor); + kfree(hid_get_drvdata(hdev)); + } +} + +static int pyra_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int retval; + + retval = hid_parse(hdev); + if (retval) { + dev_err(&hdev->dev, "parse failed\n"); + goto exit; + } + + retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (retval) { + dev_err(&hdev->dev, "hw start failed\n"); + goto exit; + } + + retval = pyra_init_specials(hdev); + if (retval) { + dev_err(&hdev->dev, "couldn't install mouse\n"); + goto exit_stop; + } + return 0; + +exit_stop: + hid_hw_stop(hdev); +exit: + return retval; +} + +static void pyra_remove(struct hid_device *hdev) +{ + pyra_remove_specials(hdev); + hid_hw_stop(hdev); +} + +static void pyra_keep_values_up_to_date(struct pyra_device *pyra, + u8 const *data) +{ + struct pyra_mouse_event_button const *button_event; + + switch (data[0]) { + case PYRA_MOUSE_REPORT_NUMBER_BUTTON: + button_event = (struct pyra_mouse_event_button const *)data; + switch (button_event->type) { + case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2: + profile_activated(pyra, button_event->data1 - 1); + break; + case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI: + pyra->actual_cpi = button_event->data1; + break; + } + break; + } +} + +static void pyra_report_to_chrdev(struct pyra_device const *pyra, + u8 const *data) +{ + struct pyra_roccat_report roccat_report; + struct pyra_mouse_event_button const *button_event; + + if (data[0] != PYRA_MOUSE_REPORT_NUMBER_BUTTON) + return; + + button_event = (struct pyra_mouse_event_button const *)data; + + switch (button_event->type) { + case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2: + case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI: + roccat_report.type = button_event->type; + roccat_report.value = button_event->data1; + roccat_report.key = 0; + roccat_report_event(pyra->chrdev_minor, + (uint8_t const *)&roccat_report, + sizeof(struct pyra_roccat_report)); + break; + case PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO: + case PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT: + case PYRA_MOUSE_EVENT_BUTTON_TYPE_QUICKLAUNCH: + if (button_event->data2 == PYRA_MOUSE_EVENT_BUTTON_PRESS) { + roccat_report.type = button_event->type; + roccat_report.key = button_event->data1; + /* + * pyra reports profile numbers with range 1-5. + * Keeping this behaviour. + */ + roccat_report.value = pyra->actual_profile + 1; + roccat_report_event(pyra->chrdev_minor, + (uint8_t const *)&roccat_report, + sizeof(struct pyra_roccat_report)); + } + break; + } +} + +static int pyra_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *data, int size) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct pyra_device *pyra = hid_get_drvdata(hdev); + + if (intf->cur_altsetting->desc.bInterfaceProtocol + != USB_INTERFACE_PROTOCOL_MOUSE) + return 0; + + pyra_keep_values_up_to_date(pyra, data); + + if (pyra->roccat_claimed) + pyra_report_to_chrdev(pyra, data); + + return 0; +} + +static const struct hid_device_id pyra_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, + USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, + /* TODO add USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS after testing */ + { } +}; + +MODULE_DEVICE_TABLE(hid, pyra_devices); + +static struct hid_driver pyra_driver = { + .name = "pyra", + .id_table = pyra_devices, + .probe = pyra_probe, + .remove = pyra_remove, + .raw_event = pyra_raw_event +}; + +static int __init pyra_init(void) +{ + return hid_register_driver(&pyra_driver); +} + +static void __exit pyra_exit(void) +{ + hid_unregister_driver(&pyra_driver); +} + +module_init(pyra_init); +module_exit(pyra_exit); + +MODULE_AUTHOR("Stefan Achatz"); +MODULE_DESCRIPTION("USB Roccat Pyra driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hid/hid-roccat-pyra.h b/drivers/hid/hid-roccat-pyra.h new file mode 100644 index 0000000..22f80a8 --- /dev/null +++ b/drivers/hid/hid-roccat-pyra.h @@ -0,0 +1,186 @@ +#ifndef __HID_ROCCAT_PYRA_H +#define __HID_ROCCAT_PYRA_H + +/* + * Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net> + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/types.h> + +#pragma pack(push) +#pragma pack(1) + +struct pyra_b { + uint8_t command; /* PYRA_COMMAND_B */ + uint8_t size; /* always 3 */ + uint8_t unknown; /* 1 */ +}; + +struct pyra_control { + uint8_t command; /* PYRA_COMMAND_CONTROL */ + /* + * value is profile number for request_settings and request_buttons + * 1 if status ok for request_status + */ + uint8_t value; /* Range 0-4 */ + uint8_t request; +}; + +enum pyra_control_requests { + PYRA_CONTROL_REQUEST_STATUS = 0x00, + PYRA_CONTROL_REQUEST_PROFILE_SETTINGS = 0x10, + PYRA_CONTROL_REQUEST_PROFILE_BUTTONS = 0x20 +}; + +struct pyra_settings { + uint8_t command; /* PYRA_COMMAND_SETTINGS */ + uint8_t size; /* always 3 */ + uint8_t startup_profile; /* Range 0-4! */ +}; + +struct pyra_profile_settings { + uint8_t command; /* PYRA_COMMAND_PROFILE_SETTINGS */ + uint8_t size; /* always 0xd */ + uint8_t number; /* Range 0-4 */ + uint8_t xysync; + uint8_t x_sensitivity; /* 0x1-0xa */ + uint8_t y_sensitivity; + uint8_t x_cpi; /* unused */ + uint8_t y_cpi; /* this value is for x and y */ + uint8_t lightswitch; /* 0 = off, 1 = on */ + uint8_t light_effect; + uint8_t handedness; + uint16_t checksum; /* byte sum */ +}; + +struct pyra_profile_buttons { + uint8_t command; /* PYRA_COMMAND_PROFILE_BUTTONS */ + uint8_t size; /* always 0x13 */ + uint8_t number; /* Range 0-4 */ + uint8_t buttons[14]; + uint16_t checksum; /* byte sum */ +}; + +struct pyra_info { + uint8_t command; /* PYRA_COMMAND_INFO */ + uint8_t size; /* always 6 */ + uint8_t firmware_version; + uint8_t unknown1; /* always 0 */ + uint8_t unknown2; /* always 1 */ + uint8_t unknown3; /* always 0 */ +}; + +enum pyra_commands { + PYRA_COMMAND_CONTROL = 0x4, + PYRA_COMMAND_SETTINGS = 0x5, + PYRA_COMMAND_PROFILE_SETTINGS = 0x6, + PYRA_COMMAND_PROFILE_BUTTONS = 0x7, + PYRA_COMMAND_INFO = 0x9, + PYRA_COMMAND_B = 0xb +}; + +enum pyra_usb_commands { + PYRA_USB_COMMAND_CONTROL = 0x304, + PYRA_USB_COMMAND_SETTINGS = 0x305, + PYRA_USB_COMMAND_PROFILE_SETTINGS = 0x306, + PYRA_USB_COMMAND_PROFILE_BUTTONS = 0x307, + PYRA_USB_COMMAND_INFO = 0x309, + PYRA_USB_COMMAND_B = 0x30b /* writes 3 bytes */ +}; + +enum pyra_mouse_report_numbers { + PYRA_MOUSE_REPORT_NUMBER_HID = 1, + PYRA_MOUSE_REPORT_NUMBER_AUDIO = 2, + PYRA_MOUSE_REPORT_NUMBER_BUTTON = 3, +}; + +struct pyra_mouse_event_button { + uint8_t report_number; /* always 3 */ + uint8_t unknown; /* always 0 */ + uint8_t type; + uint8_t data1; + uint8_t data2; +}; + +struct pyra_mouse_event_audio { + uint8_t report_number; /* always 2 */ + uint8_t type; + uint8_t unused; /* always 0 */ +}; + +/* hid audio controls */ +enum pyra_mouse_event_audio_types { + PYRA_MOUSE_EVENT_AUDIO_TYPE_MUTE = 0xe2, + PYRA_MOUSE_EVENT_AUDIO_TYPE_VOLUME_UP = 0xe9, + PYRA_MOUSE_EVENT_AUDIO_TYPE_VOLUME_DOWN = 0xea, +}; + +enum pyra_mouse_event_button_types { + /* + * Mouse sends tilt events on report_number 1 and 3 + * Tilt events are sent repeatedly with 0.94s between first and second + * event and 0.22s on subsequent + */ + PYRA_MOUSE_EVENT_BUTTON_TYPE_TILT = 0x10, + + /* + * These are sent sequentially + * data1 contains new profile number in range 1-5 + */ + PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_1 = 0x20, + PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2 = 0x30, + + /* + * data1 = button_number (rmp index) + * data2 = pressed/released + */ + PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO = 0x40, + PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT = 0x50, + + /* + * data1 = button_number (rmp index) + */ + PYRA_MOUSE_EVENT_BUTTON_TYPE_QUICKLAUNCH = 0x60, + + /* data1 = new cpi */ + PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI = 0xb0, + + /* data1 and data2 = new sensitivity */ + PYRA_MOUSE_EVENT_BUTTON_TYPE_SENSITIVITY = 0xc0, + + PYRA_MOUSE_EVENT_BUTTON_TYPE_MULTIMEDIA = 0xf0, +}; + +enum { + PYRA_MOUSE_EVENT_BUTTON_PRESS = 0, + PYRA_MOUSE_EVENT_BUTTON_RELEASE = 1, +}; + +struct pyra_roccat_report { + uint8_t type; + uint8_t value; + uint8_t key; +}; + +#pragma pack(pop) + +struct pyra_device { + int actual_profile; + int actual_cpi; + int firmware_version; + int roccat_claimed; + int chrdev_minor; + struct mutex pyra_lock; + struct pyra_settings settings; + struct pyra_profile_settings profile_settings[5]; + struct pyra_profile_buttons profile_buttons[5]; +}; + +#endif diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c index f6e80c7..5a6879e 100644 --- a/drivers/hid/hid-roccat.c +++ b/drivers/hid/hid-roccat.c @@ -384,6 +384,7 @@ static const struct file_operations roccat_ops = { .poll = roccat_poll, .open = roccat_open, .release = roccat_release, + .llseek = noop_llseek, }; static int __init roccat_init(void) diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 9fa0349..677bb3d 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -24,7 +24,9 @@ #include "hid-ids.h" -#define VAIO_RDESC_CONSTANT 0x0001 +#define VAIO_RDESC_CONSTANT (1 << 0) +#define SIXAXIS_CONTROLLER_USB (1 << 1) +#define SIXAXIS_CONTROLLER_BT (1 << 2) struct sony_sc { unsigned long quirks; @@ -45,12 +47,31 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc, return rdesc; } +static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf, + size_t count, unsigned char report_type) +{ + struct usb_interface *intf = to_usb_interface(hid->dev.parent); + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_host_interface *interface = intf->cur_altsetting; + int report_id = buf[0]; + int ret; + + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + HID_REQ_SET_REPORT, + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + ((report_type + 1) << 8) | report_id, + interface->desc.bInterfaceNumber, buf, count, + USB_CTRL_SET_TIMEOUT); + + return ret; +} + /* * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller * to "operational". Without this, the ps3 controller will not report any * events. */ -static int sony_set_operational_usb(struct hid_device *hdev) +static int sixaxis_set_operational_usb(struct hid_device *hdev) { struct usb_interface *intf = to_usb_interface(hdev->dev.parent); struct usb_device *dev = interface_to_usbdev(intf); @@ -75,7 +96,7 @@ static int sony_set_operational_usb(struct hid_device *hdev) return ret; } -static int sony_set_operational_bt(struct hid_device *hdev) +static int sixaxis_set_operational_bt(struct hid_device *hdev) { unsigned char buf[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 }; return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); @@ -109,16 +130,14 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_free; } - switch (hdev->bus) { - case BUS_USB: - ret = sony_set_operational_usb(hdev); - break; - case BUS_BLUETOOTH: - ret = sony_set_operational_bt(hdev); - break; - default: - ret = 0; + if (sc->quirks & SIXAXIS_CONTROLLER_USB) { + hdev->hid_output_raw_report = sixaxis_usb_output_raw_report; + ret = sixaxis_set_operational_usb(hdev); } + else if (sc->quirks & SIXAXIS_CONTROLLER_BT) + ret = sixaxis_set_operational_bt(hdev); + else + ret = 0; if (ret < 0) goto err_stop; @@ -138,8 +157,10 @@ static void sony_remove(struct hid_device *hdev) } static const struct hid_device_id sony_devices[] = { - { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER), + .driver_data = SIXAXIS_CONTROLLER_USB }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER), + .driver_data = SIXAXIS_CONTROLLER_BT }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE), .driver_data = VAIO_RDESC_CONSTANT }, { } diff --git a/drivers/hid/hid-stantum.c b/drivers/hid/hid-stantum.c index 90df886..3171be2 100644 --- a/drivers/hid/hid-stantum.c +++ b/drivers/hid/hid-stantum.c @@ -249,6 +249,8 @@ static void stantum_remove(struct hid_device *hdev) static const struct hid_device_id stantum_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM, USB_DEVICE_ID_MTP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM, USB_DEVICE_ID_MTP_STM) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_SITRONIX, USB_DEVICE_ID_MTP_SITRONIX) }, { } }; MODULE_DEVICE_TABLE(hid, stantum_devices); diff --git a/drivers/hid/hid-topseed.c b/drivers/hid/hid-topseed.c index 5771f85..956ed9a 100644 --- a/drivers/hid/hid-topseed.c +++ b/drivers/hid/hid-topseed.c @@ -64,6 +64,7 @@ static int ts_input_mapping(struct hid_device *hdev, struct hid_input *hi, static const struct hid_device_id ts_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) }, { HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED2, USB_DEVICE_ID_TOPSEED2_RF_COMBO) }, { } }; diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c index 807dcd1..724f46e 100644 --- a/drivers/hid/hid-wacom.c +++ b/drivers/hid/hid-wacom.c @@ -230,7 +230,7 @@ static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report, input_report_key(input, BTN_RIGHT, 0); input_report_key(input, BTN_MIDDLE, 0); input_report_abs(input, ABS_DISTANCE, - input->absmax[ABS_DISTANCE]); + input_abs_get_max(input, ABS_DISTANCE)); } else { input_report_key(input, BTN_TOUCH, 0); input_report_key(input, BTN_STYLUS, 0); @@ -383,38 +383,37 @@ move_on: /* Basics */ input->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_REL); - input->absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | - BIT(ABS_PRESSURE) | BIT(ABS_DISTANCE); - input->relbit[0] |= BIT(REL_WHEEL); - set_bit(BTN_TOOL_PEN, input->keybit); - set_bit(BTN_TOUCH, input->keybit); - set_bit(BTN_STYLUS, input->keybit); - set_bit(BTN_STYLUS2, input->keybit); - set_bit(BTN_LEFT, input->keybit); - set_bit(BTN_RIGHT, input->keybit); - set_bit(BTN_MIDDLE, input->keybit); + + __set_bit(REL_WHEEL, input->relbit); + + __set_bit(BTN_TOOL_PEN, input->keybit); + __set_bit(BTN_TOUCH, input->keybit); + __set_bit(BTN_STYLUS, input->keybit); + __set_bit(BTN_STYLUS2, input->keybit); + __set_bit(BTN_LEFT, input->keybit); + __set_bit(BTN_RIGHT, input->keybit); + __set_bit(BTN_MIDDLE, input->keybit); /* Pad */ input->evbit[0] |= BIT(EV_MSC); - input->mscbit[0] |= BIT(MSC_SERIAL); - set_bit(BTN_0, input->keybit); - set_bit(BTN_1, input->keybit); - set_bit(BTN_TOOL_FINGER, input->keybit); - /* Distance, rubber and mouse */ - input->absbit[0] |= BIT(ABS_DISTANCE); - set_bit(BTN_TOOL_RUBBER, input->keybit); - set_bit(BTN_TOOL_MOUSE, input->keybit); + __set_bit(MSC_SERIAL, input->mscbit); - input->absmax[ABS_PRESSURE] = 511; - input->absmax[ABS_DISTANCE] = 32; + __set_bit(BTN_0, input->keybit); + __set_bit(BTN_1, input->keybit); + __set_bit(BTN_TOOL_FINGER, input->keybit); - input->absmax[ABS_X] = 16704; - input->absmax[ABS_Y] = 12064; - input->absfuzz[ABS_X] = 4; - input->absfuzz[ABS_Y] = 4; + /* Distance, rubber and mouse */ + __set_bit(BTN_TOOL_RUBBER, input->keybit); + __set_bit(BTN_TOOL_MOUSE, input->keybit); + + input_set_abs_params(input, ABS_X, 0, 16704, 4, 0); + input_set_abs_params(input, ABS_Y, 0, 12064, 4, 0); + input_set_abs_params(input, ABS_PRESSURE, 0, 511, 0, 0); + input_set_abs_params(input, ABS_DISTANCE, 0, 32, 0, 0); return 0; + err_free: kfree(wdata); return ret; diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 47d70c5..8a4b32d 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -109,6 +109,12 @@ static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t int ret = 0; mutex_lock(&minors_lock); + + if (!hidraw_table[minor]) { + ret = -ENODEV; + goto out; + } + dev = hidraw_table[minor]->hid; if (!dev->hid_output_raw_report) { @@ -212,9 +218,13 @@ 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; + int ret; - if (!hidraw_table[minor]) - return -ENODEV; + mutex_lock(&minors_lock); + if (!hidraw_table[minor]) { + ret = -ENODEV; + goto unlock; + } list_del(&list->node); dev = hidraw_table[minor]; @@ -227,10 +237,12 @@ static int hidraw_release(struct inode * inode, struct file * file) kfree(list->hidraw); } } - kfree(list); + ret = 0; +unlock: + mutex_unlock(&minors_lock); - return 0; + return ret; } static long hidraw_ioctl(struct file *file, unsigned int cmd, @@ -244,6 +256,10 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, mutex_lock(&minors_lock); dev = hidraw_table[minor]; + if (!dev) { + ret = -ENODEV; + goto out; + } switch (cmd) { case HIDIOCGRDESCSIZE: @@ -317,6 +333,7 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, ret = -ENOTTY; } +out: mutex_unlock(&minors_lock); return ret; } @@ -329,6 +346,7 @@ static const struct file_operations hidraw_ops = { .open = hidraw_open, .release = hidraw_release, .unlocked_ioctl = hidraw_ioctl, + .llseek = noop_llseek, }; void hidraw_report_event(struct hid_device *hid, u8 *data, int len) diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index b729c02..5489eab 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -807,9 +807,10 @@ static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t co struct usb_host_interface *interface = intf->cur_altsetting; int ret; - if (usbhid->urbout) { + if (usbhid->urbout && report_type != HID_FEATURE_REPORT) { int actual_length; int skipped_report_id = 0; + if (buf[0] == 0x0) { /* Don't send the Report ID */ buf++; @@ -828,6 +829,7 @@ static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t co } } else { int skipped_report_id = 0; + int report_id = buf[0]; if (buf[0] == 0x0) { /* Don't send the Report ID */ buf++; @@ -837,7 +839,7 @@ static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t co ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), HID_REQ_SET_REPORT, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - ((report_type + 1) << 8) | *buf, + ((report_type + 1) << 8) | report_id, interface->desc.bInterfaceNumber, buf, count, USB_CTRL_SET_TIMEOUT); /* count also the report id, if this was a numbered report. */ @@ -1445,6 +1447,11 @@ static const struct hid_device_id hid_usb_table[] = { { } }; +struct usb_interface *usbhid_find_interface(int minor) +{ + return usb_find_interface(&hid_driver, minor); +} + static struct hid_driver hid_usb_driver = { .name = "generic-usb", .id_table = hid_usb_table, @@ -1463,9 +1470,6 @@ static int __init hid_init(void) retval = usbhid_quirks_init(quirks_param); if (retval) goto usbhid_quirks_init_fail; - retval = hiddev_init(); - if (retval) - goto hiddev_init_fail; retval = usb_register(&hid_driver); if (retval) goto usb_register_fail; @@ -1473,8 +1477,6 @@ static int __init hid_init(void) return 0; usb_register_fail: - hiddev_exit(); -hiddev_init_fail: usbhid_quirks_exit(); usbhid_quirks_init_fail: hid_unregister_driver(&hid_usb_driver); @@ -1487,7 +1489,6 @@ no_queue: static void __exit hid_exit(void) { usb_deregister(&hid_driver); - hiddev_exit(); usbhid_quirks_exit(); hid_unregister_driver(&hid_usb_driver); destroy_workqueue(resumption_waker); diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 162aa4b..5457135 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -33,8 +33,9 @@ static const struct hid_blacklist { { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD }, - { USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH, HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_DWAV, USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER, HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET }, { USB_VENDOR_ID_MOJO, USB_DEVICE_ID_RETRO_ADAPTER, HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, @@ -61,6 +62,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FLIGHT_SIM_YOKE, HID_QUIRK_NOGET }, { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_PRO_PEDALS, HID_QUIRK_NOGET }, { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET }, { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET }, { USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET }, @@ -68,6 +70,8 @@ static const struct hid_blacklist { { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET }, { USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_PF1209, HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U, HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_KNA5, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, @@ -78,6 +82,8 @@ static const struct hid_blacklist { { USB_VENDOR_ID_PI_ENGINEERING, USB_DEVICE_ID_PI_ENGINEERING_VEC_USB_FOOTPEDAL, HID_QUIRK_HIDINPUT_FORCE }, + { USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_MULTI_TOUCH, HID_QUIRK_MULTI_INPUT }, + { 0, 0 } }; diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 254a003..fedd88d 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -67,8 +67,6 @@ struct hiddev_list { struct mutex thread_lock; }; -static struct usb_driver hiddev_driver; - /* * Find a report, given the report's type and ID. The ID can be specified * indirectly by REPORT_ID_FIRST (which returns the first report of the given @@ -266,13 +264,15 @@ static int hiddev_open(struct inode *inode, struct file *file) { struct hiddev_list *list; struct usb_interface *intf; + struct hid_device *hid; struct hiddev *hiddev; int res; - intf = usb_find_interface(&hiddev_driver, iminor(inode)); + intf = usbhid_find_interface(iminor(inode)); if (!intf) return -ENODEV; - hiddev = usb_get_intfdata(intf); + hid = usb_get_intfdata(intf); + hiddev = hid->hiddev; if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL))) return -ENOMEM; @@ -587,7 +587,7 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) struct hiddev_list *list = file->private_data; struct hiddev *hiddev = list->hiddev; struct hid_device *hid = hiddev->hid; - struct usb_device *dev = hid_to_usb_dev(hid); + struct usb_device *dev; struct hiddev_collection_info cinfo; struct hiddev_report_info rinfo; struct hiddev_field_info finfo; @@ -601,9 +601,11 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) /* Called without BKL by compat methods so no BKL taken */ /* FIXME: Who or what stop this racing with a disconnect ?? */ - if (!hiddev->exist) + if (!hiddev->exist || !hid) return -EIO; + dev = hid_to_usb_dev(hid); + switch (cmd) { case HIDIOCGVERSION: @@ -843,6 +845,7 @@ static const struct file_operations hiddev_fops = { #ifdef CONFIG_COMPAT .compat_ioctl = hiddev_compat_ioctl, #endif + .llseek = noop_llseek, }; static char *hiddev_devnode(struct device *dev, mode_t *mode) @@ -888,7 +891,6 @@ int hiddev_connect(struct hid_device *hid, unsigned int force) hid->hiddev = hiddev; hiddev->hid = hid; hiddev->exist = 1; - usb_set_intfdata(usbhid->intf, usbhid); retval = usb_register_dev(usbhid->intf, &hiddev_class); if (retval) { err_hid("Not able to get a minor for this device."); @@ -922,41 +924,3 @@ void hiddev_disconnect(struct hid_device *hid) kfree(hiddev); } } - -/* Currently this driver is a USB driver. It's not a conventional one in - * the sense that it doesn't probe at the USB level. Instead it waits to - * be connected by HID through the hiddev_connect / hiddev_disconnect - * routines. The reason to register as a USB device is to gain part of the - * minor number space from the USB major. - * - * In theory, should the HID code be generalized to more than one physical - * medium (say, IEEE 1384), this driver will probably need to register its - * own major number, and in doing so, no longer need to register with USB. - * At that point the probe routine and hiddev_driver struct below will no - * longer be useful. - */ - - -/* We never attach in this manner, and rely on HID to connect us. This - * is why there is no disconnect routine defined in the usb_driver either. - */ -static int hiddev_usbd_probe(struct usb_interface *intf, - const struct usb_device_id *hiddev_info) -{ - return -ENODEV; -} - -static /* const */ struct usb_driver hiddev_driver = { - .name = "hiddev", - .probe = hiddev_usbd_probe, -}; - -int __init hiddev_init(void) -{ - return usb_register(&hiddev_driver); -} - -void hiddev_exit(void) -{ - usb_deregister(&hiddev_driver); -} diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index 693fd3e..89d2e84 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h @@ -42,6 +42,7 @@ void usbhid_submit_report (struct hid_device *hid, struct hid_report *report, unsigned char dir); int usbhid_get_power(struct hid_device *hid); void usbhid_put_power(struct hid_device *hid); +struct usb_interface *usbhid_find_interface(int minor); /* iofl flags */ #define HID_CTRL_RUNNING 1 |