diff options
| -rw-r--r-- | drivers/hid/Kconfig | 17 | ||||
| -rw-r--r-- | drivers/hid/Makefile | 1 | ||||
| -rw-r--r-- | drivers/hid/hid-cando.c | 2 | ||||
| -rw-r--r-- | drivers/hid/hid-core.c | 7 | ||||
| -rw-r--r-- | drivers/hid/hid-ids.h | 7 | ||||
| -rw-r--r-- | drivers/hid/hid-input.c | 17 | ||||
| -rw-r--r-- | drivers/hid/hid-mosart.c | 1 | ||||
| -rw-r--r-- | drivers/hid/hid-multitouch.c | 516 | ||||
| -rw-r--r-- | drivers/hid/usbhid/hid-quirks.c | 1 | ||||
| -rw-r--r-- | include/linux/hid.h | 6 | 
10 files changed, 568 insertions, 7 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index ffbc278647b..24cca2f69df 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -295,6 +295,23 @@ config HID_MONTEREY  	---help---  	Support for Monterey Genius KB29E. +config HID_MULTITOUCH +	tristate "HID Multitouch panels" +	depends on USB_HID +	---help--- +	  Generic support for HID multitouch panels. + +	  Say Y here if you have one of the following devices: +	  - 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/Makefile b/drivers/hid/Makefile index 6eae9a90b8d..6efc2a0370a 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_HID_MAGICMOUSE)    += hid-magicmouse.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_MULTITOUCH)	+= hid-multitouch.o  obj-$(CONFIG_HID_NTRIG)		+= hid-ntrig.o  obj-$(CONFIG_HID_ORTEK)		+= hid-ortek.o  obj-$(CONFIG_HID_PRODIKEYS)	+= hid-prodikeys.o diff --git a/drivers/hid/hid-cando.c b/drivers/hid/hid-cando.c index 375b50929a5..1ea066c5520 100644 --- a/drivers/hid/hid-cando.c +++ b/drivers/hid/hid-cando.c @@ -236,6 +236,8 @@ static const struct hid_device_id cando_devices[] = {  	{ HID_USB_DEVICE(USB_VENDOR_ID_CANDO,  			USB_DEVICE_ID_CANDO_MULTI_TOUCH) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_CANDO, +			USB_DEVICE_ID_CANDO_MULTI_TOUCH_10_1) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_CANDO,  			USB_DEVICE_ID_CANDO_MULTI_TOUCH_11_6) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_CANDO,  		USB_DEVICE_ID_CANDO_MULTI_TOUCH_15_6) }, diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 261168607c9..d678cf3d33d 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1312,7 +1312,9 @@ static const struct hid_device_id hid_have_special_driver[] = {  	{ HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE_2) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_MULTI_TOUCH) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_MULTI_TOUCH_10_1) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_MULTI_TOUCH_11_6) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_CANDO, USB_DEVICE_ID_CANDO_MULTI_TOUCH_15_6) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) }, @@ -1324,6 +1326,7 @@ static const struct hid_device_id hid_have_special_driver[] = {  	{ 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) }, @@ -1335,11 +1338,13 @@ static const struct hid_device_id hid_have_special_driver[] = {  	{ 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) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_2) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_HANVON, USB_DEVICE_ID_HANVON_MULTITOUCH) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, @@ -1422,6 +1427,7 @@ static const struct hid_device_id hid_have_special_driver[] = {  	{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED2, USB_DEVICE_ID_TOPSEED2_RF_COMBO) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_TWINHAN, USB_DEVICE_ID_TWINHAN_IR_REMOTE) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) }, @@ -1640,7 +1646,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 f65cace7772..92a0d61a737 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -140,7 +140,9 @@  #define USB_DEVICE_ID_BTC_EMPREX_REMOTE_2	0x5577  #define USB_VENDOR_ID_CANDO		0x2087 +#define USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH 0x0703  #define USB_DEVICE_ID_CANDO_MULTI_TOUCH	0x0a01 +#define USB_DEVICE_ID_CANDO_MULTI_TOUCH_10_1 0x0a02  #define USB_DEVICE_ID_CANDO_MULTI_TOUCH_11_6 0x0b03  #define USB_DEVICE_ID_CANDO_MULTI_TOUCH_15_6 0x0f01 @@ -186,6 +188,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 @@ -236,6 +239,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 @@ -318,6 +322,9 @@  #define USB_DEVICE_ID_HANWANG_TABLET_FIRST	0x5000  #define USB_DEVICE_ID_HANWANG_TABLET_LAST	0x8fff +#define USB_VENDOR_ID_HANVON		0x20b3 +#define USB_DEVICE_ID_HANVON_MULTITOUCH	0x0a18 +  #define USB_VENDOR_ID_HAPP		0x078b  #define USB_DEVICE_ID_UGCI_DRIVING	0x0010  #define USB_DEVICE_ID_UGCI_FLYING	0x0020 diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index e60fdb88101..7f552bfad32 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -290,6 +290,14 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel  		goto ignore;  	} +	if (field->report_type == HID_FEATURE_REPORT) { +		if (device->driver->feature_mapping) { +			device->driver->feature_mapping(device, hidinput, field, +				usage); +		} +		goto ignore; +	} +  	if (device->driver->input_mapping) {  		int ret = device->driver->input_mapping(device, hidinput, field,  				usage, &bit, &max); @@ -839,7 +847,6 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)  	struct hid_input *hidinput = NULL;  	struct input_dev *input_dev;  	int i, j, k; -	int max_report_type = HID_OUTPUT_REPORT;  	INIT_LIST_HEAD(&hid->inputs); @@ -856,10 +863,11 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)  			return -1;  	} -	if (hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS) -		max_report_type = HID_INPUT_REPORT; +	for (k = HID_INPUT_REPORT; k <= HID_FEATURE_REPORT; k++) { +		if (k == HID_OUTPUT_REPORT && +			hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS) +			continue; -	for (k = HID_INPUT_REPORT; k <= max_report_type; k++)  		list_for_each_entry(report, &hid->report_enum[k].report_list, list) {  			if (!report->maxfield) @@ -912,6 +920,7 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)  				hidinput = NULL;  			}  		} +	}  	if (hidinput && input_register_device(hidinput->input))  		goto out_cleanup; diff --git a/drivers/hid/hid-mosart.c b/drivers/hid/hid-mosart.c index 9fb050ce6f0..aed7ffe3628 100644 --- a/drivers/hid/hid-mosart.c +++ b/drivers/hid/hid-mosart.c @@ -257,6 +257,7 @@ static void mosart_remove(struct hid_device *hdev)  static const struct hid_device_id mosart_devices[] = {  	{ HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_T91MT) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUSTEK_MULTITOUCH_YFO) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART) },  	{ }  };  MODULE_DEVICE_TABLE(hid, mosart_devices); diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c new file mode 100644 index 00000000000..07d3183fdde --- /dev/null +++ b/drivers/hid/hid-multitouch.c @@ -0,0 +1,516 @@ +/* + *  HID driver for multitouch panels + * + *  Copyright (c) 2010-2011 Stephane Chatty <chatty@enac.fr> + *  Copyright (c) 2010-2011 Benjamin Tissoires <benjamin.tissoires@gmail.com> + *  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 <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/usb.h> +#include <linux/input/mt.h> +#include "usbhid/usbhid.h" + + +MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>"); +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) +#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; +	__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 name;	/* MT_CLS */ +	__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	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 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; +	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; +	} +	/* 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[] = { +	{ .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, +		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 = 0; +			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) +{ +	__s32 quirks = td->mtclass->quirks; + +	if (quirks & MT_QUIRK_SLOT_IS_CONTACTID) +		return td->curdata.contactid; + +	if (quirks & MT_QUIRK_CYPRESS) +		return cypress_compute_slot(td); + +	if (quirks & MT_QUIRK_SLOT_IS_CONTACTNUMBER) +		return td->num_received; + +	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) +{ +	td->curdata.seen_in_this_frame = true; +	if (td->curvalid) { +		int slotnum = mt_compute_slot(td); + +		if (slotnum >= 0 && slotnum < td->mtclass->maxcontacts) +			td->slots[slotnum] = td->curdata; +	} +	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) { +			s->touch_state = false; +		} + +		input_mt_slot(input, i); +		input_mt_report_slot_state(input, MT_TOOL_FINGER, +			s->touch_state); +		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; + +	} + +	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); +	__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: +			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; +			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: +			/* +			 * Includes multi-packet support where subsequent +			 * packets are sent with zero contactcount. +			 */ +			if (value) +				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 (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, i; +	struct mt_device *td; +	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. +	 */ +	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) +		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[] = { + +	/* Cypress panel */ +	{ .driver_data = MT_CLS_CYPRESS, +		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, +			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); diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 76b9a149c7d..9a94b643ccd 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -35,7 +35,6 @@ static const struct hid_blacklist {  	{ USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD },  	{ USB_VENDOR_ID_DWAV, USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER, HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET },  	{ USB_VENDOR_ID_MOJO, USB_DEVICE_ID_RETRO_ADAPTER, HID_QUIRK_MULTI_INPUT }, -	{ USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART, HID_QUIRK_MULTI_INPUT },  	{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },  	{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },  	{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, diff --git a/include/linux/hid.h b/include/linux/hid.h index 20b9801f669..d91c25e253c 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -402,7 +402,7 @@ struct hid_field {  	__u16 dpad;			/* dpad input code */  }; -#define HID_MAX_FIELDS 64 +#define HID_MAX_FIELDS 128  struct hid_report {  	struct list_head list; @@ -593,6 +593,7 @@ struct hid_usage_id {   * @report_fixup: called before report descriptor parsing (NULL means nop)   * @input_mapping: invoked on input registering before mapping an usage   * @input_mapped: invoked on input registering after mapping an usage + * @feature_mapping: invoked on feature registering   * @suspend: invoked on suspend (NULL means nop)   * @resume: invoked on resume if device was not reset (NULL means nop)   * @reset_resume: invoked on resume if device was reset (NULL means nop) @@ -636,6 +637,9 @@ struct hid_driver {  	int (*input_mapped)(struct hid_device *hdev,  			struct hid_input *hidinput, struct hid_field *field,  			struct hid_usage *usage, unsigned long **bit, int *max); +	void (*feature_mapping)(struct hid_device *hdev, +			struct hid_input *hidinput, struct hid_field *field, +			struct hid_usage *usage);  #ifdef CONFIG_PM  	int (*suspend)(struct hid_device *hdev, pm_message_t message);  	int (*resume)(struct hid_device *hdev);  |