From b6353f4f36f03a12edaf3fa5365b475a28106035 Mon Sep 17 00:00:00 2001 From: Stephane Chatty Date: Tue, 22 Dec 2009 23:04:17 +0100 Subject: [PATCH 01/41] HID: Support for 3M multitouch panel Add support for 3M multitouch panels. Signed-off-by: Stephane Chatty [jkosina@suse.cz: fix build failure because of inconsistent 3M/MMM defines] Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 7 + drivers/hid/Makefile | 1 + drivers/hid/hid-3m-pct.c | 291 +++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 3 + 5 files changed, 303 insertions(+) create mode 100644 drivers/hid/hid-3m-pct.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 24d90ea246ce..a05133f645bc 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -55,6 +55,13 @@ source "drivers/hid/usbhid/Kconfig" menu "Special HID drivers" depends on HID +config HID_3M_PCT + tristate "3M PCT" if EMBEDDED + depends on USB_HID + default !EMBEDDED + ---help--- + Support for 3M PCT touch screens. + config HID_A4TECH tristate "A4 tech" if EMBEDDED depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 0de2dff5542c..1bdfb97910f1 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -19,6 +19,7 @@ ifdef CONFIG_LOGIRUMBLEPAD2_FF hid-logitech-objs += hid-lg2ff.o endif +obj-$(CONFIG_HID_3M_PCT) += hid-3m-pct.o obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o obj-$(CONFIG_HID_APPLE) += hid-apple.o obj-$(CONFIG_HID_BELKIN) += hid-belkin.o diff --git a/drivers/hid/hid-3m-pct.c b/drivers/hid/hid-3m-pct.c new file mode 100644 index 000000000000..6d11e3dbbbff --- /dev/null +++ b/drivers/hid/hid-3m-pct.c @@ -0,0 +1,291 @@ +/* + * HID driver for 3M PCT multitouch panels + * + * Copyright (c) 2009 Stephane Chatty + * + */ + +/* + * 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 +#include +#include +#include + +MODULE_VERSION("0.6"); +MODULE_AUTHOR("Stephane Chatty "); +MODULE_DESCRIPTION("3M PCT multitouch panels"); +MODULE_LICENSE("GPL"); + +#include "hid-ids.h" + +struct mmm_finger { + __s32 x, y; + __u8 rank; + bool touch, valid; +}; + +struct mmm_data { + struct mmm_finger f[10]; + __u8 curid, num; + bool touch, valid; +}; + +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) +{ + switch (usage->hid & HID_USAGE_PAGE) { + + case HID_UP_BUTTON: + return -1; + + case HID_UP_GENDESK: + switch (usage->hid) { + case HID_GD_X: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_POSITION_X); + /* touchscreen emulation */ + input_set_abs_params(hi->input, ABS_X, + field->logical_minimum, + field->logical_maximum, 0, 0); + return 1; + case HID_GD_Y: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_POSITION_Y); + /* touchscreen emulation */ + input_set_abs_params(hi->input, ABS_Y, + field->logical_minimum, + field->logical_maximum, 0, 0); + return 1; + } + return 0; + + case HID_UP_DIGITIZER: + switch (usage->hid) { + /* we do not want to map these: no input-oriented meaning */ + case 0x14: + case 0x23: + case HID_DG_INPUTMODE: + case HID_DG_DEVICEINDEX: + case HID_DG_CONTACTCOUNT: + case HID_DG_CONTACTMAX: + case HID_DG_INRANGE: + case HID_DG_CONFIDENCE: + return -1; + case HID_DG_TIPSWITCH: + /* touchscreen emulation */ + hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); + return 1; + case HID_DG_CONTACTID: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_TRACKING_ID); + return 1; + } + /* let hid-input decide for the others */ + return 0; + + case 0xff000000: + /* we do not want to map these: no input-oriented meaning */ + return -1; + } + + return 0; +} + +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) +{ + if (usage->type == EV_KEY || usage->type == EV_ABS) + clear_bit(usage->code, *bit); + + return 0; +} + +/* + * this function is called when a whole packet has been received and processed, + * so that it can decide what to send to the input layer. + */ +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) { + struct mmm_finger *f = &md->f[i]; + if (!f->valid) { + /* this finger is just placeholder data, ignore */ + } else if (f->touch) { + /* this finger is on the screen */ + input_event(input, EV_ABS, ABS_MT_TRACKING_ID, i); + input_event(input, EV_ABS, ABS_MT_POSITION_X, f->x); + input_event(input, EV_ABS, ABS_MT_POSITION_Y, f->y); + 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) + 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; + } + f->valid = 0; + } + + /* touchscreen emulation */ + if (oldest) { + if (pressed) + 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) { + input_event(input, EV_KEY, BTN_TOUCH, 0); + } +} + +/* + * this function is called upon all reports + * so that we can accumulate contact point information, + * and call input_mt_sync after each point. + */ +static int mmm_event(struct hid_device *hid, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct mmm_data *md = hid_get_drvdata(hid); + /* + * strangely, this function can be called before + * field->hidinput is initialized! + */ + if (hid->claimed & HID_CLAIMED_INPUT) { + struct input_dev *input = field->hidinput->input; + switch (usage->hid) { + case HID_DG_TIPSWITCH: + md->touch = value; + break; + case HID_DG_CONFIDENCE: + md->valid = value; + break; + case HID_DG_CONTACTID: + if (md->valid) { + md->curid = value; + md->f[value].touch = md->touch; + md->f[value].valid = 1; + } + break; + case HID_GD_X: + if (md->valid) + md->f[md->curid].x = value; + break; + case HID_GD_Y: + if (md->valid) + md->f[md->curid].y = value; + break; + case HID_DG_CONTACTCOUNT: + mmm_filter_event(md, input); + break; + } + } + + /* we have handled the hidinput part, now remains hiddev */ + if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event) + hid->hiddev_hid_event(hid, field, usage, value); + + return 1; +} + +static int mmm_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + struct mmm_data *md; + + md = kzalloc(sizeof(struct mmm_data), GFP_KERNEL); + if (!md) { + dev_err(&hdev->dev, "cannot allocate 3M data\n"); + return -ENOMEM; + } + hid_set_drvdata(hdev, md); + + ret = hid_parse(hdev); + if (!ret) + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + + if (ret) + kfree(md); + return ret; +} + +static void mmm_remove(struct hid_device *hdev) +{ + hid_hw_stop(hdev); + kfree(hid_get_drvdata(hdev)); + hid_set_drvdata(hdev, NULL); +} + +static const struct hid_device_id mmm_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) }, + { } +}; +MODULE_DEVICE_TABLE(hid, mmm_devices); + +static const struct hid_usage_id mmm_grabbed_usages[] = { + { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID }, + { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1} +}; + +static struct hid_driver mmm_driver = { + .name = "3m-pct", + .id_table = mmm_devices, + .probe = mmm_probe, + .remove = mmm_remove, + .input_mapping = mmm_input_mapping, + .input_mapped = mmm_input_mapped, + .usage_table = mmm_grabbed_usages, + .event = mmm_event, +}; + +static int __init mmm_init(void) +{ + return hid_register_driver(&mmm_driver); +} + +static void __exit mmm_exit(void) +{ + hid_unregister_driver(&mmm_driver); +} + +module_init(mmm_init); +module_exit(mmm_exit); +MODULE_LICENSE("GPL"); + diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 80792d38d25c..70c25a056205 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1248,6 +1248,7 @@ EXPORT_SYMBOL_GPL(hid_disconnect); /* a list of devices for which there is a specialized driver on HID bus */ static const struct hid_device_id hid_blacklist[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M1968) }, { 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_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 3839340e293a..3d55f983dbaf 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -18,6 +18,9 @@ #ifndef HID_IDS_H_FILE #define HID_IDS_H_FILE +#define USB_VENDOR_ID_3M 0x0596 +#define USB_DEVICE_ID_3M1968 0x0500 + #define USB_VENDOR_ID_A4TECH 0x09da #define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006 #define USB_DEVICE_ID_A4TECH_X5_005D 0x000a From 4b186f72033611c2b526c7341534e71ee4afd222 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 23 Dec 2009 13:12:32 +0100 Subject: [PATCH 02/41] HID: make 3M PCT touchscreen driver standalone config option The point behind 'default !EMBEDDED' for certain HID drivers that simple and straightforward quirks for HID devices (which are implemented as drivers on HID bus) wouldn't have to be enabled separately, if the device is otherwise more-or-less HID standard compliant. But this driver is rather standalone driver, so we'd want to have it normally selectable. Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index a05133f645bc..0c8ce3a68d27 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -56,9 +56,8 @@ menu "Special HID drivers" depends on HID config HID_3M_PCT - tristate "3M PCT" if EMBEDDED + tristate "3M PCT" depends on USB_HID - default !EMBEDDED ---help--- Support for 3M PCT touch screens. From d3fb5454a8474d5d22c8f8fe4d043b05732d91d5 Mon Sep 17 00:00:00 2001 From: Stephane Chatty Date: Mon, 4 Jan 2010 12:04:08 +0100 Subject: [PATCH 03/41] HID: add support for Stantum multitouch panel Added support for the Stantum multitouch panel. Signed-off-by: Stephane Chatty Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 6 + drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 3 + drivers/hid/hid-stantum.c | 283 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 294 insertions(+) create mode 100644 drivers/hid/hid-stantum.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 0c8ce3a68d27..84272c690310 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -247,6 +247,12 @@ config HID_SONY ---help--- Support for Sony PS3 controller. +config HID_STANTUM + tristate "Stantum" if EMBEDDED + depends on USB_HID + ---help--- + Support for Stantum multitouch panel. + config HID_SUNPLUS tristate "Sunplus" if EMBEDDED depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 1bdfb97910f1..f8dc5bac79bd 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o obj-$(CONFIG_HID_SONY) += hid-sony.o +obj-$(CONFIG_HID_STANTUM) += hid-stantum.o obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o obj-$(CONFIG_HID_GREENASIA) += hid-gaff.o obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 70c25a056205..6462c923805f 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1339,6 +1339,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, { HID_USB_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_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 3d55f983dbaf..07e056b0e188 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -393,6 +393,9 @@ #define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST 0x0034 #define USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST 0x0046 +#define USB_VENDOR_ID_STANTUM 0x1f87 +#define USB_DEVICE_ID_MTP 0x0002 + #define USB_VENDOR_ID_SUN 0x0430 #define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab diff --git a/drivers/hid/hid-stantum.c b/drivers/hid/hid-stantum.c new file mode 100644 index 000000000000..add965dab932 --- /dev/null +++ b/drivers/hid/hid-stantum.c @@ -0,0 +1,283 @@ +/* + * HID driver for Stantum multitouch panels + * + * Copyright (c) 2009 Stephane Chatty + * + */ + +/* + * 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 +#include +#include + +MODULE_VERSION("0.6"); +MODULE_AUTHOR("Stephane Chatty "); +MODULE_DESCRIPTION("Stantum HID multitouch panels"); +MODULE_LICENSE("GPL"); + +#include "hid-ids.h" + +struct stantum_data { + __s32 x, y, z, w, h; /* x, y, pressure, width, height */ + __u16 id; /* touch id */ + bool valid; /* valid finger data, or just placeholder? */ + bool first; /* first finger in the HID packet? */ + bool activity; /* at least one active finger so far? */ +}; + +static int stantum_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + switch (usage->hid & HID_USAGE_PAGE) { + + case HID_UP_GENDESK: + switch (usage->hid) { + case HID_GD_X: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_POSITION_X); + /* touchscreen emulation */ + input_set_abs_params(hi->input, ABS_X, + field->logical_minimum, + field->logical_maximum, 0, 0); + return 1; + case HID_GD_Y: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_POSITION_Y); + /* touchscreen emulation */ + input_set_abs_params(hi->input, ABS_Y, + field->logical_minimum, + field->logical_maximum, 0, 0); + return 1; + } + return 0; + + case HID_UP_DIGITIZER: + switch (usage->hid) { + case HID_DG_INRANGE: + case HID_DG_CONFIDENCE: + case HID_DG_INPUTMODE: + case HID_DG_DEVICEINDEX: + case HID_DG_CONTACTCOUNT: + case HID_DG_CONTACTMAX: + case HID_DG_TIPPRESSURE: + return -1; + + case HID_DG_TIPSWITCH: + /* touchscreen emulation */ + hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); + return 1; + + case HID_DG_WIDTH: + hid_map_usage(hi, usage, bit, max, + 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); + input_set_abs_params(hi->input, ABS_MT_ORIENTATION, + 1, 1, 0, 0); + return 1; + case HID_DG_CONTACTID: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_TRACKING_ID); + return 1; + + } + return 0; + + case 0xff000000: + /* no input-oriented meaning */ + return -1; + } + + return 0; +} + +static int stantum_input_mapped(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if (usage->type == EV_KEY || usage->type == EV_ABS) + clear_bit(usage->code, *bit); + + return 0; +} + +/* + * this function is called when a whole finger has been parsed, + * so that it can decide what to send to the input layer. + */ +static void stantum_filter_event(struct stantum_data *sd, + struct input_dev *input) +{ + bool wide; + + if (!sd->valid) { + /* + * touchscreen emulation: if the first finger is not valid and + * there previously was finger activity, this is a release + */ + if (sd->first && sd->activity) { + input_event(input, EV_KEY, BTN_TOUCH, 0); + sd->activity = false; + } + return; + } + + input_event(input, EV_ABS, ABS_MT_TRACKING_ID, sd->id); + input_event(input, EV_ABS, ABS_MT_POSITION_X, sd->x); + input_event(input, EV_ABS, ABS_MT_POSITION_Y, sd->y); + + wide = (sd->w > sd->h); + input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide); + input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, wide ? sd->w : sd->h); + input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, wide ? sd->h : sd->w); + +#if 0 + /* MT_PRESSURE does not exist yet */ + input_event(input, EV_ABS, ABS_MT_PRESSURE, sd->z); +#endif + + input_mt_sync(input); + sd->valid = false; + sd->first = false; + + /* touchscreen emulation */ + if (sd->first) { + if (!sd->activity) { + input_event(input, EV_KEY, BTN_TOUCH, 1); + sd->activity = true; + } + input_event(input, EV_ABS, ABS_X, sd->x); + input_event(input, EV_ABS, ABS_Y, sd->y); + } +} + + +static int stantum_event(struct hid_device *hid, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct stantum_data *sd = hid_get_drvdata(hid); + + if (hid->claimed & HID_CLAIMED_INPUT) { + struct input_dev *input = field->hidinput->input; + + switch (usage->hid) { + case HID_DG_INRANGE: + /* this is the last field in a finger */ + stantum_filter_event(sd, input); + break; + case HID_DG_WIDTH: + sd->w = value; + break; + case HID_DG_HEIGHT: + sd->h = value; + break; + case HID_GD_X: + sd->x = value; + break; + case HID_GD_Y: + sd->y = value; + break; + case HID_DG_TIPPRESSURE: + sd->z = value; + break; + case HID_DG_CONTACTID: + sd->id = value; + break; + case HID_DG_CONFIDENCE: + sd->valid = !!value; + break; + case 0xff000002: + /* this comes only before the first finger */ + sd->first = true; + break; + + default: + /* ignore the others */ + return 1; + } + } + + /* we have handled the hidinput part, now remains hiddev */ + if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event) + hid->hiddev_hid_event(hid, field, usage, value); + + return 1; +} + +static int stantum_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int ret; + struct stantum_data *sd; + + sd = kmalloc(sizeof(struct stantum_data), GFP_KERNEL); + if (!sd) { + dev_err(&hdev->dev, "cannot allocate Stantum data\n"); + return -ENOMEM; + } + sd->valid = false; + sd->first = false; + sd->activity = false; + hid_set_drvdata(hdev, sd); + + ret = hid_parse(hdev); + if (!ret) + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + + if (ret) + kfree(sd); + + return ret; +} + +static void stantum_remove(struct hid_device *hdev) +{ + hid_hw_stop(hdev); + kfree(hid_get_drvdata(hdev)); + hid_set_drvdata(hdev, NULL); +} + +static const struct hid_device_id stantum_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM, USB_DEVICE_ID_MTP) }, + { } +}; +MODULE_DEVICE_TABLE(hid, stantum_devices); + +static const struct hid_usage_id stantum_grabbed_usages[] = { + { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID }, + { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1} +}; + +static struct hid_driver stantum_driver = { + .name = "stantum", + .id_table = stantum_devices, + .probe = stantum_probe, + .remove = stantum_remove, + .input_mapping = stantum_input_mapping, + .input_mapped = stantum_input_mapped, + .usage_table = stantum_grabbed_usages, + .event = stantum_event, +}; + +static int __init stantum_init(void) +{ + return hid_register_driver(&stantum_driver); +} + +static void __exit stantum_exit(void) +{ + hid_unregister_driver(&stantum_driver); +} + +module_init(stantum_init); +module_exit(stantum_exit); + From 92688c0c3c1c9e2daf705d307e8fda1b5a180d26 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 4 Jan 2010 12:04:59 +0100 Subject: [PATCH 04/41] HID: make Stantum driver standalone config option Analogically to commit "HID: make 3M PCT touchscreen driver standalone config option", remove the dependency of Stantum driver on CONFIG_EMBEDDED, as it is a standalone driver rather than device quirk. Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 84272c690310..5f73774164d8 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -248,7 +248,7 @@ config HID_SONY Support for Sony PS3 controller. config HID_STANTUM - tristate "Stantum" if EMBEDDED + tristate "Stantum" depends on USB_HID ---help--- Support for Stantum multitouch panel. From cf2f765f1896064e34c6f0f2ef896ff058dd5c06 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 4 Jan 2010 12:20:56 +0100 Subject: [PATCH 05/41] HID: handle joysticks with large number of buttons Current HID code doesn't properly handle HID joysticks which have larger number of buttons than what fits into current range reserved for BTN_JOYSTICK. One such joystick reported to not work properly is Saitek X52 Pro Flight System. We can't extend the range to fit more buttons in, because of backwards compatibility reasons. Therefore this patch introduces a new BTN_TRIGGER_HAPPY range, and uses these to map the buttons which are over BTN_JOYSTICK limit. Acked-by: Dmitry Torokhov [for the input.h part] Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 7 ++++++- include/linux/input.h | 42 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 5862b0f3b55d..dad7aae9c975 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -198,7 +198,12 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel switch (field->application) { case HID_GD_MOUSE: case HID_GD_POINTER: code += 0x110; break; - case HID_GD_JOYSTICK: code += 0x120; break; + case HID_GD_JOYSTICK: + if (code <= 0xf) + code += BTN_JOYSTICK; + else + code += BTN_TRIGGER_HAPPY; + break; case HID_GD_GAMEPAD: code += 0x130; break; default: switch (field->physical) { diff --git a/include/linux/input.h b/include/linux/input.h index 7be8a6537b57..97f98ca9b040 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -597,6 +597,48 @@ struct input_absinfo { #define KEY_CAMERA_FOCUS 0x210 +#define BTN_TRIGGER_HAPPY 0x2c0 +#define BTN_TRIGGER_HAPPY1 0x2c0 +#define BTN_TRIGGER_HAPPY2 0x2c1 +#define BTN_TRIGGER_HAPPY3 0x2c2 +#define BTN_TRIGGER_HAPPY4 0x2c3 +#define BTN_TRIGGER_HAPPY5 0x2c4 +#define BTN_TRIGGER_HAPPY6 0x2c5 +#define BTN_TRIGGER_HAPPY7 0x2c6 +#define BTN_TRIGGER_HAPPY8 0x2c7 +#define BTN_TRIGGER_HAPPY9 0x2c8 +#define BTN_TRIGGER_HAPPY10 0x2c9 +#define BTN_TRIGGER_HAPPY11 0x2ca +#define BTN_TRIGGER_HAPPY12 0x2cb +#define BTN_TRIGGER_HAPPY13 0x2cc +#define BTN_TRIGGER_HAPPY14 0x2cd +#define BTN_TRIGGER_HAPPY15 0x2ce +#define BTN_TRIGGER_HAPPY16 0x2cf +#define BTN_TRIGGER_HAPPY17 0x2d0 +#define BTN_TRIGGER_HAPPY18 0x2d1 +#define BTN_TRIGGER_HAPPY19 0x2d2 +#define BTN_TRIGGER_HAPPY20 0x2d3 +#define BTN_TRIGGER_HAPPY21 0x2d4 +#define BTN_TRIGGER_HAPPY22 0x2d5 +#define BTN_TRIGGER_HAPPY23 0x2d6 +#define BTN_TRIGGER_HAPPY24 0x2d7 +#define BTN_TRIGGER_HAPPY25 0x2d8 +#define BTN_TRIGGER_HAPPY26 0x2d9 +#define BTN_TRIGGER_HAPPY27 0x2da +#define BTN_TRIGGER_HAPPY28 0x2db +#define BTN_TRIGGER_HAPPY29 0x2dc +#define BTN_TRIGGER_HAPPY30 0x2dd +#define BTN_TRIGGER_HAPPY31 0x2de +#define BTN_TRIGGER_HAPPY32 0x2df +#define BTN_TRIGGER_HAPPY33 0x2e0 +#define BTN_TRIGGER_HAPPY34 0x2e1 +#define BTN_TRIGGER_HAPPY35 0x2e2 +#define BTN_TRIGGER_HAPPY36 0x2e3 +#define BTN_TRIGGER_HAPPY37 0x2e4 +#define BTN_TRIGGER_HAPPY38 0x2e5 +#define BTN_TRIGGER_HAPPY39 0x2e6 +#define BTN_TRIGGER_HAPPY40 0x2e7 + /* We avoid low common keys in module aliases so they don't get huge. */ #define KEY_MIN_INTERESTING KEY_MUTE #define KEY_MAX 0x2ff From 722612cd51cf1b574c89dff57cc5dbedf1f645bb Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Tue, 5 Jan 2010 11:45:52 +0100 Subject: [PATCH 06/41] HID: fix parsing of local delimiter with size 0 Acording to HID standard 1.11, value 0 allows for size being 0. Local delimiter tag has has 0 one of the possible values. Therefore we need to handle this case properly, to be fully compliant with the specification. Reported-by: Marcin Tolysz Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 6462c923805f..a4b496c68259 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -387,7 +387,8 @@ static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) __u32 data; unsigned n; - if (item->size == 0) { + /* Local delimiter could have value 0, which allows size to be 0 */ + if (item->size == 0 && item->tag != HID_LOCAL_ITEM_TAG_DELIMITER) { dbg_hid("item data expected for local item\n"); return -1; } From 54a6593d65e638ad7e1e8cc986159d76054dab4b Mon Sep 17 00:00:00 2001 From: Stefan Glasenhardt Date: Tue, 5 Jan 2010 23:30:30 +0100 Subject: [PATCH 07/41] HID: allow disabling hard-coded ISO-layout for Apple keyboards This patch adds a new option named "iso_layout" to the driver "hid-apple.ko", to allow disabling of the hard-coded ISO-layout. Disabling the hard-coded layout solves the problem that the kernel-module only works perfectly for the english/american version of the Apple aluminum keyboard. Other versions have swapped keys, e.g. the "<"-key is swapped with "^"-key on the german keyboard. There is a very long bug-entry on Launchpad to this problem: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/214786 Signed-off-by: Stefan Glasenhardt Signed-off-by: Jiri Kosina --- drivers/hid/hid-apple.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 4b96e7a898cf..1433cbbaa99f 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -40,6 +40,11 @@ module_param(fnmode, uint, 0644); MODULE_PARM_DESC(fnmode, "Mode of fn key on Apple keyboards (0 = disabled, " "[1] = fkeyslast, 2 = fkeysfirst)"); +static unsigned int iso_layout = 1; +module_param(iso_layout, uint, 0644); +MODULE_PARM_DESC(iso_layout, "Enable/Disable hardcoded ISO-layout of the keyboard. " + "(0 = disabled, [1] = enabled)"); + struct apple_sc { unsigned long quirks; unsigned int fn_on; @@ -199,11 +204,13 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, } } - if (asc->quirks & APPLE_ISO_KEYBOARD) { - trans = apple_find_translation(apple_iso_keyboard, usage->code); - if (trans) { - input_event(input, usage->type, trans->to, value); - return 1; + if (iso_layout) { + if (asc->quirks & APPLE_ISO_KEYBOARD) { + trans = apple_find_translation(apple_iso_keyboard, usage->code); + if (trans) { + input_event(input, usage->type, trans->to, value); + return 1; + } } } From d67dec5b2cc208215de21dc7806945bf6a6e85d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20N=C3=A9meth?= Date: Sun, 10 Jan 2010 17:59:22 +0100 Subject: [PATCH 08/41] HID: make USB device id constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The id_table field of the struct usb_device_id is constant in so it is worth to make the initialization data also constant. The semantic match that finds this kind of pattern is as follows: (http://coccinelle.lip6.fr/) // @r@ disable decl_init,const_decl_init; identifier I1, I2, x; @@ struct I1 { ... const struct I2 *x; ... }; @s@ identifier r.I1, y; identifier r.x, E; @@ struct I1 y = { .x = E, }; @c@ identifier r.I2; identifier s.E; @@ const struct I2 E[] = ... ; @depends on !c@ identifier r.I2; identifier s.E; @@ + const struct I2 E[] = ...; // Signed-off-by: Márton Németh Cc: Julia Lawall Cc: cocci@diku.dk Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 0258289f3b3e..e72751d6e3e7 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -1342,7 +1342,7 @@ static int hid_reset_resume(struct usb_interface *intf) #endif /* CONFIG_PM */ -static struct usb_device_id hid_usb_ids [] = { +static const struct usb_device_id hid_usb_ids[] = { { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS, .bInterfaceClass = USB_INTERFACE_CLASS_HID }, { } /* Terminating entry */ From 74f292ca8c7a2b9370f80d97a49e48174f4c7635 Mon Sep 17 00:00:00 2001 From: Gary Stein Date: Wed, 13 Jan 2010 00:25:58 +0100 Subject: [PATCH 09/41] HID: add driver for the Logitech Flight System G940 Implements a new USB-HID for Force Feedback based on the normal Logitech Force Feedback code and FF-Memless. Currently only supports the FF_CONSTANT effect although the joystick appears to support additional non-standard ones. Signed-off-by: Gary Stein Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 8 ++ drivers/hid/Makefile | 3 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-lg.c | 7 +- drivers/hid/hid-lg.h | 6 ++ drivers/hid/hid-lg3ff.c | 176 ++++++++++++++++++++++++++++++++++++++++ drivers/hid/hid-lgff.c | 1 + 8 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 drivers/hid/hid-lg3ff.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 5f73774164d8..317049b80c35 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -189,6 +189,14 @@ config LOGIRUMBLEPAD2_FF Say Y here if you want to enable force feedback support for Logitech Rumblepad 2 devices. +config LOGIG940_FF + bool "Logitech Flight System G940 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 + Flight System G940 devices. + config HID_MICROSOFT tristate "Microsoft" if EMBEDDED depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index f8dc5bac79bd..ce6c8da28486 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -18,6 +18,9 @@ endif ifdef CONFIG_LOGIRUMBLEPAD2_FF hid-logitech-objs += hid-lg2ff.o endif +ifdef CONFIG_LOGIG940_FF + hid-logitech-objs += hid-lg3ff.o +endif obj-$(CONFIG_HID_3M_PCT) += hid-3m-pct.o obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index a4b496c68259..a0c0c49dec09 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1323,6 +1323,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940) }, { 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) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 07e056b0e188..c7c9cbf8132c 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -300,6 +300,7 @@ #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2 0xc219 #define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283 #define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO 0xc286 +#define USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940 0xc287 #define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294 #define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG 0xc293 #define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295 diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index 9fcd3d017ab3..3677c9037a11 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -34,6 +34,7 @@ #define LG_FF 0x200 #define LG_FF2 0x400 #define LG_RDESC_REL_ABS 0x800 +#define LG_FF3 0x1000 /* * Certain Logitech keyboards send in report #3 keys which are far @@ -266,7 +267,7 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_free; } - if (quirks & (LG_FF | LG_FF2)) + if (quirks & (LG_FF | LG_FF2 | LG_FF3)) connect_mask &= ~HID_CONNECT_FF; ret = hid_hw_start(hdev, connect_mask); @@ -279,6 +280,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) lgff_init(hdev); if (quirks & LG_FF2) lg2ff_init(hdev); + if (quirks & LG_FF3) + lg3ff_init(hdev); return 0; err_free: @@ -331,6 +334,8 @@ static const struct hid_device_id lg_devices[] = { .driver_data = LG_FF }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2), .driver_data = LG_FF2 }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940), + .driver_data = LG_FF3 }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR), .driver_data = LG_RDESC_REL_ABS }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER), diff --git a/drivers/hid/hid-lg.h b/drivers/hid/hid-lg.h index 27ae750ca878..8069c3e8b64a 100644 --- a/drivers/hid/hid-lg.h +++ b/drivers/hid/hid-lg.h @@ -15,4 +15,10 @@ int lg2ff_init(struct hid_device *hdev); static inline int lg2ff_init(struct hid_device *hdev) { return -1; } #endif +#ifdef CONFIG_LOGIG940_FF +int lg3ff_init(struct hid_device *hdev); +#else +static inline int lg3ff_init(struct hid_device *hdev) { return -1; } +#endif + #endif diff --git a/drivers/hid/hid-lg3ff.c b/drivers/hid/hid-lg3ff.c new file mode 100644 index 000000000000..4002832ee4af --- /dev/null +++ b/drivers/hid/hid-lg3ff.c @@ -0,0 +1,176 @@ +/* + * Force feedback support for Logitech Flight System G940 + * + * Copyright (c) 2009 Gary Stein + */ + +/* + * 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 +#include +#include + +#include "usbhid/usbhid.h" +#include "hid-lg.h" + +/* + * G940 Theory of Operation (from experimentation) + * + * There are 63 fields (only 3 of them currently used) + * 0 - seems to be command field + * 1 - 30 deal with the x axis + * 31 -60 deal with the y axis + * + * Field 1 is x axis constant force + * Field 31 is y axis constant force + * + * other interesting fields 1,2,3,4 on x axis + * (same for 31,32,33,34 on y axis) + * + * 0 0 127 127 makes the joystick autocenter hard + * + * 127 0 127 127 makes the joystick loose on the right, + * but stops all movemnt left + * + * -127 0 -127 -127 makes the joystick loose on the left, + * but stops all movement right + * + * 0 0 -127 -127 makes the joystick rattle very hard + * + * I'm sure these are effects that I don't know enough about them + */ + +struct lg3ff_device { + struct hid_report *report; +}; + +static int hid_lg3ff_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, y; + +/* + * Maxusage should always be 63 (maximum fields) + * likely a better way to ensure this data is clean + */ + memset(report->field[0]->value, 0, sizeof(__s32)*report->field[0]->maxusage); + + switch (effect->type) { + case FF_CONSTANT: +/* + * Already clamped in ff_memless + * 0 is center (different then other logitech) + */ + x = effect->u.ramp.start_level; + y = effect->u.ramp.end_level; + + /* send command byte */ + report->field[0]->value[0] = 0x51; + +/* + * Sign backwards from other Force3d pro + * which get recast here in two's complement 8 bits + */ + report->field[0]->value[1] = (unsigned char)(-x); + report->field[0]->value[31] = (unsigned char)(-y); + + usbhid_submit_report(hid, report, USB_DIR_OUT); + break; + } + return 0; +} +static void hid_lg3ff_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); + +/* + * Auto Centering probed from device + * NOTE: deadman's switch on G940 must be covered + * for effects to work + */ + report->field[0]->value[0] = 0x51; + report->field[0]->value[1] = 0x00; + report->field[0]->value[2] = 0x00; + report->field[0]->value[3] = 0x7F; + report->field[0]->value[4] = 0x7F; + report->field[0]->value[31] = 0x00; + report->field[0]->value[32] = 0x00; + report->field[0]->value[33] = 0x7F; + report->field[0]->value[34] = 0x7F; + + usbhid_submit_report(hid, report, USB_DIR_OUT); +} + + +static const signed short ff3_joystick_ac[] = { + FF_CONSTANT, + FF_AUTOCENTER, + -1 +}; + +int lg3ff_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 = ff3_joystick_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; + } + + /* Assume single fixed device G940 */ + for (i = 0; ff_bits[i] >= 0; i++) + set_bit(ff_bits[i], dev->ffbit); + + error = input_ff_create_memless(dev, NULL, hid_lg3ff_play); + if (error) + return error; + + if (test_bit(FF_AUTOCENTER, dev->ffbit)) + dev->ff->set_autocenter = hid_lg3ff_set_autocenter; + + dev_info(&hid->dev, "Force feedback for Logitech Flight System G940 by " + "Gary Stein \n"); + return 0; +} + diff --git a/drivers/hid/hid-lgff.c b/drivers/hid/hid-lgff.c index 987abebe0829..61142b76a9b1 100644 --- a/drivers/hid/hid-lgff.c +++ b/drivers/hid/hid-lgff.c @@ -67,6 +67,7 @@ static const struct dev_type devices[] = { { 0x046d, 0xc219, ff_rumble }, { 0x046d, 0xc283, ff_joystick }, { 0x046d, 0xc286, ff_joystick_ac }, + { 0x046d, 0xc287, ff_joystick_ac }, { 0x046d, 0xc293, ff_joystick }, { 0x046d, 0xc294, ff_wheel }, { 0x046d, 0xc295, ff_joystick }, From 49e4739a0cf681cbfe08c72232c1dcc130b66dde Mon Sep 17 00:00:00 2001 From: Stephane Chatty Date: Wed, 13 Jan 2010 00:29:16 +0100 Subject: [PATCH 10/41] HID: add support for Acer T230H multitouch Add support for the Quanta Optical Touch dual-touch panel, present in the Acer T230H monitor, HP L2105tm, and Packard-Bell Video 200t. Signed-off-by: Stephane Chatty Tested-by: Jerome Vidal Tested-by: Cedric Berthier Acked-by: Dmitry Torokhov Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 6 + drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 3 + drivers/hid/hid-quanta.c | 259 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 270 insertions(+) create mode 100644 drivers/hid/hid-quanta.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 317049b80c35..38e969207636 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -241,6 +241,12 @@ config HID_PETALYNX ---help--- Support for Petalynx Maxter remote control. +config HID_QUANTA + tristate "Quanta Optical Touch" + depends on USB_HID + ---help--- + Support for Quanta Optical Touch dual-touch panels. + config HID_SAMSUNG tristate "Samsung" if EMBEDDED depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index ce6c8da28486..15541c47b172 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o +obj-$(CONFIG_HID_QUANTA) += hid-quanta.o obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index a0c0c49dec09..056384cf05e4 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1338,6 +1338,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) }, { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) }, { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, { HID_USB_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) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index c7c9cbf8132c..b009fd68deae 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -383,6 +383,9 @@ #define USB_VENDOR_ID_SAITEK 0x06a3 #define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 +#define USB_VENDOR_ID_QUANTA 0x0408 +#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH 0x3000 + #define USB_VENDOR_ID_SAMSUNG 0x0419 #define USB_DEVICE_ID_SAMSUNG_IR_REMOTE 0x0001 diff --git a/drivers/hid/hid-quanta.c b/drivers/hid/hid-quanta.c new file mode 100644 index 000000000000..244d61c18a47 --- /dev/null +++ b/drivers/hid/hid-quanta.c @@ -0,0 +1,259 @@ +/* + * HID driver for Quanta Optical Touch dual-touch panels + * + * Copyright (c) 2009-2010 Stephane Chatty + * + */ + +/* + * 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 +#include +#include + +MODULE_VERSION("1.00"); +MODULE_AUTHOR("Stephane Chatty "); +MODULE_DESCRIPTION("Quanta dual-touch panel"); +MODULE_LICENSE("GPL"); + +#include "hid-ids.h" + +struct quanta_data { + __u16 x, y; + __u8 id; + bool valid; /* valid finger data, or just placeholder? */ + bool first; /* is this the first finger in this frame? */ + bool activity_now; /* at least one active finger in this frame? */ + bool activity; /* at least one active finger previously? */ +}; + +static int quanta_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + switch (usage->hid & HID_USAGE_PAGE) { + + case HID_UP_GENDESK: + switch (usage->hid) { + case HID_GD_X: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_POSITION_X); + /* touchscreen emulation */ + input_set_abs_params(hi->input, ABS_X, + field->logical_minimum, + field->logical_maximum, 0, 0); + return 1; + case HID_GD_Y: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_POSITION_Y); + /* touchscreen emulation */ + input_set_abs_params(hi->input, ABS_Y, + field->logical_minimum, + field->logical_maximum, 0, 0); + return 1; + } + return 0; + + case HID_UP_DIGITIZER: + switch (usage->hid) { + case HID_DG_CONFIDENCE: + case HID_DG_TIPSWITCH: + case HID_DG_INPUTMODE: + case HID_DG_DEVICEINDEX: + case HID_DG_CONTACTCOUNT: + case HID_DG_CONTACTMAX: + case HID_DG_TIPPRESSURE: + case HID_DG_WIDTH: + case HID_DG_HEIGHT: + return -1; + case HID_DG_INRANGE: + /* touchscreen emulation */ + hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); + return 1; + case HID_DG_CONTACTID: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_TRACKING_ID); + return 1; + } + return 0; + + case 0xff000000: + /* ignore vendor-specific features */ + return -1; + } + + return 0; +} + +static int quanta_input_mapped(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if (usage->type == EV_KEY || usage->type == EV_ABS) + clear_bit(usage->code, *bit); + + return 0; +} + +/* + * this function is called when a whole finger has been parsed, + * so that it can decide what to send to the input layer. + */ +static void quanta_filter_event(struct quanta_data *td, struct input_dev *input) +{ + + td->first = !td->first; /* touchscreen emulation */ + + if (!td->valid) { + /* + * touchscreen emulation: if no finger in this frame is valid + * and there previously was finger activity, this is a release + */ + if (!td->first && !td->activity_now && td->activity) { + input_event(input, EV_KEY, BTN_TOUCH, 0); + td->activity = false; + } + return; + } + + 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_mt_sync(input); + td->valid = false; + + /* touchscreen emulation: if first active finger in this frame... */ + if (!td->activity_now) { + /* if there was no previous activity, emit touch event */ + if (!td->activity) { + input_event(input, EV_KEY, BTN_TOUCH, 1); + td->activity = true; + } + td->activity_now = true; + /* and in any case this is our preferred finger */ + input_event(input, EV_ABS, ABS_X, td->x); + input_event(input, EV_ABS, ABS_Y, td->y); + } +} + + +static int quanta_event(struct hid_device *hid, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct quanta_data *td = hid_get_drvdata(hid); + + if (hid->claimed & HID_CLAIMED_INPUT) { + struct input_dev *input = field->hidinput->input; + + switch (usage->hid) { + case HID_DG_INRANGE: + td->valid = !!value; + break; + case HID_GD_X: + td->x = value; + break; + case HID_GD_Y: + td->y = value; + quanta_filter_event(td, input); + break; + case HID_DG_CONTACTID: + td->id = value; + break; + case HID_DG_CONTACTCOUNT: + /* touch emulation: this is the last field in a frame */ + td->first = false; + td->activity_now = false; + break; + case HID_DG_CONFIDENCE: + case HID_DG_TIPSWITCH: + /* avoid interference from generic hidinput handling */ + break; + + default: + /* fallback to the generic hidinput handling */ + return 0; + } + } + + /* we have handled the hidinput part, now remains hiddev */ + if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event) + hid->hiddev_hid_event(hid, field, usage, value); + + return 1; +} + +static int quanta_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + struct quanta_data *td; + + td = kmalloc(sizeof(struct quanta_data), GFP_KERNEL); + if (!td) { + dev_err(&hdev->dev, "cannot allocate Quanta Touch data\n"); + return -ENOMEM; + } + td->valid = false; + td->activity = false; + td->activity_now = false; + td->first = false; + hid_set_drvdata(hdev, td); + + ret = hid_parse(hdev); + if (!ret) + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + + if (ret) + kfree(td); + + return ret; +} + +static void quanta_remove(struct hid_device *hdev) +{ + hid_hw_stop(hdev); + kfree(hid_get_drvdata(hdev)); + hid_set_drvdata(hdev, NULL); +} + +static const struct hid_device_id quanta_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, + USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, + { } +}; +MODULE_DEVICE_TABLE(hid, quanta_devices); + +static const struct hid_usage_id quanta_grabbed_usages[] = { + { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID }, + { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1} +}; + +static struct hid_driver quanta_driver = { + .name = "quanta-touch", + .id_table = quanta_devices, + .probe = quanta_probe, + .remove = quanta_remove, + .input_mapping = quanta_input_mapping, + .input_mapped = quanta_input_mapped, + .usage_table = quanta_grabbed_usages, + .event = quanta_event, +}; + +static int __init quanta_init(void) +{ + return hid_register_driver(&quanta_driver); +} + +static void __exit quanta_exit(void) +{ + hid_unregister_driver(&quanta_driver); +} + +module_init(quanta_init); +module_exit(quanta_exit); + From 4bb9508bbbb06f10bc3e249dd34375b4a4d6bfc0 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 23 Dec 2009 14:13:46 +0100 Subject: [PATCH 11/41] HID: remove TENX iBuddy from blacklist There were multiple reports which indicate that vendor messed up horribly and the same VID/PID combination is used for completely different devices, some of them requiring the blacklist entry and other not. Remove the blacklist entry for this combination of VID/PID completely, and let the user decide and unbind the driver via sysfs eventually, if needed. Proper fix would be fixing the vendor. References: http://lkml.org/lkml/2009/2/10/434 http://bugzilla.kernel.org/show_bug.cgi?id=13411 Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 2 -- drivers/hid/hid-ids.h | 4 ---- 2 files changed, 6 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 056384cf05e4..116a3460e75a 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1662,8 +1662,6 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) }, { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE) }, { HID_USB_DEVICE(USB_VENDOR_ID_POWERCOM, USB_DEVICE_ID_POWERCOM_UPS) }, - { HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY1) }, - { HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY2) }, { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) }, { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) }, { HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index b009fd68deae..064c09a221ef 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -406,10 +406,6 @@ #define USB_VENDOR_ID_SUNPLUS 0x04fc #define USB_DEVICE_ID_SUNPLUS_WDESKTOP 0x05d8 -#define USB_VENDOR_ID_TENX 0x1130 -#define USB_DEVICE_ID_TENX_IBUDDY1 0x0001 -#define USB_DEVICE_ID_TENX_IBUDDY2 0x0002 - #define USB_VENDOR_ID_THRUSTMASTER 0x044f #define USB_VENDOR_ID_TOPMAX 0x0663 From 62e62da856dba2edb897b672cbd05a69edd4485c Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Thu, 14 Jan 2010 19:10:07 -0700 Subject: [PATCH 12/41] HID: hid-debug.c: make local symbols static hid-debug.c: make local symbols static The symbols hid_resolv_event and hid_dump_input_mapping are only used locally in this file. Make them static to prevent the following sparse warnings: warning: symbol 'hid_resolv_event' was not declared. Should it be static? warning: symbol 'hid_dump_input_mapping' was not declared. Should it be static? Signed-off-by: H Hartley Sweeten Signed-off-by: Jiri Kosina --- drivers/hid/hid-debug.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 6abd0369aedb..cd4ece6fdfb9 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -864,13 +864,13 @@ static const char **names[EV_MAX + 1] = { [EV_SND] = sounds, [EV_REP] = repeats, }; -void hid_resolv_event(__u8 type, __u16 code, struct seq_file *f) { - +static void hid_resolv_event(__u8 type, __u16 code, struct seq_file *f) +{ seq_printf(f, "%s.%s", events[type] ? events[type] : "?", names[type] ? (names[type][code] ? names[type][code] : "?") : "?"); } -void hid_dump_input_mapping(struct hid_device *hid, struct seq_file *f) +static void hid_dump_input_mapping(struct hid_device *hid, struct seq_file *f) { int i, j, k; struct hid_report *report; From 8b0e58a70a7a41443c779de074288035b014cb94 Mon Sep 17 00:00:00 2001 From: Stephane Chatty Date: Wed, 13 Jan 2010 21:52:34 +0100 Subject: [PATCH 13/41] HID: let hid-input accept digitizers Extended IS_INPUT_APPLICATION to accept digitzers that are actual input devices (touchscreens, light pens, touch pads, white boards) Signed-off-by: Stephane Chatty Signed-off-by: Jiri Kosina --- include/linux/hid.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/hid.h b/include/linux/hid.h index 87093652dda8..b978c1e2e74d 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -663,7 +663,7 @@ struct hid_ll_driver { /* Applications from HID Usage Tables 4/8/99 Version 1.1 */ /* We ignore a few input applications that are not widely used */ -#define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001) || (a == 0x000d0002)) +#define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001) || ((a >= 0x000d0002) && (a <= 0x000d0006))) /* HID core API */ From cd9ec30da58bcd8ab154eba9eb54d16c67e7ef3b Mon Sep 17 00:00:00 2001 From: Johnathon Harris Date: Thu, 21 Jan 2010 14:36:52 +0000 Subject: [PATCH 14/41] HID: add support for Ortek WKB-2000 This patch adds a new USB HID driver for the Ortek WKB-2000, working around an incorrect LogicalMaximum value in the USB resource descriptor. Tracked by http://bugzilla.kernel.org/show_bug.cgi?id=14787 Bug originally reported by Ubuntu users: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/405390 Signed-off-by: Johnathon Harris Tested-by: Daniel J Blueman Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 7 ++++++ drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 3 +++ drivers/hid/hid-ortek.c | 56 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 68 insertions(+) create mode 100644 drivers/hid/hid-ortek.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 38e969207636..139668d6382a 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -218,6 +218,13 @@ config HID_NTRIG ---help--- Support for N-Trig touch screen. +config HID_ORTEK + tristate "Ortek" if EMBEDDED + depends on USB_HID + default !EMBEDDED + ---help--- + Support for Ortek WKB-2000 wireless keyboard + mouse trackpad. + config HID_PANTHERLORD tristate "Pantherlord support" if EMBEDDED depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 15541c47b172..b62d4b3afdc2 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o +obj-$(CONFIG_HID_ORTEK) += hid-ortek.o obj-$(CONFIG_HID_QUANTA) += hid-quanta.o obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 116a3460e75a..7fe509879fe3 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1337,6 +1337,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) }, { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) }, { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) }, { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 064c09a221ef..6c38359385a9 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -363,6 +363,9 @@ #define USB_VENDOR_ID_ONTRAK 0x0a07 #define USB_DEVICE_ID_ONTRAK_ADU100 0x0064 +#define USB_VENDOR_ID_ORTEK 0x05a4 +#define USB_DEVICE_ID_ORTEK_WKB2000 0x2000 + #define USB_VENDOR_ID_PANJIT 0x134c #define USB_VENDOR_ID_PANTHERLORD 0x0810 diff --git a/drivers/hid/hid-ortek.c b/drivers/hid/hid-ortek.c new file mode 100644 index 000000000000..aa9a960f73a4 --- /dev/null +++ b/drivers/hid/hid-ortek.c @@ -0,0 +1,56 @@ +/* + * HID driver for Ortek WKB-2000 (wireless keyboard + mouse trackpad). + * Fixes LogicalMaximum error in USB report description, see + * http://bugzilla.kernel.org/show_bug.cgi?id=14787 + * + * Copyright (c) 2010 Johnathon Harris + */ + +/* + * 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 +#include +#include + +#include "hid-ids.h" + +static void ortek_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int rsize) +{ + if (rsize >= 56 && rdesc[54] == 0x25 && rdesc[55] == 0x01) { + dev_info(&hdev->dev, "Fixing up Ortek WKB-2000 " + "report descriptor.\n"); + rdesc[55] = 0x92; + } +} + +static const struct hid_device_id ortek_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) }, + { } +}; +MODULE_DEVICE_TABLE(hid, ortek_devices); + +static struct hid_driver ortek_driver = { + .name = "ortek", + .id_table = ortek_devices, + .report_fixup = ortek_report_fixup +}; + +static int __init ortek_init(void) +{ + return hid_register_driver(&ortek_driver); +} + +static void __exit ortek_exit(void) +{ + hid_unregister_driver(&ortek_driver); +} + +module_init(ortek_init); +module_exit(ortek_exit); +MODULE_LICENSE("GPL"); From 7d39e849912f0c3c8c6fc94be7bf7d120b1ee0ba Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Tue, 2 Feb 2010 20:46:34 +0100 Subject: [PATCH 15/41] HID: update copyright Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 2 +- drivers/hid/hid-input.c | 2 +- drivers/hid/usbhid/hid-core.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 7fe509879fe3..1bbd96d4efe4 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -4,7 +4,7 @@ * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000-2005 Vojtech Pavlik * Copyright (c) 2005 Michael Haboustak for Concept2, Inc - * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2006-2010 Jiri Kosina */ /* diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index dad7aae9c975..8430d626511c 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2000-2001 Vojtech Pavlik - * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2006-2010 Jiri Kosina * * HID to Linux Input mapping */ diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index e72751d6e3e7..eb7e0019891f 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -5,7 +5,7 @@ * Copyright (c) 2000-2005 Vojtech Pavlik * Copyright (c) 2005 Michael Haboustak for Concept2, Inc * Copyright (c) 2007-2008 Oliver Neukum - * Copyright (c) 2006-2009 Jiri Kosina + * Copyright (c) 2006-2010 Jiri Kosina */ /* From 1c3a02c215a5b955b342f29dc1719e1a5771eaf1 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 2 Feb 2010 18:43:32 +0200 Subject: [PATCH 16/41] HID: add NOGET quirk for Prodige Cordless Combo I happen to own a keyboard identified as 05af:3062 which is labeled as "FlatX Coldless Combo" by "Prodige", which exhibits input problems without NOGET quirk. For some reason, lsusb reports this device as "Jing-Mold Enterprise Co., Ltd", which is not mentioned anywhere on the package. A quick search on the intenet shows that there a other people who have this in their lsusb output, but apparently they don't have the problem I am seeing (or they are not such furious typists as myself). Signed-off-by: Alexander Shishkin Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 3 +++ drivers/hid/usbhid/hid-quirks.c | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 6c38359385a9..4e5adc3fac4a 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -383,6 +383,9 @@ #define USB_VENDOR_ID_POWERCOM 0x0d9f #define USB_DEVICE_ID_POWERCOM_UPS 0x0002 +#define USB_VENDOR_ID_PRODIGE 0x05af +#define USB_DEVICE_ID_PRODIGE_CORDLESS 0x3062 + #define USB_VENDOR_ID_SAITEK 0x06a3 #define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 38773dc2821b..fc074c11e0d3 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -45,6 +45,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, { USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET }, From 8127f4e883666c9960cfa89cffd36313748f8bab Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Tue, 2 Feb 2010 18:09:06 -0200 Subject: [PATCH 17/41] HID: use multi input quirk for eTurboTouch touchscreen This device generates ABS_Z and ABS_RX events, while it should be generating ABS_X and ABS_Y instead. Using the MULTI_INPUT quirk solves this issue. Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Daniel Oliveira Nascimento Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 3 +++ drivers/hid/usbhid/hid-quirks.c | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 4e5adc3fac4a..bb5f4fa479ad 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -169,6 +169,9 @@ #define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f #define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100 +#define USB_VENDOR_ID_ETURBOTOUCH 0x22b9 +#define USB_DEVICE_ID_ETURBOTOUCH 0x0006 + #define USB_VENDOR_ID_EZKEY 0x0518 #define USB_DEVICE_ID_BTC_8193 0x0002 diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index fc074c11e0d3..88a1c693fdcc 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -43,6 +43,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_AFATECH, USB_DEVICE_ID_AFATECH_AF9016, HID_QUIRK_FULLSPEED_INTERVAL }, + { USB_VENDOR_ID_ETURBOTOUCH, USB_DEVICE_ID_ETURBOTOUCH, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, { USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET }, From f54405db66fbec11679241daefd16fd8291a5762 Mon Sep 17 00:00:00 2001 From: Alex Neblett Date: Tue, 2 Feb 2010 21:16:03 -0800 Subject: [PATCH 18/41] HID: add support for Pixart Imaging Optical Touch Screen Added support for the Pixart Imaging Inc. Optical Touch Screen found in the MSI AE2220 and other new all in one computers to the Quanta Optical Touch dual-touch panel driver found in the latest git clone git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git. Signed-off-by: Alex Neblett Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-quanta.c | 2 ++ 3 files changed, 4 insertions(+) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 2dd9b28e39c4..282f4a1e7214 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1343,6 +1343,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) }, { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, { 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_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, { HID_USB_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) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 39ff98a5bd61..b29d9da799f4 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -400,6 +400,7 @@ #define USB_VENDOR_ID_QUANTA 0x0408 #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH 0x3000 +#define USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN 0x3001 #define USB_VENDOR_ID_SAMSUNG 0x0419 #define USB_DEVICE_ID_SAMSUNG_IR_REMOTE 0x0001 diff --git a/drivers/hid/hid-quanta.c b/drivers/hid/hid-quanta.c index 244d61c18a47..9ac49287b943 100644 --- a/drivers/hid/hid-quanta.c +++ b/drivers/hid/hid-quanta.c @@ -224,6 +224,8 @@ static void quanta_remove(struct hid_device *hdev) static const struct hid_device_id quanta_devices[] = { { 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) }, { } }; MODULE_DEVICE_TABLE(hid, quanta_devices); From d4bfa033ed84e0ae446eff445d107ffd5ee78df3 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Fri, 29 Jan 2010 15:03:36 +0100 Subject: [PATCH 19/41] HID: make raw reports possible for both feature and output reports In commit 2da31939a42 ("Bluetooth: Implement raw output support for HIDP layer"), support for Bluetooth hid_output_raw_report was added, but it pushes the data to the intr socket instead of the ctrl one. This has been fixed by 6bf8268f9a91f1 ("Bluetooth: Use the control channel for raw HID reports") Still, it is necessary to distinguish whether the report in question should be either FEATURE or OUTPUT. For this, we have to extend the generic HID API, so that hid_output_raw_report() callback provides means to specify this value so that it can be passed down to lower level hardware drivers (currently Bluetooth and USB). Based on original patch by Bastien Nocera Acked-by: Marcel Holtmann Signed-off-by: Jiri Kosina --- drivers/hid/hidraw.c | 2 +- drivers/hid/usbhid/hid-core.c | 5 +++-- include/linux/hid.h | 2 +- net/bluetooth/hidp/core.c | 17 ++++++++++++++--- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index cdd136942bca..d04476700b7b 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -134,7 +134,7 @@ static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t goto out; } - ret = dev->hid_output_raw_report(dev, buf, count); + ret = dev->hid_output_raw_report(dev, buf, count, HID_OUTPUT_REPORT); out: kfree(buf); return ret; diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index e2997a8d5e1b..caa16c057ce2 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -774,7 +774,8 @@ static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid) return 0; } -static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count) +static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count, + unsigned char report_type) { struct usbhid_device *usbhid = hid->driver_data; struct usb_device *dev = hid_to_usb_dev(hid); @@ -785,7 +786,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, - ((HID_OUTPUT_REPORT + 1) << 8) | *buf, + ((report_type + 1) << 8) | *buf, interface->desc.bInterfaceNumber, buf + 1, count - 1, USB_CTRL_SET_TIMEOUT); diff --git a/include/linux/hid.h b/include/linux/hid.h index 87093652dda8..3661a626941d 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -501,7 +501,7 @@ struct hid_device { /* device report descriptor */ void (*hiddev_report_event) (struct hid_device *, struct hid_report *); /* handler for raw output data, used by hidraw */ - int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t); + int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t, unsigned char); /* debugging support via debugfs */ unsigned short debug; diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 6cf526d06e21..37ba153c4cd4 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -313,10 +313,21 @@ static int hidp_send_report(struct hidp_session *session, struct hid_report *rep return hidp_queue_report(session, buf, rsize); } -static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count) +static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count, + unsigned char report_type) { - if (hidp_send_ctrl_message(hid->driver_data, - HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE, + switch (report_type) { + case HID_FEATURE_REPORT: + report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE; + break; + case HID_OUTPUT_REPORT: + report_type = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT; + break; + default: + return -EINVAL; + } + + if (hidp_send_ctrl_message(hid->driver_data, report_type, data, count)) return -ENOMEM; return count; From 46a709b900bfcf43244cd19cf3245c77484ec733 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Wed, 20 Jan 2010 12:00:53 +0000 Subject: [PATCH 20/41] HID: Implement Wacom quirk in the kernel The hid-wacom driver required user-space to poke at the tablet to make it send data about the cursor location. This patch makes it do the same thing but in the kernel. Signed-off-by: Bastien Nocera Acked-by: Marcel Holtmann Signed-off-by: Jiri Kosina --- drivers/hid/hid-wacom.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c index 12dcda529201..b8778db720bc 100644 --- a/drivers/hid/hid-wacom.c +++ b/drivers/hid/hid-wacom.c @@ -156,7 +156,9 @@ static int wacom_probe(struct hid_device *hdev, struct hid_input *hidinput; struct input_dev *input; struct wacom_data *wdata; + char rep_data[2]; int ret; + int limit; wdata = kzalloc(sizeof(*wdata), GFP_KERNEL); if (wdata == NULL) { @@ -166,6 +168,7 @@ static int wacom_probe(struct hid_device *hdev, hid_set_drvdata(hdev, wdata); + /* Parse the HID report now */ ret = hid_parse(hdev); if (ret) { dev_err(&hdev->dev, "parse failed\n"); @@ -178,6 +181,30 @@ static int wacom_probe(struct hid_device *hdev, goto err_free; } + /* Set Wacom mode2 */ + rep_data[0] = 0x03; rep_data[1] = 0x00; + limit = 3; + do { + ret = hdev->hid_output_raw_report(hdev, rep_data, 2, + HID_FEATURE_REPORT); + } while (ret < 0 && limit-- > 0); + if (ret < 0) { + dev_err(&hdev->dev, "failed to poke device #1, %d\n", ret); + goto err_free; + } + + /* 0x06 - high reporting speed, 0x05 - low speed */ + rep_data[0] = 0x06; rep_data[1] = 0x00; + limit = 3; + do { + ret = hdev->hid_output_raw_report(hdev, rep_data, 2, + HID_FEATURE_REPORT); + } while (ret < 0 && limit-- > 0); + if (ret < 0) { + dev_err(&hdev->dev, "failed to poke device #2, %d\n", ret); + goto err_free; + } + hidinput = list_entry(hdev->inputs.next, struct hid_input, list); input = hidinput->input; From f9ce7c283c16538955d5d094101889792bcde109 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Wed, 20 Jan 2010 12:01:53 +0000 Subject: [PATCH 21/41] HID: Enable Sixaxis controller over Bluetooth Now that hid_output_raw_report works, port the PS3 Sixaxis Bluetooth quirk from user-space, into kernel-space. Signed-off-by: Bastien Nocera Acked-by: Marcel Holtmann Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 1 + drivers/hid/hid-sony.c | 21 +++++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index eabe5f87c6c1..f7f80e1f1eef 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1340,6 +1340,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, { 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_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) }, diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 4e8450228a24..6ced140b1411 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -48,7 +48,7 @@ static void sony_report_fixup(struct hid_device *hdev, __u8 *rdesc, * to "operational". Without this, the ps3 controller will not report any * events. */ -static int sony_set_operational(struct hid_device *hdev) +static int sony_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); @@ -73,6 +73,12 @@ static int sony_set_operational(struct hid_device *hdev) return ret; } +static int sony_set_operational_bt(struct hid_device *hdev) +{ + unsigned char buf[] = { 0x53, 0xf4, 0x42, 0x03, 0x00, 0x00 }; + return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); +} + static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret; @@ -101,7 +107,17 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_free; } - ret = sony_set_operational(hdev); + 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 (ret < 0) goto err_stop; @@ -121,6 +137,7 @@ 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_VAIO_VGX_MOUSE), .driver_data = VAIO_RDESC_CONSTANT }, { } From 342f31e84eec9002b75f6fcdec6bd932ac77a390 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 3 Feb 2010 15:52:31 +0100 Subject: [PATCH 22/41] HID: make Wacom modesetting failures non-fatal With Wacom tablet mode-setting moved from userspace into kernel, we don't have to consider failures of device queries through the _raw callback as hard failure, as the driver can safely continue anyway. This is consistent with the current USB driver in wacom_sys.c Reported-by: Ping Cheng Signed-off-by: Jiri Kosina --- drivers/hid/hid-wacom.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c index b8778db720bc..8d3b46f5d149 100644 --- a/drivers/hid/hid-wacom.c +++ b/drivers/hid/hid-wacom.c @@ -181,6 +181,11 @@ static int wacom_probe(struct hid_device *hdev, goto err_free; } + /* + * Note that if the raw queries fail, it's not a hard failure and it + * is safe to continue + */ + /* Set Wacom mode2 */ rep_data[0] = 0x03; rep_data[1] = 0x00; limit = 3; @@ -188,10 +193,8 @@ static int wacom_probe(struct hid_device *hdev, ret = hdev->hid_output_raw_report(hdev, rep_data, 2, HID_FEATURE_REPORT); } while (ret < 0 && limit-- > 0); - if (ret < 0) { - dev_err(&hdev->dev, "failed to poke device #1, %d\n", ret); - goto err_free; - } + if (ret < 0) + dev_warn(&hdev->dev, "failed to poke device #1, %d\n", ret); /* 0x06 - high reporting speed, 0x05 - low speed */ rep_data[0] = 0x06; rep_data[1] = 0x00; @@ -200,10 +203,8 @@ static int wacom_probe(struct hid_device *hdev, ret = hdev->hid_output_raw_report(hdev, rep_data, 2, HID_FEATURE_REPORT); } while (ret < 0 && limit-- > 0); - if (ret < 0) { - dev_err(&hdev->dev, "failed to poke device #2, %d\n", ret); - goto err_free; - } + if (ret < 0) + dev_warn(&hdev->dev, "failed to poke device #2, %d\n", ret); hidinput = list_entry(hdev->inputs.next, struct hid_input, list); input = hidinput->input; From 2dbf209d7a7ab94266b936bd2da6a4026c279992 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 3 Feb 2010 16:11:12 +0100 Subject: [PATCH 23/41] HID: make full-fledged hid-bus drivers properly selectable For historical reasons, we don't have most of the in-tree drivers residing on hid-bus properly selectable in kernel configuration unless CONFIG_EMBEDDED is set. This has been introduced on Linus' request from 14 Oct === As to the Kconfig options - do they really add so much space that you need to ask for the quirks? You didn't use to. Can you make the questions depend on EMBEDDED, or at least on the HID_COMPAT thing or whatever? === This still makes perfect sense for small and tiny drivers, which just fix report descriptors, fix up HID->input mappings that slightly violates HUT standard, send one extra packet to the device that is needed before it becomes functional, etc. Since then, we have been gathering more and more HID-bus drivers, which are full-fledged drivers. For these, the size argument becomes more valid. Plus the devices are much more special than "just violates HID specification in this one or two tiny unimportant points". Therefore I am marking such drivers as properly selectable no matter the setting of CONFIG_EMBEDDED, while keeping all the small and tiny ones compiled by default. Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 139668d6382a..617ed7db67d1 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -212,9 +212,8 @@ config HID_MONTEREY Support for Monterey Genius KB29E. config HID_NTRIG - tristate "NTrig" if EMBEDDED + tristate "NTrig" depends on USB_HID - default !EMBEDDED ---help--- Support for N-Trig touch screen. @@ -338,9 +337,8 @@ config THRUSTMASTER_FF Rumble Force or Force Feedback Wheel. config HID_WACOM - tristate "Wacom Bluetooth devices support" if EMBEDDED + tristate "Wacom Bluetooth devices support" depends on BT_HIDP - default !EMBEDDED ---help--- Support for Wacom Graphire Bluetooth tablet. From c2c3489c5b0fdb8fbf0f5e9424905c2994ab5660 Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Thu, 4 Feb 2010 13:36:24 -0200 Subject: [PATCH 24/41] HID: use multi input quirk for TouchPack touchscreen This device generates ABS_Z and ABS_RX events, while it should be generating ABS_X and ABS_Y instead. Using the MULTI_INPUT quirk solves this issue. Signed-off-by: Thadeu Lima de Souza Cascardo Signed-off-by: Daniel Oliveira Nascimento [jkosina@suse.cz: fixed blacklist ordering while resolving conflict] [jkosina@suse.cz: fixed typo to make it compile] Signed-off-by: Jiri Kosina --- drivers/hid/hid-ids.h | 3 +++ drivers/hid/usbhid/hid-quirks.c | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index b29d9da799f4..282bfe8d95e8 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -424,6 +424,9 @@ #define USB_VENDOR_ID_THRUSTMASTER 0x044f +#define USB_VENDOR_ID_TOUCHPACK 0x1bfd +#define USB_DEVICE_ID_TOUCHPACK_RTS 0x1688 + #define USB_VENDOR_ID_TOPMAX 0x0663 #define USB_DEVICE_ID_TOPMAX_COBRAPAD 0x0103 diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 88a1c693fdcc..7844280897d1 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -46,7 +46,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_ETURBOTOUCH, USB_DEVICE_ID_ETURBOTOUCH, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, { USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT }, - { USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_TOUCHPACK, USB_DEVICE_ID_TOUCHPACK_RTS, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET }, @@ -59,6 +59,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK, 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 }, { 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 }, From 0690535d6bcec5eb28573824df5c8a49ec85b696 Mon Sep 17 00:00:00 2001 From: Leo P White Date: Mon, 8 Feb 2010 13:02:05 +0000 Subject: [PATCH 25/41] HID: add mapping for "AL Network Chat" usage Adding a mapping for the 'AL Network Chat' usage from the 'Consumer' usage page (USB HID Usage Tables v1.11). This usage is used by some keyboards for a multimedia key. Signed-off-by: Leo P White Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 8430d626511c..a01a1b0de1c3 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -405,6 +405,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x192: map_key_clear(KEY_CALC); break; case 0x194: map_key_clear(KEY_FILE); break; case 0x196: map_key_clear(KEY_WWW); break; + case 0x199: map_key_clear(KEY_CHAT); break; case 0x19c: map_key_clear(KEY_LOGOFF); break; case 0x19e: map_key_clear(KEY_COFFEE); break; case 0x1a6: map_key_clear(KEY_HELP); break; From eabe5c90580a065aed8ce6a5ba53eb443d317fae Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Tue, 9 Feb 2010 11:43:19 +0100 Subject: [PATCH 26/41] HID: fix typo in error message Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 4e8450228a24..96d723ea5131 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -81,7 +81,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) sc = kzalloc(sizeof(*sc), GFP_KERNEL); if (sc == NULL) { - dev_err(&hdev->dev, "can't alloc apple descriptor\n"); + dev_err(&hdev->dev, "can't alloc sony descriptor\n"); return -ENOMEM; } From b32758c7216f337044ceb6dcaa754b8eda95a59f Mon Sep 17 00:00:00 2001 From: Stephane Chatty Date: Wed, 10 Feb 2010 12:09:17 +0100 Subject: [PATCH 27/41] HID: fixed bug in single-touch emulation on the stantum panel Fixed stupid copy-paste bug in touchscreen emulation for the Stantum multitouch panel: a flag was reset just before being tested. Signed-off-by: Stephane Chatty Signed-off-by: Jiri Kosina --- drivers/hid/hid-stantum.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/hid-stantum.c b/drivers/hid/hid-stantum.c index add965dab932..1b7b1659e7bb 100644 --- a/drivers/hid/hid-stantum.c +++ b/drivers/hid/hid-stantum.c @@ -147,7 +147,6 @@ static void stantum_filter_event(struct stantum_data *sd, input_mt_sync(input); sd->valid = false; - sd->first = false; /* touchscreen emulation */ if (sd->first) { @@ -158,6 +157,7 @@ static void stantum_filter_event(struct stantum_data *sd, input_event(input, EV_ABS, ABS_X, sd->x); input_event(input, EV_ABS, ABS_Y, sd->y); } + sd->first = false; } From 580363db92572cccbe6226bf83321e50a9ea50ea Mon Sep 17 00:00:00 2001 From: Stephane Chatty Date: Sat, 6 Feb 2010 15:20:03 +0100 Subject: [PATCH 28/41] HID: add pressure support for the Stantum multitouch panel Added pressure handling for Stantum multitouch panels Signed-off-by: Stephane Chatty Signed-off-by: Jiri Kosina --- drivers/hid/hid-stantum.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/hid/hid-stantum.c b/drivers/hid/hid-stantum.c index 1b7b1659e7bb..04b0b986f5f4 100644 --- a/drivers/hid/hid-stantum.c +++ b/drivers/hid/hid-stantum.c @@ -66,7 +66,6 @@ static int stantum_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_DG_DEVICEINDEX: case HID_DG_CONTACTCOUNT: case HID_DG_CONTACTMAX: - case HID_DG_TIPPRESSURE: return -1; case HID_DG_TIPSWITCH: @@ -84,6 +83,11 @@ static int stantum_input_mapping(struct hid_device *hdev, struct hid_input *hi, input_set_abs_params(hi->input, ABS_MT_ORIENTATION, 1, 1, 0, 0); return 1; + case HID_DG_TIPPRESSURE: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_PRESSURE); + return 1; + case HID_DG_CONTACTID: hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_TRACKING_ID); @@ -140,10 +144,7 @@ static void stantum_filter_event(struct stantum_data *sd, input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, wide ? sd->w : sd->h); input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, wide ? sd->h : sd->w); -#if 0 - /* MT_PRESSURE does not exist yet */ input_event(input, EV_ABS, ABS_MT_PRESSURE, sd->z); -#endif input_mt_sync(input); sd->valid = false; From 77f720b71d88a3cbf574c113566a31c93099f97d Mon Sep 17 00:00:00 2001 From: Stephane Chatty Date: Sat, 6 Feb 2010 15:17:13 +0100 Subject: [PATCH 29/41] HID: Support for MosArt multitouch panel Added support for MosArt dual-touch panels, present in the Asus T91MT notebook. Signed-off-by: Stephane Chatty Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 6 + drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 5 +- drivers/hid/hid-ids.h | 9 +- drivers/hid/hid-mosart.c | 274 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 290 insertions(+), 5 deletions(-) create mode 100644 drivers/hid/hid-mosart.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 617ed7db67d1..2b93d7f647ee 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -204,6 +204,12 @@ config HID_MICROSOFT ---help--- Support for Microsoft devices that are not fully compliant with HID standard. +config HID_MOSART + tristate "MosArt" + depends on USB_HID + ---help--- + Support for MosArt dual-touch panels. + config HID_MONTEREY tristate "Monterey" if EMBEDDED depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index b62d4b3afdc2..03051694d546 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_HID_KYE) += hid-kye.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o +obj-$(CONFIG_HID_MOSART) += hid-mosart.o obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o obj-$(CONFIG_HID_ORTEK) += hid-ortek.o obj-$(CONFIG_HID_QUANTA) += hid-quanta.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 282f4a1e7214..da97195474ff 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1550,8 +1550,9 @@ 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_LCM)}, - { HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_LCM2)}, + { 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) }, { HID_USB_DEVICE(USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD) }, { HID_USB_DEVICE(USB_VENDOR_ID_CIDC, 0x0103) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 282bfe8d95e8..ab370f1c4b6e 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -99,9 +99,12 @@ #define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241 #define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242 -#define USB_VENDOR_ID_ASUS 0x0b05 -#define USB_DEVICE_ID_ASUS_LCM 0x1726 -#define USB_DEVICE_ID_ASUS_LCM2 0x175b +#define USB_VENDOR_ID_ASUS 0x0486 +#define USB_DEVICE_ID_ASUS_T91MT 0x0185 + +#define USB_VENDOR_ID_ASUSTEK 0x0b05 +#define USB_DEVICE_ID_ASUSTEK_LCM 0x1726 +#define USB_DEVICE_ID_ASUSTEK_LCM2 0x175b #define USB_VENDOR_ID_ATEN 0x0557 #define USB_DEVICE_ID_ATEN_UC100KM 0x2004 diff --git a/drivers/hid/hid-mosart.c b/drivers/hid/hid-mosart.c new file mode 100644 index 000000000000..f6ec4f150859 --- /dev/null +++ b/drivers/hid/hid-mosart.c @@ -0,0 +1,274 @@ +/* + * HID driver for the multitouch panel on the ASUS EeePC T91MT + * + * Copyright (c) 2009-2010 Stephane Chatty + * Copyright (c) 2010 Teemu Tuominen + * + */ + +/* + * 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 +#include +#include +#include +#include "usbhid/usbhid.h" + +MODULE_VERSION("1.00"); +MODULE_AUTHOR("Stephane Chatty "); +MODULE_DESCRIPTION("MosArt dual-touch panel"); +MODULE_LICENSE("GPL"); + +#include "hid-ids.h" + +struct mosart_data { + __u16 x, y; + __u8 id; + bool valid; /* valid finger data, or just placeholder? */ + bool first; /* is this the first finger in this frame? */ + bool activity_now; /* at least one active finger in this frame? */ + bool activity; /* at least one active finger previously? */ +}; + +static int mosart_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + switch (usage->hid & HID_USAGE_PAGE) { + + case HID_UP_GENDESK: + switch (usage->hid) { + case HID_GD_X: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_POSITION_X); + /* touchscreen emulation */ + input_set_abs_params(hi->input, ABS_X, + field->logical_minimum, + field->logical_maximum, 0, 0); + return 1; + case HID_GD_Y: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_POSITION_Y); + /* touchscreen emulation */ + input_set_abs_params(hi->input, ABS_Y, + field->logical_minimum, + field->logical_maximum, 0, 0); + return 1; + } + return 0; + + case HID_UP_DIGITIZER: + switch (usage->hid) { + case HID_DG_CONFIDENCE: + case HID_DG_TIPSWITCH: + case HID_DG_INPUTMODE: + case HID_DG_DEVICEINDEX: + case HID_DG_CONTACTCOUNT: + case HID_DG_CONTACTMAX: + case HID_DG_TIPPRESSURE: + case HID_DG_WIDTH: + case HID_DG_HEIGHT: + return -1; + case HID_DG_INRANGE: + /* touchscreen emulation */ + hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); + return 1; + + case HID_DG_CONTACTID: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_TRACKING_ID); + return 1; + + } + return 0; + + case 0xff000000: + /* ignore HID features */ + return -1; + } + + return 0; +} + +static int mosart_input_mapped(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if (usage->type == EV_KEY || usage->type == EV_ABS) + clear_bit(usage->code, *bit); + + return 0; +} + +/* + * this function is called when a whole finger has been parsed, + * so that it can decide what to send to the input layer. + */ +static void mosart_filter_event(struct mosart_data *td, struct input_dev *input) +{ + td->first = !td->first; /* touchscreen emulation */ + + if (!td->valid) { + /* + * touchscreen emulation: if no finger in this frame is valid + * and there previously was finger activity, this is a release + */ + if (!td->first && !td->activity_now && td->activity) { + input_event(input, EV_KEY, BTN_TOUCH, 0); + td->activity = false; + } + return; + } + + 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_mt_sync(input); + td->valid = false; + + /* touchscreen emulation: if first active finger in this frame... */ + if (!td->activity_now) { + /* if there was no previous activity, emit touch event */ + if (!td->activity) { + input_event(input, EV_KEY, BTN_TOUCH, 1); + td->activity = true; + } + td->activity_now = true; + /* and in any case this is our preferred finger */ + input_event(input, EV_ABS, ABS_X, td->x); + input_event(input, EV_ABS, ABS_Y, td->y); + } +} + + +static int mosart_event(struct hid_device *hid, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct mosart_data *td = hid_get_drvdata(hid); + + if (hid->claimed & HID_CLAIMED_INPUT) { + struct input_dev *input = field->hidinput->input; + switch (usage->hid) { + case HID_DG_INRANGE: + td->valid = !!value; + break; + case HID_GD_X: + td->x = value; + break; + case HID_GD_Y: + td->y = value; + mosart_filter_event(td, input); + break; + case HID_DG_CONTACTID: + td->id = value; + break; + case HID_DG_CONTACTCOUNT: + /* touch emulation: this is the last field in a frame */ + td->first = false; + td->activity_now = false; + break; + case HID_DG_CONFIDENCE: + case HID_DG_TIPSWITCH: + /* avoid interference from generic hidinput handling */ + break; + + default: + /* fallback to the generic hidinput handling */ + return 0; + } + } + + /* we have handled the hidinput part, now remains hiddev */ + if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event) + hid->hiddev_hid_event(hid, field, usage, value); + + return 1; +} + +static int mosart_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + struct mosart_data *td; + + + td = kmalloc(sizeof(struct mosart_data), GFP_KERNEL); + if (!td) { + dev_err(&hdev->dev, "cannot allocate MosArt data\n"); + return -ENOMEM; + } + td->valid = false; + td->activity = false; + td->activity_now = false; + td->first = false; + hid_set_drvdata(hdev, td); + + /* currently, it's better to have one evdev device only */ +#if 0 + hdev->quirks |= HID_QUIRK_MULTI_INPUT; +#endif + + ret = hid_parse(hdev); + if (ret == 0) + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + + if (ret == 0) { + struct hid_report_enum *re = hdev->report_enum + + HID_FEATURE_REPORT; + struct hid_report *r = re->report_id_hash[7]; + + r->field[0]->value[0] = 0x02; + usbhid_submit_report(hdev, r, USB_DIR_OUT); + } else + kfree(td); + + return ret; +} + +static void mosart_remove(struct hid_device *hdev) +{ + hid_hw_stop(hdev); + kfree(hid_get_drvdata(hdev)); + hid_set_drvdata(hdev, NULL); +} + +static const struct hid_device_id mosart_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_T91MT) }, + { } +}; +MODULE_DEVICE_TABLE(hid, mosart_devices); + +static const struct hid_usage_id mosart_grabbed_usages[] = { + { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID }, + { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1} +}; + +static struct hid_driver mosart_driver = { + .name = "mosart", + .id_table = mosart_devices, + .probe = mosart_probe, + .remove = mosart_remove, + .input_mapping = mosart_input_mapping, + .input_mapped = mosart_input_mapped, + .usage_table = mosart_grabbed_usages, + .event = mosart_event, +}; + +static int __init mosart_init(void) +{ + return hid_register_driver(&mosart_driver); +} + +static void __exit mosart_exit(void) +{ + hid_unregister_driver(&mosart_driver); +} + +module_init(mosart_init); +module_exit(mosart_exit); + From 90a006abf8015c8cab893555244d8fc673b24839 Mon Sep 17 00:00:00 2001 From: Michael Poole Date: Sun, 24 Jan 2010 22:32:29 -0500 Subject: [PATCH 30/41] HID: Export hid_register_report The Apple Magic Mouse (and probably other devices) publish reports that are not called out in their HID report descriptors -- they only send them when enabled through other writes to the device. This allows a driver to handle these unlisted reports. Signed-off-by: Michael Poole Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 3 ++- include/linux/hid.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index f7f80e1f1eef..66a91eb3e4c4 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -51,7 +51,7 @@ EXPORT_SYMBOL_GPL(hid_debug); * Register a new report for a device. */ -static struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id) +struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id) { struct hid_report_enum *report_enum = device->report_enum + type; struct hid_report *report; @@ -75,6 +75,7 @@ static struct hid_report *hid_register_report(struct hid_device *device, unsigne return report; } +EXPORT_SYMBOL_GPL(hid_register_report); /* * Register a new field for this report. diff --git a/include/linux/hid.h b/include/linux/hid.h index 3661a626941d..456838c8884e 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -690,6 +690,7 @@ int hid_input_report(struct hid_device *, int type, u8 *, int, int); int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field); void hid_output_report(struct hid_report *report, __u8 *data); struct hid_device *hid_allocate_device(void); +struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id); int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size); int hid_check_keys_pressed(struct hid_device *hid); int hid_connect(struct hid_device *hid, unsigned int connect_mask); From 128537cea464d919febeaea2000e256749f317eb Mon Sep 17 00:00:00 2001 From: Michael Poole Date: Sat, 6 Feb 2010 12:24:36 -0500 Subject: [PATCH 31/41] HID: add a device driver for the Apple Magic Mouse. The Magic Mouse requires that a driver send an unlock Report(Feature) command, similar to the Wacom wireless tablet and Sixaxis controller quirks. This turns on an Input Report that isn't published in the input Report descriptor that contains touch data (and usually overrides the normal motion and click Report). Because the mouse has only one switch and no scroll wheel, the driver (under control of parameters) emulates a middle button and scroll wheel. User space could also ignore and/or re-synthesize those events based on the reported events. Some user-space tools to talk to the mouse directly (that is, when it is not associated with the host's HIDP stack) are at http://github.com/entrope/linux-magicmouse Signed-off-by: Michael Poole Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 10 + drivers/hid/Makefile | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-magicmouse.c | 469 +++++++++++++++++++++++++++++++++++ 5 files changed, 482 insertions(+) create mode 100644 drivers/hid/hid-magicmouse.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 24d90ea246ce..ba14ec898258 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -183,6 +183,16 @@ config LOGIRUMBLEPAD2_FF Say Y here if you want to enable force feedback support for Logitech Rumblepad 2 devices. +config HID_MAGICMOUSE + tristate "Apple" if EMBEDDED + depends on BT_HIDP + default !EMBEDDED + ---help--- + Support for the Apple Magic Mouse. + + Say Y here if you want support for the multi-touch features of the + Apple Wireless "Magic" Mouse. + config HID_MICROSOFT tristate "Microsoft" if EMBEDDED depends on USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 0de2dff5542c..45d81e9cd2de 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_HID_GYRATION) += hid-gyration.o obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o obj-$(CONFIG_HID_KYE) += hid-kye.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o +obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 66a91eb3e4c4..f038422f432c 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1254,6 +1254,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) }, { 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_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) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 010368e649ed..11e521864abb 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -56,6 +56,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_FOUNTAIN_ANSI 0x020e #define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f #define USB_DEVICE_ID_APPLE_GEYSER_ANSI 0x0214 diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c new file mode 100644 index 000000000000..f94b3e43c5b6 --- /dev/null +++ b/drivers/hid/hid-magicmouse.c @@ -0,0 +1,469 @@ +/* + * Apple "Magic" Wireless Mouse driver + * + * Copyright (c) 2010 Michael Poole + */ + +/* + * 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 +#include +#include +#include + +#include "hid-ids.h" + +static bool emulate_3button = 1; +module_param(emulate_3button, bool, 0644); +MODULE_PARM_DESC(emulate_3button, "Emulate a middle button"); + +static int middle_button_start = -350; +static int middle_button_stop = +350; + +static bool emulate_scroll_wheel = 1; +module_param(emulate_scroll_wheel, bool, 0644); +MODULE_PARM_DESC(emulate_scroll_wheel, "Emulate a scroll wheel"); + +static bool report_touches = 1; +module_param(report_touches, bool, 0644); +MODULE_PARM_DESC(report_touches, "Emit touch records (otherwise, only use them for emulation)"); + +static bool report_undeciphered = 0; +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 +/* 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, + * and 0x40 is actual contact, and 0x10 may be a start/stop or change + * indication.) + */ +#define TOUCH_STATE_MASK 0xf0 +#define TOUCH_STATE_NONE 0x00 +#define TOUCH_STATE_START 0x30 +#define TOUCH_STATE_DRAG 0x40 + +/** + * 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. + * @touches: Most recent data for a touch, indexed by tracking ID. + * @tracking_ids: Mapping of current touch input data to @touches. + */ +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; + + struct { + short x; + short y; + short scroll_y; + u8 size; + } touches[16]; + int tracking_ids[16]; +}; + +static int magicmouse_firm_touch(struct magicmouse_sc *msc) +{ + int touch = -1; + int ii; + + /* If there is only one "firm" touch, set touch to its + * tracking ID. + */ + for (ii = 0; ii < msc->ntouches; ii++) { + int idx = msc->tracking_ids[ii]; + if (msc->touches[idx].size < 8) { + /* Ignore this touch. */ + } else if (touch >= 0) { + touch = -1; + break; + } else { + touch = idx; + } + } + + return touch; +} + +static void magicmouse_emit_buttons(struct magicmouse_sc *msc, int state) +{ + int last_state = test_bit(BTN_LEFT, msc->input->key) << 0 | + test_bit(BTN_RIGHT, msc->input->key) << 1 | + test_bit(BTN_MIDDLE, msc->input->key) << 2; + + if (emulate_3button) { + int id; + + /* If some button was pressed before, keep it held + * down. Otherwise, if there's exactly one firm + * touch, use that to override the mouse's guess. + */ + if (state == 0) { + /* The button was released. */ + } else if (last_state != 0) { + state = last_state; + } else if ((id = magicmouse_firm_touch(msc)) >= 0) { + int x = msc->touches[id].x; + if (x < middle_button_start) + state = 1; + else if (x > middle_button_stop) + state = 2; + else + state = 4; + } /* else: we keep the mouse's guess */ + + input_report_key(msc->input, BTN_MIDDLE, state & 4); + } + + input_report_key(msc->input, BTN_LEFT, state & 1); + input_report_key(msc->input, BTN_RIGHT, state & 2); + + if (state != last_state) + msc->scroll_accel = 0; +} + +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); + + /* 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; + + /* If requested, emulate a scroll wheel by detecting small + * vertical touch motions along the middle of the mouse. + */ + if (emulate_scroll_wheel && + middle_button_start < x && x < middle_button_stop) { + static const int accel_profile[] = { + 256, 228, 192, 160, 128, 96, 64, 32, + }; + unsigned long now = jiffies; + int step = msc->touches[id].scroll_y - y; + + /* Reset acceleration after half a second. */ + if (time_after(now, msc->scroll_jiffies + HZ / 2)) + msc->scroll_accel = 0; + + /* Calculate and apply the scroll motion. */ + switch (tdata[7] & TOUCH_STATE_MASK) { + case TOUCH_STATE_START: + msc->touches[id].scroll_y = y; + msc->scroll_accel = min_t(int, msc->scroll_accel + 1, + ARRAY_SIZE(accel_profile) - 1); + break; + case TOUCH_STATE_DRAG: + step = step / accel_profile[msc->scroll_accel]; + if (step != 0) { + msc->touches[id].scroll_y = y; + msc->scroll_jiffies = now; + input_report_rel(input, REL_WHEEL, step); + } + break; + } + } + + /* Generate the input events for this touch. */ + if (report_touches) { + int orientation = (misc >> 10) - 32; + + 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_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]); + } + + input_mt_sync(input); + } +} + +static int magicmouse_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + struct magicmouse_sc *msc = hid_get_drvdata(hdev); + struct input_dev *input = msc->input; + int x, y, ts, ii, clicks; + + switch (data[0]) { + case 0x10: + if (size != 6) + return 0; + x = (__s16)(data[2] | data[3] << 8); + y = (__s16)(data[4] | data[5] << 8); + clicks = data[1]; + break; + case TOUCH_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++) + magicmouse_emit_touch(msc, ii, data + ii * 8 + 6); + /* When emulating three-button mode, it is important + * to have the current touch information before + * generating a click event. + */ + x = (signed char)data[1]; + y = (signed char)data[2]; + clicks = data[3]; + 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); + 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); + + if (report_touches) { + set_bit(EV_ABS, input->evbit); + + set_bit(ABS_MT_TRACKING_ID, input->absbit); + input->absmin[ABS_MT_TRACKING_ID] = 0; + input->absmax[ABS_MT_TRACKING_ID] = 15; + input->absfuzz[ABS_MT_TRACKING_ID] = 0; + + set_bit(ABS_MT_TOUCH_MAJOR, input->absbit); + input->absmin[ABS_MT_TOUCH_MAJOR] = 0; + input->absmax[ABS_MT_TOUCH_MAJOR] = 255; + input->absfuzz[ABS_MT_TOUCH_MAJOR] = 4; + + set_bit(ABS_MT_TOUCH_MINOR, input->absbit); + input->absmin[ABS_MT_TOUCH_MINOR] = 0; + input->absmax[ABS_MT_TOUCH_MINOR] = 255; + input->absfuzz[ABS_MT_TOUCH_MINOR] = 4; + + set_bit(ABS_MT_ORIENTATION, input->absbit); + input->absmin[ABS_MT_ORIENTATION] = -32; + input->absmax[ABS_MT_ORIENTATION] = 31; + input->absfuzz[ABS_MT_ORIENTATION] = 1; + + set_bit(ABS_MT_POSITION_X, input->absbit); + input->absmin[ABS_MT_POSITION_X] = -1100; + input->absmax[ABS_MT_POSITION_X] = 1358; + input->absfuzz[ABS_MT_POSITION_X] = 4; + + /* 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. + */ + set_bit(ABS_MT_POSITION_Y, input->absbit); + input->absmin[ABS_MT_POSITION_Y] = -1589; + input->absmax[ABS_MT_POSITION_Y] = 2047; + input->absfuzz[ABS_MT_POSITION_Y] = 4; + } + + if (report_undeciphered) { + set_bit(EV_MSC, input->evbit); + set_bit(MSC_RAW, input->mscbit); + } +} + +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; + struct magicmouse_sc *msc; + struct hid_report *report; + int ret; + + msc = kzalloc(sizeof(*msc), GFP_KERNEL); + if (msc == NULL) { + dev_err(&hdev->dev, "can't alloc magicmouse descriptor\n"); + return -ENOMEM; + } + + msc->quirks = id->driver_data; + hid_set_drvdata(hdev, msc); + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "magicmouse hid parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + dev_err(&hdev->dev, "magicmouse hw start failed\n"); + goto err_free; + } + + 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; + goto err_free; + } + report->size = 6; + + ret = hdev->hid_output_raw_report(hdev, feature_1, sizeof(feature_1), + HID_FEATURE_REPORT); + if (ret != sizeof(feature_1)) { + dev_err(&hdev->dev, "unable to request touch data (1:%d)\n", + ret); + goto err_free; + } + 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", + ret); + goto err_free; + } + + input = input_allocate_device(); + if (!input) { + dev_err(&hdev->dev, "can't alloc input device\n"); + ret = -ENOMEM; + goto err_free; + } + magicmouse_setup_input(input, hdev); + + ret = input_register_device(input); + if (ret) { + dev_err(&hdev->dev, "input device registration failed\n"); + goto err_both; + } + msc->input = input; + + return 0; + err_both: + input_free_device(input); + err_free: + kfree(msc); + return ret; +} + +static void magicmouse_remove(struct hid_device *hdev) +{ + hid_hw_stop(hdev); + kfree(hid_get_drvdata(hdev)); +} + +static const struct hid_device_id magic_mice[] = { + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE), + .driver_data = 0 }, + { } +}; +MODULE_DEVICE_TABLE(hid, magic_mice); + +static struct hid_driver magicmouse_driver = { + .name = "magicmouse", + .id_table = magic_mice, + .probe = magicmouse_probe, + .remove = magicmouse_remove, + .raw_event = magicmouse_raw_event, +}; + +static int __init magicmouse_init(void) +{ + int ret; + + ret = hid_register_driver(&magicmouse_driver); + if (ret) + printk(KERN_ERR "can't register magicmouse driver\n"); + + return ret; +} + +static void __exit magicmouse_exit(void) +{ + hid_unregister_driver(&magicmouse_driver); +} + +module_init(magicmouse_init); +module_exit(magicmouse_exit); +MODULE_LICENSE("GPL"); From 9f5231472340ebcaf2dec75428b67d5d0d872857 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 10 Feb 2010 14:59:03 +0100 Subject: [PATCH 32/41] HID: fix up Kconfig entry for MagicMouse Make Apple MagicMouse Kconfig entry consistent with other dirvers. Also expand the tristate text a little bit more, so that it doesn't clash with already existing HID_APPLE. Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index ba14ec898258..0e668ccc2511 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -184,11 +184,10 @@ config LOGIRUMBLEPAD2_FF Rumblepad 2 devices. config HID_MAGICMOUSE - tristate "Apple" if EMBEDDED + tristate "Apple MagicMouse multi-touch support" depends on BT_HIDP - default !EMBEDDED ---help--- - Support for the Apple Magic Mouse. + Support for the Apple Magic Mouse multi-touch. Say Y here if you want support for the multi-touch features of the Apple Wireless "Magic" Mouse. From c8a8602b76b6703df1243e31be01cf0e4451e4a6 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 10 Feb 2010 15:29:02 +0100 Subject: [PATCH 33/41] HID: remove MODULE_VERSION from new drivers MODULE_VERSION doesn't make too much sense for drivers merged into main tree, as git is much better tracking revisions than any developer might ever be. Signed-off-by: Jiri Kosina --- drivers/hid/hid-3m-pct.c | 1 - drivers/hid/hid-mosart.c | 1 - drivers/hid/hid-quanta.c | 1 - drivers/hid/hid-stantum.c | 1 - 4 files changed, 4 deletions(-) diff --git a/drivers/hid/hid-3m-pct.c b/drivers/hid/hid-3m-pct.c index 6d11e3dbbbff..2370aefc86b2 100644 --- a/drivers/hid/hid-3m-pct.c +++ b/drivers/hid/hid-3m-pct.c @@ -17,7 +17,6 @@ #include #include -MODULE_VERSION("0.6"); MODULE_AUTHOR("Stephane Chatty "); MODULE_DESCRIPTION("3M PCT multitouch panels"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-mosart.c b/drivers/hid/hid-mosart.c index f6ec4f150859..c8718168fe42 100644 --- a/drivers/hid/hid-mosart.c +++ b/drivers/hid/hid-mosart.c @@ -19,7 +19,6 @@ #include #include "usbhid/usbhid.h" -MODULE_VERSION("1.00"); MODULE_AUTHOR("Stephane Chatty "); MODULE_DESCRIPTION("MosArt dual-touch panel"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-quanta.c b/drivers/hid/hid-quanta.c index 9ac49287b943..01dd51c4986c 100644 --- a/drivers/hid/hid-quanta.c +++ b/drivers/hid/hid-quanta.c @@ -16,7 +16,6 @@ #include #include -MODULE_VERSION("1.00"); MODULE_AUTHOR("Stephane Chatty "); MODULE_DESCRIPTION("Quanta dual-touch panel"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-stantum.c b/drivers/hid/hid-stantum.c index 04b0b986f5f4..2e592a06654e 100644 --- a/drivers/hid/hid-stantum.c +++ b/drivers/hid/hid-stantum.c @@ -16,7 +16,6 @@ #include #include -MODULE_VERSION("0.6"); MODULE_AUTHOR("Stephane Chatty "); MODULE_DESCRIPTION("Stantum HID multitouch panels"); MODULE_LICENSE("GPL"); From 71b38bd4c1cc4f2b653064357e4efab77dfd711d Mon Sep 17 00:00:00 2001 From: Michael Poole Date: Thu, 11 Feb 2010 00:32:57 -0500 Subject: [PATCH 34/41] HID: magicmouse: coding style and probe failure fixes Use proper values to initialize bool configuration variables, tabs rather than spaces, no braces for one-line else clause, __set_bit() when the operation doesn't have to be atomic, input_set_abs_params() rather than writing the fields directly, and call hid_hw_stop() when appropriate to handle failures in the probe. Signed-off-by: Michael Poole Signed-off-by: Jiri Kosina --- drivers/hid/hid-magicmouse.c | 98 ++++++++++++++---------------------- 1 file changed, 39 insertions(+), 59 deletions(-) diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index f94b3e43c5b6..4a3a94f2b10c 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -18,22 +18,22 @@ #include "hid-ids.h" -static bool emulate_3button = 1; +static bool emulate_3button = true; module_param(emulate_3button, bool, 0644); MODULE_PARM_DESC(emulate_3button, "Emulate a middle button"); static int middle_button_start = -350; static int middle_button_stop = +350; -static bool emulate_scroll_wheel = 1; +static bool emulate_scroll_wheel = true; module_param(emulate_scroll_wheel, bool, 0644); MODULE_PARM_DESC(emulate_scroll_wheel, "Emulate a scroll wheel"); -static bool report_touches = 1; +static bool report_touches = true; module_param(report_touches, bool, 0644); MODULE_PARM_DESC(report_touches, "Emit touch records (otherwise, only use them for emulation)"); -static bool report_undeciphered = 0; +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"); @@ -108,9 +108,9 @@ static int magicmouse_firm_touch(struct magicmouse_sc *msc) static void magicmouse_emit_buttons(struct magicmouse_sc *msc, int state) { - int last_state = test_bit(BTN_LEFT, msc->input->key) << 0 | - test_bit(BTN_RIGHT, msc->input->key) << 1 | - test_bit(BTN_MIDDLE, msc->input->key) << 2; + int last_state = test_bit(BTN_LEFT, msc->input->key) << 0 | + test_bit(BTN_RIGHT, msc->input->key) << 1 | + test_bit(BTN_MIDDLE, msc->input->key) << 2; if (emulate_3button) { int id; @@ -177,7 +177,7 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda switch (tdata[7] & TOUCH_STATE_MASK) { case TOUCH_STATE_START: msc->touches[id].scroll_y = y; - msc->scroll_accel = min_t(int, msc->scroll_accel + 1, + msc->scroll_accel = min_t(int, msc->scroll_accel + 1, ARRAY_SIZE(accel_profile) - 1); break; case TOUCH_STATE_DRAG: @@ -193,7 +193,7 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda /* Generate the input events for this touch. */ if (report_touches) { - int orientation = (misc >> 10) - 32; + int orientation = (misc >> 10) - 32; input_report_abs(input, ABS_MT_TRACKING_ID, id); input_report_abs(input, ABS_MT_TOUCH_MAJOR, tdata[3]); @@ -202,9 +202,8 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); - if (report_undeciphered) { + if (report_undeciphered) input_event(input, EV_MSC, MSC_RAW, tdata[7]); - } input_mt_sync(input); } @@ -291,62 +290,41 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h 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); + __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(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); + __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_WHEEL, input->relbit); if (report_touches) { - set_bit(EV_ABS, input->evbit); - - set_bit(ABS_MT_TRACKING_ID, input->absbit); - input->absmin[ABS_MT_TRACKING_ID] = 0; - input->absmax[ABS_MT_TRACKING_ID] = 15; - input->absfuzz[ABS_MT_TRACKING_ID] = 0; - - set_bit(ABS_MT_TOUCH_MAJOR, input->absbit); - input->absmin[ABS_MT_TOUCH_MAJOR] = 0; - input->absmax[ABS_MT_TOUCH_MAJOR] = 255; - input->absfuzz[ABS_MT_TOUCH_MAJOR] = 4; - - set_bit(ABS_MT_TOUCH_MINOR, input->absbit); - input->absmin[ABS_MT_TOUCH_MINOR] = 0; - input->absmax[ABS_MT_TOUCH_MINOR] = 255; - input->absfuzz[ABS_MT_TOUCH_MINOR] = 4; - - set_bit(ABS_MT_ORIENTATION, input->absbit); - input->absmin[ABS_MT_ORIENTATION] = -32; - input->absmax[ABS_MT_ORIENTATION] = 31; - input->absfuzz[ABS_MT_ORIENTATION] = 1; - - set_bit(ABS_MT_POSITION_X, input->absbit); - input->absmin[ABS_MT_POSITION_X] = -1100; - input->absmax[ABS_MT_POSITION_X] = 1358; - input->absfuzz[ABS_MT_POSITION_X] = 4; + __set_bit(EV_ABS, input->evbit); + input_set_abs_params(input, ABS_MT_TRACKING_ID, 0, 15, 0, 0); + 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. */ - set_bit(ABS_MT_POSITION_Y, input->absbit); - input->absmin[ABS_MT_POSITION_Y] = -1589; - input->absmax[ABS_MT_POSITION_Y] = 2047; - input->absfuzz[ABS_MT_POSITION_Y] = 4; + input_set_abs_params(input, ABS_MT_POSITION_Y, -1589, 2047, + 4, 0); } if (report_undeciphered) { - set_bit(EV_MSC, input->evbit); - set_bit(MSC_RAW, input->mscbit); + __set_bit(EV_MSC, input->evbit); + __set_bit(MSC_RAW, input->mscbit); } } @@ -385,7 +363,7 @@ static int magicmouse_probe(struct hid_device *hdev, if (!report) { dev_err(&hdev->dev, "unable to register touch report\n"); ret = -ENOMEM; - goto err_free; + goto err_stop_hw; } report->size = 6; @@ -394,35 +372,37 @@ static int magicmouse_probe(struct hid_device *hdev, if (ret != sizeof(feature_1)) { dev_err(&hdev->dev, "unable to request touch data (1:%d)\n", ret); - goto err_free; + 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", ret); - goto err_free; + goto err_stop_hw; } input = input_allocate_device(); if (!input) { dev_err(&hdev->dev, "can't alloc input device\n"); ret = -ENOMEM; - goto err_free; + 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_both; + goto err_input; } msc->input = input; return 0; - err_both: +err_input: input_free_device(input); - err_free: +err_stop_hw: + hid_hw_stop(hdev); +err_free: kfree(msc); return ret; } From 858155fbcc0cd713f6382c527bb1c3abc0ed6d00 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 12 Feb 2010 13:02:28 +0100 Subject: [PATCH 35/41] HID: usbhid: introduce timeout for stuck ctrl/out URBs Some devices do not react to a control request (seen on APC UPS's) resulting in a slow stream of messages, "generic-usb ... control queue full". Therefore request needs a timeout. Cc: stable@kernel.org Signed-off-by: Oliver Neukum Signed-off-by: David Fries Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 28 ++++++++++++++++++++++++++-- drivers/hid/usbhid/usbhid.h | 2 ++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 54060741d45b..74bd3ca220d3 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -316,6 +316,7 @@ static int hid_submit_out(struct hid_device *hid) err_hid("usb_submit_urb(out) failed"); return -1; } + usbhid->last_out = jiffies; } else { /* * queue work to wake up the device. @@ -377,6 +378,7 @@ static int hid_submit_ctrl(struct hid_device *hid) err_hid("usb_submit_urb(ctrl) failed"); return -1; } + usbhid->last_ctrl = jiffies; } else { /* * queue work to wake up the device. @@ -512,9 +514,20 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re usbhid->out[usbhid->outhead].report = report; usbhid->outhead = head; - if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) + if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) { if (hid_submit_out(hid)) clear_bit(HID_OUT_RUNNING, &usbhid->iofl); + } else { + /* + * the queue is known to run + * but an earlier request may be stuck + * we may need to time out + * no race because this is called under + * spinlock + */ + if (time_after(jiffies, usbhid->last_out + HZ * 5)) + usb_unlink_urb(usbhid->urbout); + } return; } @@ -535,9 +548,20 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re usbhid->ctrl[usbhid->ctrlhead].dir = dir; usbhid->ctrlhead = head; - if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl)) + if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl)) { if (hid_submit_ctrl(hid)) clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); + } else { + /* + * the queue is known to run + * but an earlier request may be stuck + * we may need to time out + * no race because this is called under + * spinlock + */ + if (time_after(jiffies, usbhid->last_ctrl + HZ * 5)) + usb_unlink_urb(usbhid->urbctrl); + } } void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir) diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index 08f505ca2e3d..ec20400c7f29 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h @@ -80,12 +80,14 @@ struct usbhid_device { unsigned char ctrlhead, ctrltail; /* Control fifo head & tail */ char *ctrlbuf; /* Control buffer */ dma_addr_t ctrlbuf_dma; /* Control buffer dma */ + unsigned long last_ctrl; /* record of last output for timeouts */ struct urb *urbout; /* Output URB */ struct hid_output_fifo out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */ unsigned char outhead, outtail; /* Output pipe fifo head & tail */ char *outbuf; /* Output buffer */ dma_addr_t outbuf_dma; /* Output buffer dma */ + unsigned long last_out; /* record of last output for timeouts */ spinlock_t lock; /* fifo spinlock */ unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */ From 943ed464f3722de0569cf41ba6ec094768ac046d Mon Sep 17 00:00:00 2001 From: Rafi Rubin Date: Thu, 11 Feb 2010 22:14:05 -0500 Subject: [PATCH 36/41] HID: hid-ntrig add multi input quirk and clean up Added a quirk to enable distinct input devices. The digitizer utilizes three inputs to represent pen, multitouch and a normal touch screen. With the Pen partitioned, it behaves well and does not need special handling. Also, I set names to the input devices to clarify the functions of the various inputs. Signed-off-by: Rafi Rubin Signed-off-by: Jiri Kosina --- drivers/hid/hid-ntrig.c | 69 ++++++++++++++++++++++++++++++++++------- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index 49ce69d7bba7..38b2364c8867 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -26,10 +26,10 @@ struct ntrig_data { __s32 x, y, id, w, h; - char reading_a_point, found_contact_id; - char pen_active; - char finger_active; - char inverted; + bool reading_a_point, found_contact_id; + bool pen_active; + bool finger_active; + bool inverted; }; /* @@ -42,6 +42,10 @@ 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) { + /* No special mappings needed for the pen */ + if (field->application == HID_DG_PEN) + return 0; + switch (usage->hid & HID_USAGE_PAGE) { case HID_UP_GENDESK: @@ -104,6 +108,9 @@ 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) { + /* No special mappings needed for the pen */ + if (field->application == HID_DG_PEN) + return 0; if (usage->type == EV_KEY || usage->type == EV_REL || usage->type == EV_ABS) clear_bit(usage->code, *bit); @@ -123,6 +130,10 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field, struct input_dev *input = field->hidinput->input; struct ntrig_data *nd = hid_get_drvdata(hid); + /* No special handling needed for the pen */ + if (field->application == HID_DG_PEN) + return 0; + if (hid->claimed & HID_CLAIMED_INPUT) { switch (usage->hid) { @@ -231,8 +242,8 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field, } /* we have handled the hidinput part, now remains hiddev */ - if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event) - hid->hiddev_hid_event(hid, field, usage, value); + if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_hid_event) + hid->hiddev_hid_event(hid, field, usage, value); return 1; } @@ -241,6 +252,11 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret; struct ntrig_data *nd; + struct hid_input *hidinput; + struct input_dev *input; + + if (id->driver_data) + hdev->quirks |= HID_QUIRK_MULTI_INPUT; nd = kmalloc(sizeof(struct ntrig_data), GFP_KERNEL); if (!nd) { @@ -252,12 +268,43 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) hid_set_drvdata(hdev, nd); ret = hid_parse(hdev); - if (!ret) - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err_free; + } - if (ret) - kfree (nd); + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err_free; + } + + list_for_each_entry(hidinput, &hdev->inputs, list) { + input = hidinput->input; + switch (hidinput->report->field[0]->application) { + case HID_DG_PEN: + input->name = "N-Trig Pen"; + break; + case HID_DG_TOUCHSCREEN: + /* + * The physical touchscreen (single touch) + * input has a value for physical, whereas + * the multitouch only has logical input + * fields. + */ + input->name = + (hidinput->report->field[0] + ->physical) ? + "N-Trig Touchscreen" : + "N-Trig MultiTouch"; + break; + } + } + + return 0; +err_free: + kfree(nd); return ret; } @@ -276,7 +323,7 @@ MODULE_DEVICE_TABLE(hid, ntrig_devices); static const struct hid_usage_id ntrig_grabbed_usages[] = { { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID }, - { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1} + { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1 } }; static struct hid_driver ntrig_driver = { From b0549cf1a35a92edf053d94066e60fb0ed02bb71 Mon Sep 17 00:00:00 2001 From: Rafi Rubin Date: Thu, 11 Feb 2010 22:14:06 -0500 Subject: [PATCH 37/41] HID: n-trig: remove unnecessary tool switching With the pen and touch split apart, we no longer need to inject additional tool switching events. Signed-off-by: Rafi Rubin Signed-off-by: Jiri Kosina --- drivers/hid/hid-ntrig.c | 41 +---------------------------------------- 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index 38b2364c8867..1bda3a4bd71f 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -27,9 +27,6 @@ struct ntrig_data { __s32 x, y, id, w, h; bool reading_a_point, found_contact_id; - bool pen_active; - bool finger_active; - bool inverted; }; /* @@ -47,7 +44,6 @@ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi, return 0; switch (usage->hid & HID_USAGE_PAGE) { - case HID_UP_GENDESK: switch (usage->hid) { case HID_GD_X: @@ -111,6 +107,7 @@ static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi, /* No special mappings needed for the pen */ if (field->application == HID_DG_PEN) return 0; + if (usage->type == EV_KEY || usage->type == EV_REL || usage->type == EV_ABS) clear_bit(usage->code, *bit); @@ -136,18 +133,6 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field, if (hid->claimed & HID_CLAIMED_INPUT) { switch (usage->hid) { - - case HID_DG_INRANGE: - if (field->application & 0x3) - nd->pen_active = (value != 0); - else - nd->finger_active = (value != 0); - return 0; - - case HID_DG_INVERT: - nd->inverted = value; - return 0; - case HID_GD_X: nd->x = value; nd->reading_a_point = 1; @@ -171,34 +156,10 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field, * to emit a normal (X, Y) position */ if (!nd->found_contact_id) { - if (nd->pen_active && nd->finger_active) { - input_report_key(input, BTN_TOOL_DOUBLETAP, 0); - input_report_key(input, BTN_TOOL_DOUBLETAP, 1); - } input_event(input, EV_ABS, ABS_X, nd->x); input_event(input, EV_ABS, ABS_Y, nd->y); } break; - case HID_DG_TIPPRESSURE: - /* - * when in single touch mode, this is the last - * report received in a pen event. We want - * to emit a normal (X, Y) position - */ - if (! nd->found_contact_id) { - if (nd->pen_active && nd->finger_active) { - input_report_key(input, - nd->inverted ? BTN_TOOL_RUBBER : BTN_TOOL_PEN - , 0); - input_report_key(input, - nd->inverted ? BTN_TOOL_RUBBER : BTN_TOOL_PEN - , 1); - } - input_event(input, EV_ABS, ABS_X, nd->x); - input_event(input, EV_ABS, ABS_Y, nd->y); - input_event(input, EV_ABS, ABS_PRESSURE, value); - } - break; case 0xff000002: /* * we receive this when the device is in multitouch From dbf2b17de505d390b5ecf5b5944fc0c88f6d66fe Mon Sep 17 00:00:00 2001 From: Rafi Rubin Date: Fri, 12 Feb 2010 21:13:05 -0500 Subject: [PATCH 38/41] HID: hid-ntrig: multitouch cleanup and fix This cleans up the identification of multitouch groups and enables the end of group sync. Taps are now explicitly handled to adjust for the changes in the event stream in multitouch mode. Added triple and quad tap for the benefit of tools that recognize different tap types but do not have full multi touch support. This cleans up the behavior particularly for the latest firmware, which didn't work particularly well with the older version of the driver. In this form, when multitouch is active, both mt and st events will come out of the "N-Trig MultiTouch" device. And when its not st events will come out of "N-Trig Touchscreen". Signed-off-by: Rafi Rubin Signed-off-by: Jiri Kosina --- drivers/hid/hid-ntrig.c | 116 ++++++++++++++++++++++++++++++++-------- 1 file changed, 94 insertions(+), 22 deletions(-) diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index 1bda3a4bd71f..f6f882da6267 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -25,8 +25,16 @@ EV_KEY, (c)) struct ntrig_data { - __s32 x, y, id, w, h; - bool reading_a_point, found_contact_id; + /* Incoming raw values for a single contact */ + __u16 x, y, w, h; + __u16 id; + __u8 confidence; + + bool reading_mt; + __u8 first_contact_confidence; + + __u8 mt_footer[4]; + __u8 mt_foot_count; }; /* @@ -39,8 +47,8 @@ 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) { - /* No special mappings needed for the pen */ - if (field->application == HID_DG_PEN) + /* No special mappings needed for the pen and single touch */ + if (field->physical) return 0; switch (usage->hid & HID_USAGE_PAGE) { @@ -66,18 +74,12 @@ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_UP_DIGITIZER: switch (usage->hid) { /* we do not want to map these for now */ - case HID_DG_CONTACTID: /* value is useless */ + case HID_DG_CONTACTID: /* Not trustworthy, squelch for now */ case HID_DG_INPUTMODE: case HID_DG_DEVICEINDEX: - case HID_DG_CONTACTCOUNT: case HID_DG_CONTACTMAX: return -1; - /* original mapping by Rafi Rubin */ - case HID_DG_CONFIDENCE: - nt_map_key_clear(BTN_TOOL_DOUBLETAP); - return 1; - /* width/height mapped on TouchMajor/TouchMinor/Orientation */ case HID_DG_WIDTH: hid_map_usage(hi, usage, bit, max, @@ -104,8 +106,8 @@ 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) { - /* No special mappings needed for the pen */ - if (field->application == HID_DG_PEN) + /* No special mappings needed for the pen and single touch */ + if (field->physical) return 0; if (usage->type == EV_KEY || usage->type == EV_REL @@ -133,17 +135,24 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field, if (hid->claimed & HID_CLAIMED_INPUT) { switch (usage->hid) { + case 0xff000001: + /* Tag indicating the start of a multitouch group */ + nd->reading_mt = 1; + nd->first_contact_confidence = 0; + break; + case HID_DG_CONFIDENCE: + nd->confidence = value; + break; case HID_GD_X: nd->x = value; - nd->reading_a_point = 1; + /* Clear the contact footer */ + nd->mt_foot_count = 0; break; case HID_GD_Y: nd->y = value; break; case HID_DG_CONTACTID: nd->id = value; - /* we receive this only when in multitouch mode */ - nd->found_contact_id = 1; break; case HID_DG_WIDTH: nd->w = value; @@ -155,7 +164,7 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field, * report received in a finger event. We want * to emit a normal (X, Y) position */ - if (!nd->found_contact_id) { + if (!nd->reading_mt) { input_event(input, EV_ABS, ABS_X, nd->x); input_event(input, EV_ABS, ABS_Y, nd->y); } @@ -167,10 +176,34 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field, * this usage tells if the contact point is real * or a placeholder */ - if (!nd->reading_a_point || value != 1) + + /* Shouldn't get more than 4 footer packets, so skip */ + if (nd->mt_foot_count >= 4) break; + + nd->mt_footer[nd->mt_foot_count++] = value; + + /* if the footer isn't complete break */ + if (nd->mt_foot_count != 4) + break; + + /* Pen activity signal, trigger end of touch. */ + if (nd->mt_footer[2]) { + nd->confidence = 0; + break; + } + + /* If the contact was invalid */ + if (!(nd->confidence && nd->mt_footer[0]) + || nd->w <= 250 + || nd->h <= 190) { + nd->confidence = 0; + break; + } + /* emit a normal (X, Y) for the first point only */ if (nd->id == 0) { + nd->first_contact_confidence = nd->confidence; input_event(input, EV_ABS, ABS_X, nd->x); input_event(input, EV_ABS, ABS_Y, nd->y); } @@ -192,8 +225,39 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field, ABS_MT_TOUCH_MINOR, nd->w); } input_mt_sync(field->hidinput->input); - nd->reading_a_point = 0; - nd->found_contact_id = 0; + break; + + case HID_DG_CONTACTCOUNT: /* End of a multitouch group */ + if (!nd->reading_mt) + break; + + nd->reading_mt = 0; + + if (nd->first_contact_confidence) { + switch (value) { + case 0: /* for single touch devices */ + case 1: + input_report_key(input, + BTN_TOOL_DOUBLETAP, 1); + break; + case 2: + input_report_key(input, + BTN_TOOL_TRIPLETAP, 1); + break; + case 3: + default: + input_report_key(input, + BTN_TOOL_QUADTAP, 1); + } + input_report_key(input, BTN_TOUCH, 1); + } else { + input_report_key(input, + BTN_TOOL_DOUBLETAP, 0); + input_report_key(input, + BTN_TOOL_TRIPLETAP, 0); + input_report_key(input, + BTN_TOOL_QUADTAP, 0); + } break; default: @@ -224,8 +288,8 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) dev_err(&hdev->dev, "cannot allocate N-Trig data\n"); return -ENOMEM; } - nd->reading_a_point = 0; - nd->found_contact_id = 0; + + nd->reading_mt = 0; hid_set_drvdata(hdev, nd); ret = hid_parse(hdev); @@ -248,6 +312,14 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) input->name = "N-Trig Pen"; break; case HID_DG_TOUCHSCREEN: + __clear_bit(BTN_TOOL_PEN, input->keybit); + /* + * A little something special to enable + * two and three finger taps. + */ + __set_bit(BTN_TOOL_DOUBLETAP, input->keybit); + __set_bit(BTN_TOOL_TRIPLETAP, input->keybit); + __set_bit(BTN_TOOL_QUADTAP, input->keybit); /* * The physical touchscreen (single touch) * input has a value for physical, whereas From ff5cf34c809cd5950579e46e7c10c29bc647aaf1 Mon Sep 17 00:00:00 2001 From: Rafi Rubin Date: Tue, 16 Feb 2010 10:22:11 -0500 Subject: [PATCH 39/41] HID: hid-ntrig: Single touch mode tap Add DOUBLETAP to events emitted when in single touch only mode. Users with a single touch firmware report not seeing the DOUBLETAP events; this is a side effect of dropping old mapping for confidence. The confidence mapping may be fine for singletouch mode but causes problems in multitouch mode. Signed-off-by: Rafi Rubin Signed-off-by: Jiri Kosina --- drivers/hid/hid-ntrig.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index f6f882da6267..3234c729a895 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -165,6 +165,8 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field, * to emit a normal (X, Y) position */ if (!nd->reading_mt) { + input_report_key(input, BTN_TOOL_DOUBLETAP, + (nd->confidence != 0)); input_event(input, EV_ABS, ABS_X, nd->x); input_event(input, EV_ABS, ABS_Y, nd->y); } From 7f978b9bf535cbbd1ae8d9fbdd7c4f60ab38f260 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 17 Feb 2010 09:36:35 +0100 Subject: [PATCH 40/41] HID: extend mask for BUTTON usage page Now that joystick button usages can expand over 15 buttons, we have to properly mask out the code from hid usage to cover the whole 0xffff available space. Reported-by: Stefan Bader Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index a01a1b0de1c3..79d9edd0bdfa 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -193,7 +193,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel break; case HID_UP_BUTTON: - code = ((usage->hid - 1) & 0xf); + code = ((usage->hid - 1) & HID_USAGE); switch (field->application) { case HID_GD_MOUSE: From 57ab12e418ec4fe24c11788bb1bbdabb29d05679 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 17 Feb 2010 14:25:01 +0100 Subject: [PATCH 41/41] HID: usbhid: initialize interface pointers early enough MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the initialization of USB interface pointers from _start() over to _probe() callback, which is where it belongs. This fixes case where interface is NULL when parsing of report descriptor fails. LKML-Reference: <20100213135720.603e5f64@neptune.home> Reported-by: Alan Stern Tested-by: Bruno Prémont Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 74bd3ca220d3..ceaf4a14b67b 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -1005,9 +1005,6 @@ static int usbhid_start(struct hid_device *hid) spin_lock_init(&usbhid->lock); - usbhid->intf = intf; - usbhid->ifnum = interface->desc.bInterfaceNumber; - usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL); if (!usbhid->urbctrl) { ret = -ENOMEM; @@ -1178,6 +1175,8 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id * hid->driver_data = usbhid; usbhid->hid = hid; + usbhid->intf = intf; + usbhid->ifnum = interface->desc.bInterfaceNumber; ret = hid_add_device(hid); if (ret) {