From 5519cab477b61326963c8d523520db0342862b63 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 7 Jan 2011 23:45:50 +0100 Subject: HID: hid-multitouch: support for PixCir-based panels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created a driver for PixCir based dual-touch panels, including the one in the Hanvon tablet. This is done in a code structure aimed at unifying support for several existing HID multitouch panels. Signed-off-by: Benjamin Tissoires Signed-off-by: Stéphane Chatty Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 468 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 468 insertions(+) create mode 100644 drivers/hid/hid-multitouch.c (limited to 'drivers/hid/hid-multitouch.c') diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c new file mode 100644 index 000000000000..3b05dfe910bc --- /dev/null +++ b/drivers/hid/hid-multitouch.c @@ -0,0 +1,468 @@ +/* + * HID driver for multitouch panels + * + * Copyright (c) 2010-2011 Stephane Chatty + * Copyright (c) 2010-2011 Benjamin Tissoires + * Copyright (c) 2010-2011 Ecole Nationale de l'Aviation Civile, France + * + */ + +/* + * 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 +#include +#include "usbhid/usbhid.h" + + +MODULE_AUTHOR("Stephane Chatty "); +MODULE_DESCRIPTION("HID multitouch panels"); +MODULE_LICENSE("GPL"); + +#include "hid-ids.h" + +/* quirks to control the device */ +#define MT_QUIRK_NOT_SEEN_MEANS_UP (1 << 0) +#define MT_QUIRK_SLOT_IS_CONTACTID (1 << 1) + +struct mt_slot { + __s32 x, y, p, w, h; + __s32 contactid; /* the device ContactID assigned to this slot */ + bool touch_state; /* is the touch valid? */ + bool seen_in_this_frame;/* has this slot been updated */ +}; + +struct mt_device { + struct mt_slot curdata; /* placeholder of incoming data */ + struct mt_class *mtclass; /* our mt device class */ + unsigned last_field_index; /* last field index of the report */ + unsigned last_slot_field; /* the last field of a slot */ + __s8 inputmode; /* InputMode HID feature, -1 if non-existent */ + __u8 num_received; /* how many contacts we received */ + __u8 num_expected; /* expected last contact index */ + bool curvalid; /* is the current contact valid? */ + struct mt_slot slots[0]; /* first slot */ +}; + +struct mt_class { + __s32 quirks; + __s32 sn_move; /* Signal/noise ratio for move events */ + __s32 sn_pressure; /* Signal/noise ratio for pressure events */ + __u8 maxcontacts; +}; + +/* classes of device behavior */ +#define MT_CLS_DEFAULT 0 +#define MT_CLS_DUAL1 1 + +/* + * these device-dependent functions determine what slot corresponds + * to a valid contact that was just read. + */ + +static int slot_is_contactid(struct mt_device *td) +{ + return td->curdata.contactid; +} + +static int find_slot_from_contactid(struct mt_device *td) +{ + int i; + for (i = 0; i < td->mtclass->maxcontacts; ++i) { + if (td->slots[i].contactid == td->curdata.contactid && + td->slots[i].touch_state) + return i; + } + for (i = 0; i < td->mtclass->maxcontacts; ++i) { + if (!td->slots[i].seen_in_this_frame && + !td->slots[i].touch_state) + return i; + } + return -1; + /* should not occurs. If this happens that means + * that the device sent more touches that it says + * in the report descriptor. It is ignored then. */ +} + +struct mt_class mt_classes[] = { + { 0, 0, 0, 10 }, /* MT_CLS_DEFAULT */ + { MT_QUIRK_SLOT_IS_CONTACTID, 0, 0, 2 }, /* MT_CLS_DUAL1 */ +}; + +static void mt_feature_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage) +{ + if (usage->hid == HID_DG_INPUTMODE) { + struct mt_device *td = hid_get_drvdata(hdev); + td->inputmode = field->report->id; + } +} + +static void set_abs(struct input_dev *input, unsigned int code, + struct hid_field *field, int snratio) +{ + int fmin = field->logical_minimum; + int fmax = field->logical_maximum; + int fuzz = snratio ? (fmax - fmin) / snratio : 0; + input_set_abs_params(input, code, fmin, fmax, fuzz, 0); +} + +static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + struct mt_device *td = hid_get_drvdata(hdev); + struct mt_class *cls = td->mtclass; + 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); + set_abs(hi->input, ABS_MT_POSITION_X, field, + cls->sn_move); + /* touchscreen emulation */ + set_abs(hi->input, ABS_X, field, cls->sn_move); + td->last_slot_field = usage->hid; + return 1; + case HID_GD_Y: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_POSITION_Y); + set_abs(hi->input, ABS_MT_POSITION_Y, field, + cls->sn_move); + /* touchscreen emulation */ + set_abs(hi->input, ABS_Y, field, cls->sn_move); + td->last_slot_field = usage->hid; + return 1; + } + return 0; + + case HID_UP_DIGITIZER: + switch (usage->hid) { + case HID_DG_INRANGE: + td->last_slot_field = usage->hid; + return 1; + case HID_DG_CONFIDENCE: + td->last_slot_field = usage->hid; + return 1; + case HID_DG_TIPSWITCH: + hid_map_usage(hi, usage, bit, max, EV_KEY, BTN_TOUCH); + input_set_capability(hi->input, EV_KEY, BTN_TOUCH); + td->last_slot_field = usage->hid; + return 1; + case HID_DG_CONTACTID: + input_mt_init_slots(hi->input, + td->mtclass->maxcontacts); + td->last_slot_field = usage->hid; + return 1; + case HID_DG_WIDTH: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_TOUCH_MAJOR); + td->last_slot_field = usage->hid; + return 1; + case HID_DG_HEIGHT: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_TOUCH_MINOR); + field->logical_maximum = 1; + field->logical_minimum = 1; + set_abs(hi->input, ABS_MT_ORIENTATION, field, 0); + td->last_slot_field = usage->hid; + return 1; + case HID_DG_TIPPRESSURE: + hid_map_usage(hi, usage, bit, max, + EV_ABS, ABS_MT_PRESSURE); + set_abs(hi->input, ABS_MT_PRESSURE, field, + cls->sn_pressure); + /* touchscreen emulation */ + set_abs(hi->input, ABS_PRESSURE, field, + cls->sn_pressure); + td->last_slot_field = usage->hid; + return 1; + case HID_DG_CONTACTCOUNT: + td->last_field_index = field->report->maxfield - 1; + return 1; + case HID_DG_CONTACTMAX: + /* we don't set td->last_slot_field as contactcount and + * contact max are global to the report */ + 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 mt_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) + set_bit(usage->type, hi->input->evbit); + + return -1; +} + +static int mt_compute_slot(struct mt_device *td) +{ + struct mt_class *cls = td->mtclass; + + if (cls->quirks & MT_QUIRK_SLOT_IS_CONTACTID) + return slot_is_contactid(td); + + return find_slot_from_contactid(td); +} + +/* + * this function is called when a whole contact has been processed, + * so that it can assign it to a slot and store the data there + */ +static void mt_complete_slot(struct mt_device *td) +{ + if (td->curvalid) { + struct mt_slot *slot; + int slotnum = mt_compute_slot(td); + + if (slotnum >= 0 && slotnum < td->mtclass->maxcontacts) { + slot = td->slots + slotnum; + + memcpy(slot, &(td->curdata), sizeof(struct mt_slot)); + slot->seen_in_this_frame = true; + } + } + td->num_received++; +} + + +/* + * 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 mt_emit_event(struct mt_device *td, struct input_dev *input) +{ + int i; + + for (i = 0; i < td->mtclass->maxcontacts; ++i) { + struct mt_slot *s = &(td->slots[i]); + if ((td->mtclass->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) && + !s->seen_in_this_frame) { + /* + * this slot does not contain useful data, + * notify its closure + */ + s->touch_state = false; + } + + input_mt_slot(input, i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, + s->touch_state); + input_event(input, EV_ABS, ABS_MT_POSITION_X, s->x); + input_event(input, EV_ABS, ABS_MT_POSITION_Y, s->y); + input_event(input, EV_ABS, ABS_MT_PRESSURE, s->p); + input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, s->w); + input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, s->h); + s->seen_in_this_frame = false; + + } + + input_mt_report_pointer_emulation(input, true); + input_sync(input); + td->num_received = 0; +} + + + +static int mt_event(struct hid_device *hid, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct mt_device *td = hid_get_drvdata(hid); + + if (hid->claimed & HID_CLAIMED_INPUT) { + switch (usage->hid) { + case HID_DG_INRANGE: + break; + case HID_DG_TIPSWITCH: + td->curvalid = value; + td->curdata.touch_state = value; + break; + case HID_DG_CONFIDENCE: + break; + case HID_DG_CONTACTID: + td->curdata.contactid = value; + break; + case HID_DG_TIPPRESSURE: + td->curdata.p = value; + break; + case HID_GD_X: + td->curdata.x = value; + break; + case HID_GD_Y: + td->curdata.y = value; + break; + case HID_DG_WIDTH: + td->curdata.w = value; + break; + case HID_DG_HEIGHT: + td->curdata.h = value; + break; + case HID_DG_CONTACTCOUNT: + /* + * We must not overwrite the previous value (some + * devices send one sequence splitted over several + * messages) + */ + if (value) + td->num_expected = value - 1; + break; + + default: + /* fallback to the generic hidinput handling */ + return 0; + } + } + + if (usage->hid == td->last_slot_field) + mt_complete_slot(td); + + if (field->index == td->last_field_index + && td->num_received > td->num_expected) + mt_emit_event(td, field->hidinput->input); + + /* 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 void mt_set_input_mode(struct hid_device *hdev) +{ + struct mt_device *td = hid_get_drvdata(hdev); + struct hid_report *r; + struct hid_report_enum *re; + + if (td->inputmode < 0) + return; + + re = &(hdev->report_enum[HID_FEATURE_REPORT]); + r = re->report_id_hash[td->inputmode]; + if (r) { + r->field[0]->value[0] = 0x02; + usbhid_submit_report(hdev, r, USB_DIR_OUT); + } +} + +static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + struct mt_device *td; + struct mt_class *mtclass = mt_classes + id->driver_data; + + /* This allows the driver to correctly support devices + * that emit events over several HID messages. + */ + hdev->quirks |= HID_QUIRK_NO_INPUT_SYNC; + + td = kzalloc(sizeof(struct mt_device) + + mtclass->maxcontacts * sizeof(struct mt_slot), + GFP_KERNEL); + if (!td) { + dev_err(&hdev->dev, "cannot allocate multitouch data\n"); + return -ENOMEM; + } + td->mtclass = mtclass; + td->inputmode = -1; + hid_set_drvdata(hdev, td); + + ret = hid_parse(hdev); + if (ret != 0) + goto fail; + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret != 0) + goto fail; + + mt_set_input_mode(hdev); + + return 0; + +fail: + kfree(td); + return ret; +} + +#ifdef CONFIG_PM +static int mt_reset_resume(struct hid_device *hdev) +{ + mt_set_input_mode(hdev); + return 0; +} +#endif + +static void mt_remove(struct hid_device *hdev) +{ + struct mt_device *td = hid_get_drvdata(hdev); + hid_hw_stop(hdev); + kfree(td); + hid_set_drvdata(hdev, NULL); +} + +static const struct hid_device_id mt_devices[] = { + + /* PixCir-based panels */ + { .driver_data = MT_CLS_DUAL1, + HID_USB_DEVICE(USB_VENDOR_ID_HANVON, + USB_DEVICE_ID_HANVON_MULTITOUCH) }, + { .driver_data = MT_CLS_DUAL1, + HID_USB_DEVICE(USB_VENDOR_ID_CANDO, + USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH) }, + + { } +}; +MODULE_DEVICE_TABLE(hid, mt_devices); + +static const struct hid_usage_id mt_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 mt_driver = { + .name = "hid-multitouch", + .id_table = mt_devices, + .probe = mt_probe, + .remove = mt_remove, + .input_mapping = mt_input_mapping, + .input_mapped = mt_input_mapped, + .feature_mapping = mt_feature_mapping, + .usage_table = mt_grabbed_usages, + .event = mt_event, +#ifdef CONFIG_PM + .reset_resume = mt_reset_resume, +#endif +}; + +static int __init mt_init(void) +{ + return hid_register_driver(&mt_driver); +} + +static void __exit mt_exit(void) +{ + hid_unregister_driver(&mt_driver); +} + +module_init(mt_init); +module_exit(mt_exit); -- cgit v1.2.3 From a3b5e577d96bfccbc41ebf4df784e3a153072273 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 7 Jan 2011 23:46:30 +0100 Subject: HID: hid-multitouch: add support for Cypress TrueTouch panels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added support for Cypress TrueTouch panels, which detect up to 10 fingers Signed-off-by: Benjamin Tissoires Signed-off-by: Stéphane Chatty Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 1 + drivers/hid/hid-core.c | 1 + drivers/hid/hid-ids.h | 1 + drivers/hid/hid-multitouch.c | 19 +++++++++++++++++++ 4 files changed, 22 insertions(+) (limited to 'drivers/hid/hid-multitouch.c') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 511554de526e..de31d75e276f 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -293,6 +293,7 @@ config HID_MULTITOUCH Say Y here if you have one of the following devices: - PixCir touchscreen + - Cypress TrueTouch config HID_NTRIG tristate "N-Trig touch screen" diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 9292eb148ce4..12d4afafc73b 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1298,6 +1298,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_TRUETOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) }, { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) }, { HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH1) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index bd3cd551b4ee..f5657a8c757d 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -180,6 +180,7 @@ #define USB_DEVICE_ID_CYPRESS_BARCODE_1 0xde61 #define USB_DEVICE_ID_CYPRESS_BARCODE_2 0xde64 #define USB_DEVICE_ID_CYPRESS_BARCODE_3 0xbca1 +#define USB_DEVICE_ID_CYPRESS_TRUETOUCH 0xc001 #define USB_VENDOR_ID_DEALEXTREAME 0x10c5 #define USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701 0x819a diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 3b05dfe910bc..7af9f7136bd5 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -32,6 +32,7 @@ MODULE_LICENSE("GPL"); /* quirks to control the device */ #define MT_QUIRK_NOT_SEEN_MEANS_UP (1 << 0) #define MT_QUIRK_SLOT_IS_CONTACTID (1 << 1) +#define MT_QUIRK_CYPRESS (1 << 2) struct mt_slot { __s32 x, y, p, w, h; @@ -62,6 +63,7 @@ struct mt_class { /* classes of device behavior */ #define MT_CLS_DEFAULT 0 #define MT_CLS_DUAL1 1 +#define MT_CLS_CYPRESS 2 /* * these device-dependent functions determine what slot corresponds @@ -73,6 +75,14 @@ static int slot_is_contactid(struct mt_device *td) return td->curdata.contactid; } +static int cypress_compute_slot(struct mt_device *td) +{ + if (td->curdata.contactid != 0 || td->num_received == 0) + return td->curdata.contactid; + else + return -1; +} + static int find_slot_from_contactid(struct mt_device *td) { int i; @@ -95,6 +105,7 @@ static int find_slot_from_contactid(struct mt_device *td) struct mt_class mt_classes[] = { { 0, 0, 0, 10 }, /* MT_CLS_DEFAULT */ { MT_QUIRK_SLOT_IS_CONTACTID, 0, 0, 2 }, /* MT_CLS_DUAL1 */ + { MT_QUIRK_CYPRESS | MT_QUIRK_NOT_SEEN_MEANS_UP, 0, 0, 10 }, /* MT_CLS_CYPRESS */ }; static void mt_feature_mapping(struct hid_device *hdev, struct hid_input *hi, @@ -223,6 +234,9 @@ static int mt_compute_slot(struct mt_device *td) if (cls->quirks & MT_QUIRK_SLOT_IS_CONTACTID) return slot_is_contactid(td); + if (cls->quirks & MT_QUIRK_CYPRESS) + return cypress_compute_slot(td); + return find_slot_from_contactid(td); } @@ -422,6 +436,11 @@ static void mt_remove(struct hid_device *hdev) static const struct hid_device_id mt_devices[] = { + /* Cypress panel */ + { .driver_data = MT_CLS_CYPRESS, + HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, + USB_DEVICE_ID_CYPRESS_TRUETOUCH) }, + /* PixCir-based panels */ { .driver_data = MT_CLS_DUAL1, HID_USB_DEVICE(USB_VENDOR_ID_HANVON, -- cgit v1.2.3 From 5572da08a784621f2ab4fdc8dc65471261871795 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 7 Jan 2011 23:47:27 +0100 Subject: HID: hid-mulitouch: add support for the 'Sensing Win7-TwoFinger' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added support for the 'Sensing Win7-TwoFinger' panel by GeneralTouch found on some tablets. Because of conflicting VID/PID, this conflicts with previous support for some single-touch panels by GeneralTouch Signed-off-by: Benjamin Tissoires Signed-off-by: Stéphane Chatty Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 1 + drivers/hid/hid-core.c | 2 +- drivers/hid/hid-ids.h | 1 + drivers/hid/hid-multitouch.c | 18 +++++++++++++++++- 4 files changed, 20 insertions(+), 2 deletions(-) (limited to 'drivers/hid/hid-multitouch.c') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index de31d75e276f..9bd21482e618 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -294,6 +294,7 @@ config HID_MULTITOUCH Say Y here if you have one of the following devices: - PixCir touchscreen - Cypress TrueTouch + - 'Sensing Win7-TwoFinger' panel by GeneralTouch config HID_NTRIG tristate "N-Trig touch screen" diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 12d4afafc73b..42cfc9464c0a 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1309,6 +1309,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) }, { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) }, { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR) }, + { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS) }, { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003) }, { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0012) }, { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE) }, @@ -1614,7 +1615,6 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) }, { HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC5UH) }, { HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC4UM) }, - { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0001) }, { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0002) }, { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0003) }, { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0004) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index f5657a8c757d..265c747ba9b8 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -228,6 +228,7 @@ #define USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR 0x0002 #define USB_VENDOR_ID_GENERAL_TOUCH 0x0dfc +#define USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS 0x0001 #define USB_VENDOR_ID_GLAB 0x06c2 #define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 7af9f7136bd5..3442ed56e964 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -33,6 +33,7 @@ MODULE_LICENSE("GPL"); #define MT_QUIRK_NOT_SEEN_MEANS_UP (1 << 0) #define MT_QUIRK_SLOT_IS_CONTACTID (1 << 1) #define MT_QUIRK_CYPRESS (1 << 2) +#define MT_QUIRK_SLOT_IS_CONTACTNUMBER (1 << 3) struct mt_slot { __s32 x, y, p, w, h; @@ -63,7 +64,8 @@ struct mt_class { /* classes of device behavior */ #define MT_CLS_DEFAULT 0 #define MT_CLS_DUAL1 1 -#define MT_CLS_CYPRESS 2 +#define MT_CLS_DUAL2 2 +#define MT_CLS_CYPRESS 3 /* * these device-dependent functions determine what slot corresponds @@ -75,6 +77,11 @@ static int slot_is_contactid(struct mt_device *td) return td->curdata.contactid; } +static int slot_is_contactnumber(struct mt_device *td) +{ + return td->num_received; +} + static int cypress_compute_slot(struct mt_device *td) { if (td->curdata.contactid != 0 || td->num_received == 0) @@ -105,6 +112,7 @@ static int find_slot_from_contactid(struct mt_device *td) struct mt_class mt_classes[] = { { 0, 0, 0, 10 }, /* MT_CLS_DEFAULT */ { MT_QUIRK_SLOT_IS_CONTACTID, 0, 0, 2 }, /* MT_CLS_DUAL1 */ + { MT_QUIRK_SLOT_IS_CONTACTNUMBER, 0, 0, 10 }, /* MT_CLS_DUAL2 */ { MT_QUIRK_CYPRESS | MT_QUIRK_NOT_SEEN_MEANS_UP, 0, 0, 10 }, /* MT_CLS_CYPRESS */ }; @@ -237,6 +245,9 @@ static int mt_compute_slot(struct mt_device *td) if (cls->quirks & MT_QUIRK_CYPRESS) return cypress_compute_slot(td); + if (cls->quirks & MT_QUIRK_SLOT_IS_CONTACTNUMBER) + return slot_is_contactnumber(td); + return find_slot_from_contactid(td); } @@ -441,6 +452,11 @@ static const struct hid_device_id mt_devices[] = { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_TRUETOUCH) }, + /* GeneralTouch panel */ + { .driver_data = MT_CLS_DUAL2, + HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, + USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS) }, + /* PixCir-based panels */ { .driver_data = MT_CLS_DUAL1, HID_USB_DEVICE(USB_VENDOR_ID_HANVON, -- cgit v1.2.3 From 2d93666e70662cfcf1927e1a858685f5b38d5d65 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 11 Jan 2011 16:45:54 +0100 Subject: HID: hid-multitouch: minor fixes based on additional review * amended Kconfig (PixCir and Hanvon are the same panel but with different name) * insert field name in mt_class and retrieving it in mt_probe * add 2 quirks: MT_QUIRK_VALID_IS_INRANGE, MT_QUIRK_VALID_IS_CONFIDENCE, in order to find the field "valid" * inlined slot_is_contactid and slot_is_contact_number * cosmetics changes (tabs and comments) * do not send unnecessary properties once the touch is up Signed-off-by: Benjamin Tissoires Acked-by: Henrik Rydberg Signed-off-by: Jiri Kosina --- drivers/hid/Kconfig | 10 +++- drivers/hid/hid-multitouch.c | 127 ++++++++++++++++++++++++------------------- 2 files changed, 78 insertions(+), 59 deletions(-) (limited to 'drivers/hid/hid-multitouch.c') diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 9bd21482e618..97c200b0f4b8 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -292,10 +292,16 @@ config HID_MULTITOUCH Generic support for HID multitouch panels. Say Y here if you have one of the following devices: - - PixCir touchscreen - - Cypress TrueTouch + - Cypress TrueTouch panels + - Hanvon dual touch panels + - Pixcir dual touch panels - 'Sensing Win7-TwoFinger' panel by GeneralTouch + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called hid-multitouch. + config HID_NTRIG tristate "N-Trig touch screen" depends on USB_HID diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 3442ed56e964..07d3183fdde5 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -32,8 +32,10 @@ MODULE_LICENSE("GPL"); /* quirks to control the device */ #define MT_QUIRK_NOT_SEEN_MEANS_UP (1 << 0) #define MT_QUIRK_SLOT_IS_CONTACTID (1 << 1) -#define MT_QUIRK_CYPRESS (1 << 2) +#define MT_QUIRK_CYPRESS (1 << 2) #define MT_QUIRK_SLOT_IS_CONTACTNUMBER (1 << 3) +#define MT_QUIRK_VALID_IS_INRANGE (1 << 4) +#define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 5) struct mt_slot { __s32 x, y, p, w, h; @@ -55,6 +57,7 @@ struct mt_device { }; struct mt_class { + __s32 name; /* MT_CLS */ __s32 quirks; __s32 sn_move; /* Signal/noise ratio for move events */ __s32 sn_pressure; /* Signal/noise ratio for pressure events */ @@ -62,26 +65,16 @@ struct mt_class { }; /* classes of device behavior */ -#define MT_CLS_DEFAULT 0 -#define MT_CLS_DUAL1 1 -#define MT_CLS_DUAL2 2 -#define MT_CLS_CYPRESS 3 +#define MT_CLS_DEFAULT 1 +#define MT_CLS_DUAL1 2 +#define MT_CLS_DUAL2 3 +#define MT_CLS_CYPRESS 4 /* * these device-dependent functions determine what slot corresponds * to a valid contact that was just read. */ -static int slot_is_contactid(struct mt_device *td) -{ - return td->curdata.contactid; -} - -static int slot_is_contactnumber(struct mt_device *td) -{ - return td->num_received; -} - static int cypress_compute_slot(struct mt_device *td) { if (td->curdata.contactid != 0 || td->num_received == 0) @@ -103,17 +96,30 @@ static int find_slot_from_contactid(struct mt_device *td) !td->slots[i].touch_state) return i; } - return -1; /* should not occurs. If this happens that means * that the device sent more touches that it says * in the report descriptor. It is ignored then. */ + return -1; } struct mt_class mt_classes[] = { - { 0, 0, 0, 10 }, /* MT_CLS_DEFAULT */ - { MT_QUIRK_SLOT_IS_CONTACTID, 0, 0, 2 }, /* MT_CLS_DUAL1 */ - { MT_QUIRK_SLOT_IS_CONTACTNUMBER, 0, 0, 10 }, /* MT_CLS_DUAL2 */ - { MT_QUIRK_CYPRESS | MT_QUIRK_NOT_SEEN_MEANS_UP, 0, 0, 10 }, /* MT_CLS_CYPRESS */ + { .name = MT_CLS_DEFAULT, + .quirks = MT_QUIRK_VALID_IS_INRANGE, + .maxcontacts = 10 }, + { .name = MT_CLS_DUAL1, + .quirks = MT_QUIRK_VALID_IS_INRANGE | + MT_QUIRK_SLOT_IS_CONTACTID, + .maxcontacts = 2 }, + { .name = MT_CLS_DUAL2, + .quirks = MT_QUIRK_VALID_IS_INRANGE | + MT_QUIRK_SLOT_IS_CONTACTNUMBER, + .maxcontacts = 2 }, + { .name = MT_CLS_CYPRESS, + .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | + MT_QUIRK_CYPRESS, + .maxcontacts = 10 }, + + { } }; static void mt_feature_mapping(struct hid_device *hdev, struct hid_input *hi, @@ -192,7 +198,7 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, hid_map_usage(hi, usage, bit, max, EV_ABS, ABS_MT_TOUCH_MINOR); field->logical_maximum = 1; - field->logical_minimum = 1; + field->logical_minimum = 0; set_abs(hi->input, ABS_MT_ORIENTATION, field, 0); td->last_slot_field = usage->hid; return 1; @@ -237,16 +243,16 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi, static int mt_compute_slot(struct mt_device *td) { - struct mt_class *cls = td->mtclass; + __s32 quirks = td->mtclass->quirks; - if (cls->quirks & MT_QUIRK_SLOT_IS_CONTACTID) - return slot_is_contactid(td); + if (quirks & MT_QUIRK_SLOT_IS_CONTACTID) + return td->curdata.contactid; - if (cls->quirks & MT_QUIRK_CYPRESS) + if (quirks & MT_QUIRK_CYPRESS) return cypress_compute_slot(td); - if (cls->quirks & MT_QUIRK_SLOT_IS_CONTACTNUMBER) - return slot_is_contactnumber(td); + if (quirks & MT_QUIRK_SLOT_IS_CONTACTNUMBER) + return td->num_received; return find_slot_from_contactid(td); } @@ -257,16 +263,12 @@ static int mt_compute_slot(struct mt_device *td) */ static void mt_complete_slot(struct mt_device *td) { + td->curdata.seen_in_this_frame = true; if (td->curvalid) { - struct mt_slot *slot; int slotnum = mt_compute_slot(td); - if (slotnum >= 0 && slotnum < td->mtclass->maxcontacts) { - slot = td->slots + slotnum; - - memcpy(slot, &(td->curdata), sizeof(struct mt_slot)); - slot->seen_in_this_frame = true; - } + if (slotnum >= 0 && slotnum < td->mtclass->maxcontacts) + td->slots[slotnum] = td->curdata; } td->num_received++; } @@ -284,21 +286,19 @@ static void mt_emit_event(struct mt_device *td, struct input_dev *input) struct mt_slot *s = &(td->slots[i]); if ((td->mtclass->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) && !s->seen_in_this_frame) { - /* - * this slot does not contain useful data, - * notify its closure - */ s->touch_state = false; } input_mt_slot(input, i); input_mt_report_slot_state(input, MT_TOOL_FINGER, s->touch_state); - input_event(input, EV_ABS, ABS_MT_POSITION_X, s->x); - input_event(input, EV_ABS, ABS_MT_POSITION_Y, s->y); - input_event(input, EV_ABS, ABS_MT_PRESSURE, s->p); - input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, s->w); - input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, s->h); + if (s->touch_state) { + input_event(input, EV_ABS, ABS_MT_POSITION_X, s->x); + input_event(input, EV_ABS, ABS_MT_POSITION_Y, s->y); + input_event(input, EV_ABS, ABS_MT_PRESSURE, s->p); + input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, s->w); + input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, s->h); + } s->seen_in_this_frame = false; } @@ -314,16 +314,22 @@ static int mt_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) { struct mt_device *td = hid_get_drvdata(hid); + __s32 quirks = td->mtclass->quirks; if (hid->claimed & HID_CLAIMED_INPUT) { switch (usage->hid) { case HID_DG_INRANGE: + if (quirks & MT_QUIRK_VALID_IS_INRANGE) + td->curvalid = value; break; case HID_DG_TIPSWITCH: - td->curvalid = value; + if (quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) + td->curvalid = value; td->curdata.touch_state = value; break; case HID_DG_CONFIDENCE: + if (quirks & MT_QUIRK_VALID_IS_CONFIDENCE) + td->curvalid = value; break; case HID_DG_CONTACTID: td->curdata.contactid = value; @@ -345,26 +351,26 @@ static int mt_event(struct hid_device *hid, struct hid_field *field, break; case HID_DG_CONTACTCOUNT: /* - * We must not overwrite the previous value (some - * devices send one sequence splitted over several - * messages) + * Includes multi-packet support where subsequent + * packets are sent with zero contactcount. */ if (value) - td->num_expected = value - 1; + td->num_expected = value; break; default: /* fallback to the generic hidinput handling */ return 0; } - } - if (usage->hid == td->last_slot_field) - mt_complete_slot(td); + if (usage->hid == td->last_slot_field) + mt_complete_slot(td); + + if (field->index == td->last_field_index + && td->num_received >= td->num_expected) + mt_emit_event(td, field->hidinput->input); - if (field->index == td->last_field_index - && td->num_received > td->num_expected) - mt_emit_event(td, field->hidinput->input); + } /* we have handled the hidinput part, now remains hiddev */ if (hid->claimed & HID_CLAIMED_HIDDEV && hid->hiddev_hid_event) @@ -392,9 +398,16 @@ static void mt_set_input_mode(struct hid_device *hdev) static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) { - int ret; + int ret, i; struct mt_device *td; - struct mt_class *mtclass = mt_classes + id->driver_data; + struct mt_class *mtclass = mt_classes; /* MT_CLS_DEFAULT */ + + for (i = 0; mt_classes[i].name ; i++) { + if (id->driver_data == mt_classes[i].name) { + mtclass = &(mt_classes[i]); + break; + } + } /* This allows the driver to correctly support devices * that emit events over several HID messages. @@ -417,7 +430,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) goto fail; ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); - if (ret != 0) + if (ret) goto fail; mt_set_input_mode(hdev); -- cgit v1.2.3