diff options
| -rw-r--r-- | drivers/hid/Kconfig | 54 | ||||
| -rw-r--r-- | drivers/hid/Makefile | 9 | ||||
| -rw-r--r-- | drivers/hid/hid-3m-pct.c | 290 | ||||
| -rw-r--r-- | drivers/hid/hid-apple.c | 17 | ||||
| -rw-r--r-- | drivers/hid/hid-core.c | 23 | ||||
| -rw-r--r-- | drivers/hid/hid-debug.c | 6 | ||||
| -rw-r--r-- | drivers/hid/hid-ids.h | 37 | ||||
| -rw-r--r-- | drivers/hid/hid-input.c | 12 | ||||
| -rw-r--r-- | drivers/hid/hid-lg.c | 7 | ||||
| -rw-r--r-- | drivers/hid/hid-lg.h | 6 | ||||
| -rw-r--r-- | drivers/hid/hid-lg3ff.c | 176 | ||||
| -rw-r--r-- | drivers/hid/hid-lgff.c | 1 | ||||
| -rw-r--r-- | drivers/hid/hid-magicmouse.c | 449 | ||||
| -rw-r--r-- | drivers/hid/hid-mosart.c | 273 | ||||
| -rw-r--r-- | drivers/hid/hid-ntrig.c | 212 | ||||
| -rw-r--r-- | drivers/hid/hid-ortek.c | 56 | ||||
| -rw-r--r-- | drivers/hid/hid-quanta.c | 260 | ||||
| -rw-r--r-- | drivers/hid/hid-sony.c | 23 | ||||
| -rw-r--r-- | drivers/hid/hid-stantum.c | 283 | ||||
| -rw-r--r-- | drivers/hid/hid-wacom.c | 28 | ||||
| -rw-r--r-- | drivers/hid/hidraw.c | 2 | ||||
| -rw-r--r-- | drivers/hid/usbhid/hid-core.c | 42 | ||||
| -rw-r--r-- | drivers/hid/usbhid/hid-quirks.c | 3 | ||||
| -rw-r--r-- | drivers/hid/usbhid/usbhid.h | 2 | ||||
| -rw-r--r-- | include/linux/hid.h | 5 | ||||
| -rw-r--r-- | include/linux/input.h | 42 | ||||
| -rw-r--r-- | net/bluetooth/hidp/core.c | 17 | 
27 files changed, 2222 insertions, 113 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 24d90ea246c..71d4c070362 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -55,6 +55,12 @@ source "drivers/hid/usbhid/Kconfig"  menu "Special HID drivers"  	depends on HID +config HID_3M_PCT +	tristate "3M PCT" +	depends on USB_HID +	---help--- +	Support for 3M PCT touch screens. +  config HID_A4TECH  	tristate "A4 tech" if EMBEDDED  	depends on USB_HID @@ -183,6 +189,23 @@ 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_MAGICMOUSE +	tristate "Apple MagicMouse multi-touch support" +	depends on BT_HIDP +	---help--- +	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. +  config HID_MICROSOFT  	tristate "Microsoft" if EMBEDDED  	depends on USB_HID @@ -190,6 +213,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 @@ -198,12 +227,18 @@ 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. +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 @@ -227,6 +262,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 @@ -241,6 +282,12 @@ config HID_SONY  	---help---  	Support for Sony PS3 controller. +config HID_STANTUM +	tristate "Stantum" +	depends on USB_HID +	---help--- +	Support for Stantum multitouch panel. +  config HID_SUNPLUS  	tristate "Sunplus" if EMBEDDED  	depends on USB_HID @@ -305,9 +352,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. diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 0de2dff5542..0b2618f092c 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -18,7 +18,11 @@ 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  obj-$(CONFIG_HID_APPLE)		+= hid-apple.o  obj-$(CONFIG_HID_BELKIN)	+= hid-belkin.o @@ -31,14 +35,19 @@ 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_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  obj-$(CONFIG_HID_PANTHERLORD)	+= hid-pl.o  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-3m-pct.c b/drivers/hid/hid-3m-pct.c new file mode 100644 index 00000000000..2370aefc86b --- /dev/null +++ b/drivers/hid/hid-3m-pct.c @@ -0,0 +1,290 @@ +/* + *  HID driver for 3M PCT multitouch panels + * + *  Copyright (c) 2009 Stephane Chatty <chatty@enac.fr> + * + */ + +/* + * 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/usb.h> + +MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>"); +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-apple.c b/drivers/hid/hid-apple.c index 5b4d66dc1a0..78286b184ac 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; +			}  		}  	} diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index eabe5f87c6c..368fbb0c4ca 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 <vojtech@suse.cz>   *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc - *  Copyright (c) 2006-2007 Jiri Kosina + *  Copyright (c) 2006-2010 Jiri Kosina   */  /* @@ -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. @@ -387,7 +388,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;  	} @@ -1248,11 +1250,13 @@ 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) },  	{ 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) }, @@ -1324,6 +1328,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) }, @@ -1337,10 +1342,15 @@ 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_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_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_STANTUM, USB_DEVICE_ID_MTP) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) }, @@ -1543,8 +1553,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) }, @@ -1661,8 +1672,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-debug.c b/drivers/hid/hid-debug.c index 6abd0369aed..cd4ece6fdfb 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; diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 010368e649e..72c05f90553 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 @@ -56,6 +59,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 @@ -96,9 +100,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 @@ -169,6 +176,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_ETT		0x0664  #define USB_DEVICE_ID_TC5UH		0x0309 @@ -303,6 +313,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 @@ -365,6 +376,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 @@ -382,9 +396,16 @@  #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 +#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 @@ -396,18 +417,20 @@  #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  #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_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/hid-input.c b/drivers/hid/hid-input.c index 5862b0f3b55..79d9edd0bdf 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   */ @@ -193,12 +193,17 @@ 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:  		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) { @@ -400,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; diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index 9fcd3d017ab..3677c9037a1 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 bf31592eaf7..ce2ac867262 100644 --- a/drivers/hid/hid-lg.h +++ b/drivers/hid/hid-lg.h @@ -13,4 +13,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 00000000000..4002832ee4a --- /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 <LordCnidarian@gmail.com> + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include <linux/input.h> +#include <linux/usb.h> +#include <linux/hid.h> + +#include "usbhid/usbhid.h" +#include "hid-lg.h" + +/* + * 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 <LordCnidarian@gmail.com>\n"); +	return 0; +} + diff --git a/drivers/hid/hid-lgff.c b/drivers/hid/hid-lgff.c index 987abebe082..61142b76a9b 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 }, diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c new file mode 100644 index 00000000000..4a3a94f2b10 --- /dev/null +++ b/drivers/hid/hid-magicmouse.c @@ -0,0 +1,449 @@ +/* + *   Apple "Magic" Wireless Mouse driver + * + *   Copyright (c) 2010 Michael Poole <mdpoole@troilus.org> + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> +#include <linux/usb.h> + +#include "hid-ids.h" + +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 = true; +module_param(emulate_scroll_wheel, bool, 0644); +MODULE_PARM_DESC(emulate_scroll_wheel, "Emulate a scroll wheel"); + +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; +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); + +		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. +		 */ +		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); +	} +} + +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_stop_hw; +	} +	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_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_stop_hw; +	} + +	input = input_allocate_device(); +	if (!input) { +		dev_err(&hdev->dev, "can't alloc input device\n"); +		ret = -ENOMEM; +		goto err_stop_hw; +	} +	magicmouse_setup_input(input, hdev); + +	ret = input_register_device(input); +	if (ret) { +		dev_err(&hdev->dev, "input device registration failed\n"); +		goto err_input; +	} +	msc->input = input; + +	return 0; +err_input: +	input_free_device(input); +err_stop_hw: +	hid_hw_stop(hdev); +err_free: +	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"); diff --git a/drivers/hid/hid-mosart.c b/drivers/hid/hid-mosart.c new file mode 100644 index 00000000000..c8718168fe4 --- /dev/null +++ b/drivers/hid/hid-mosart.c @@ -0,0 +1,273 @@ +/* + *  HID driver for the multitouch panel on the ASUS EeePC T91MT + * + *  Copyright (c) 2009-2010 Stephane Chatty <chatty@enac.fr> + *  Copyright (c) 2010 Teemu Tuominen <teemu.tuominen@cybercom.com> + * + */ + +/* + * 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/usb.h> +#include "usbhid/usbhid.h" + +MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>"); +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); + diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index 49ce69d7bba..3234c729a89 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -25,11 +25,16 @@  					EV_KEY, (c))  struct ntrig_data { -	__s32 x, y, id, w, h; -	char reading_a_point, found_contact_id; -	char pen_active; -	char finger_active; -	char inverted; +	/* 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;  };  /* @@ -42,8 +47,11 @@ 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)  { -	switch (usage->hid & HID_USAGE_PAGE) { +	/* No special mappings needed for the pen and single touch */ +	if (field->physical) +		return 0; +	switch (usage->hid & HID_USAGE_PAGE) {  	case HID_UP_GENDESK:  		switch (usage->hid) {  		case HID_GD_X: @@ -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,6 +106,10 @@ 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 and single touch */ +	if (field->physical) +		return 0; +  	if (usage->type == EV_KEY || usage->type == EV_REL  			|| usage->type == EV_ABS)  		clear_bit(usage->code, *bit); @@ -123,31 +129,30 @@ 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) { - -		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 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; @@ -159,35 +164,13 @@ 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->pen_active && nd->finger_active) { -					input_report_key(input, BTN_TOOL_DOUBLETAP, 0); -					input_report_key(input, BTN_TOOL_DOUBLETAP, 1); -				} +			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);  			}  			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 @@ -195,10 +178,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);  			} @@ -220,8 +227,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: @@ -231,8 +269,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,23 +279,67 @@ 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) {  		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); -	if (!ret) -		ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); +	if (ret) { +		dev_err(&hdev->dev, "parse failed\n"); +		goto err_free; +	} + +	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); +	if (ret) { +		dev_err(&hdev->dev, "hw start failed\n"); +		goto err_free; +	} -	if (ret) -		kfree (nd); +	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: +			__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 +			 * 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 +358,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 = { diff --git a/drivers/hid/hid-ortek.c b/drivers/hid/hid-ortek.c new file mode 100644 index 00000000000..aa9a960f73a --- /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 <jmharris@gmail.com> + */ + +/* + * 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 "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"); diff --git a/drivers/hid/hid-quanta.c b/drivers/hid/hid-quanta.c new file mode 100644 index 00000000000..01dd51c4986 --- /dev/null +++ b/drivers/hid/hid-quanta.c @@ -0,0 +1,260 @@ +/* + *  HID driver for Quanta Optical Touch dual-touch panels + * + *  Copyright (c) 2009-2010 Stephane Chatty <chatty@enac.fr> + * + */ + +/* + * 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> + +MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>"); +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) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, +			USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) }, +	{ } +}; +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); + diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 4e8450228a2..9bf00d77d92 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; @@ -81,7 +87,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;  	} @@ -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 },  	{ } diff --git a/drivers/hid/hid-stantum.c b/drivers/hid/hid-stantum.c new file mode 100644 index 00000000000..2e592a06654 --- /dev/null +++ b/drivers/hid/hid-stantum.c @@ -0,0 +1,283 @@ +/* + *  HID driver for Stantum multitouch panels + * + *  Copyright (c) 2009 Stephane Chatty <chatty@enac.fr> + * + */ + +/* + * 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> + +MODULE_AUTHOR("Stephane Chatty <chatty@enac.fr>"); +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: +			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_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); +			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); + +	input_event(input, EV_ABS, ABS_MT_PRESSURE, sd->z); + +	input_mt_sync(input); +	sd->valid = 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); +	} +	sd->first = false; +} + + +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); + diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c index 12dcda52920..8d3b46f5d14 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,31 @@ 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; +	do { +		ret = hdev->hid_output_raw_report(hdev, rep_data, 2, +				HID_FEATURE_REPORT); +	} while (ret < 0 && limit-- > 0); +	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; +	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_warn(&hdev->dev, "failed to poke device #2, %d\n", ret); +  	hidinput = list_entry(hdev->inputs.next, struct hid_input, list);  	input = hidinput->input; diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index cdd136942bc..d04476700b7 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 e2997a8d5e1..56d06cd8075 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 <vojtech@suse.cz>   *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc   *  Copyright (c) 2007-2008 Oliver Neukum - *  Copyright (c) 2006-2009 Jiri Kosina + *  Copyright (c) 2006-2010 Jiri Kosina   */  /* @@ -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) @@ -774,7 +798,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 +810,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); @@ -981,9 +1006,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; @@ -1154,6 +1176,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) { @@ -1342,7 +1366,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 */ diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 38773dc2821..7844280897d 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -43,8 +43,10 @@ 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_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 }, @@ -57,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 }, diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index 08f505ca2e3..ec20400c7f2 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) */ diff --git a/include/linux/hid.h b/include/linux/hid.h index 87093652dda..b1344ec4b7f 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; @@ -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 */ @@ -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); diff --git a/include/linux/input.h b/include/linux/input.h index 663208afb64..f44ee911440 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -598,6 +598,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 diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index fc6ec1e7265..280529ad927 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;  |