diff options
| -rw-r--r-- | Documentation/ABI/testing/sysfs-driver-hid-roccat-kone | 13 | ||||
| -rw-r--r-- | drivers/hid/Kconfig | 19 | ||||
| -rw-r--r-- | drivers/hid/Makefile | 2 | ||||
| -rw-r--r-- | drivers/hid/hid-axff.c | 172 | ||||
| -rw-r--r-- | drivers/hid/hid-core.c | 8 | ||||
| -rw-r--r-- | drivers/hid/hid-elecom.c | 57 | ||||
| -rw-r--r-- | drivers/hid/hid-ids.h | 91 | ||||
| -rw-r--r-- | drivers/hid/hid-input.c | 21 | ||||
| -rw-r--r-- | drivers/hid/hid-magicmouse.c | 96 | ||||
| -rw-r--r-- | drivers/hid/hid-picolcd.c | 199 | ||||
| -rw-r--r-- | drivers/hid/hid-roccat-kone.c | 25 | ||||
| -rw-r--r-- | drivers/hid/hid-roccat-kone.h | 2 | ||||
| -rw-r--r-- | drivers/hid/hid-roccat.c | 9 | ||||
| -rw-r--r-- | drivers/hid/hid-roccat.h | 2 | ||||
| -rw-r--r-- | drivers/hid/hid-topseed.c | 5 | ||||
| -rw-r--r-- | drivers/hid/hid-wacom.c | 2 | ||||
| -rw-r--r-- | drivers/hid/hidraw.c | 2 | ||||
| -rw-r--r-- | drivers/hid/usbhid/hid-quirks.c | 2 | ||||
| -rw-r--r-- | drivers/hid/usbhid/hiddev.c | 54 | ||||
| -rw-r--r-- | include/linux/hid.h | 1 | 
20 files changed, 586 insertions, 196 deletions
diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-kone b/Documentation/ABI/testing/sysfs-driver-hid-roccat-kone index 88340a23ce9..063bda7fe70 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-roccat-kone +++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-kone @@ -33,19 +33,6 @@ Description:	When read, this file returns the raw integer version number of the  		left. E.g. a returned value of 138 means 1.38  		This file is readonly. -What:		/sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/kone_driver_version -Date:		March 2010 -Contact:	Stefan Achatz <erazor_de@users.sourceforge.net> -Description:	When read, this file returns the driver version. -		The format of the string is "v<major>.<minor>.<patchlevel>". -		This attribute is used by the userland tools to find the sysfs- -		paths of installed kone-mice and determine the capabilites of -		the driver. Versions of this driver for old kernels replace -		usbhid instead of generic-usb. The way to scan for this file -		has been chosen to provide a consistent way for all supported -		kernel versions. -		This file is readonly. -  What:		/sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/profile[1-5]  Date:		March 2010  Contact:	Stefan Achatz <erazor_de@users.sourceforge.net> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 43409936905..6369ba7f96f 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -68,6 +68,14 @@ config HID_A4TECH  	---help---  	Support for A4 tech X5 and WOP-35 / Trust 450L mice. +config HID_ACRUX_FF +	tristate "ACRUX force feedback support" +	depends on USB_HID +	select INPUT_FF_MEMLESS +	---help--- +	Say Y here if you want to enable force feedback support for ACRUX +	game controllers. +  config HID_APPLE  	tristate "Apple" if EMBEDDED  	depends on (USB_HID || BT_HIDP) @@ -148,6 +156,12 @@ config HID_EGALAX  	---help---  	Support for the eGalax dual-touch panel. +config HID_ELECOM +	tristate "ELECOM" +	depends on BT_HIDP +	---help--- +	Support for the ELECOM BM084 (bluetooth mouse). +  config HID_EZKEY  	tristate "Ezkey" if EMBEDDED  	depends on USB_HID @@ -417,10 +431,11 @@ config SMARTJOYPLUS_FF  	enable force feedback support for it.  config HID_TOPSEED -	tristate "TopSeed Cyberlink remote control support" +	tristate "TopSeed Cyberlink, BTC Emprex, Conceptronic remote control support"  	depends on USB_HID  	---help--- -	Say Y if you have a TopSeed Cyberlink or BTC Emprex remote control. +	Say Y if you have a TopSeed Cyberlink or BTC Emprex or Conceptronic +	CLLRCMCE remote control.  config HID_THRUSTMASTER  	tristate "ThrustMaster devices support" diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 987fa062736..46f037f3df8 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -24,6 +24,7 @@ endif  obj-$(CONFIG_HID_3M_PCT)	+= hid-3m-pct.o  obj-$(CONFIG_HID_A4TECH)	+= hid-a4tech.o +obj-$(CONFIG_HID_ACRUX_FF)	+= hid-axff.o  obj-$(CONFIG_HID_APPLE)		+= hid-apple.o  obj-$(CONFIG_HID_BELKIN)	+= hid-belkin.o  obj-$(CONFIG_HID_CANDO)		+= hid-cando.o @@ -32,6 +33,7 @@ obj-$(CONFIG_HID_CHICONY)	+= hid-chicony.o  obj-$(CONFIG_HID_CYPRESS)	+= hid-cypress.o  obj-$(CONFIG_HID_DRAGONRISE)	+= hid-drff.o  obj-$(CONFIG_HID_EGALAX)	+= hid-egalax.o +obj-$(CONFIG_HID_ELECOM)	+= hid-elecom.o  obj-$(CONFIG_HID_EZKEY)		+= hid-ezkey.o  obj-$(CONFIG_HID_GYRATION)	+= hid-gyration.o  obj-$(CONFIG_HID_KENSINGTON)	+= hid-kensington.o diff --git a/drivers/hid/hid-axff.c b/drivers/hid/hid-axff.c new file mode 100644 index 00000000000..f42ee140738 --- /dev/null +++ b/drivers/hid/hid-axff.c @@ -0,0 +1,172 @@ +/* + * Force feedback support for ACRUX game controllers + * + * From what I have gathered, these devices are mass produced in China + * by several vendors. They often share the same design as the original + * Xbox 360 controller. + * + * 1a34:0802 "ACRUX USB GAMEPAD 8116" + *  - tested with a EXEQ EQ-PCU-02090 game controller. + * + * Copyright (c) 2010 Sergei Kolzun <x0r@dv-life.ru> + */ + +/* + * 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/slab.h> +#include <linux/usb.h> +#include <linux/hid.h> + +#include "hid-ids.h" +#include "usbhid/usbhid.h" + +struct axff_device { +	struct hid_report *report; +}; + +static int axff_play(struct input_dev *dev, void *data, struct ff_effect *effect) +{ +	struct hid_device *hid = input_get_drvdata(dev); +	struct axff_device *axff = data; +	int left, right; + +	left = effect->u.rumble.strong_magnitude; +	right = effect->u.rumble.weak_magnitude; + +	dbg_hid("called with 0x%04x 0x%04x", left, right); + +	left = left * 0xff / 0xffff; +	right = right * 0xff / 0xffff; + +	axff->report->field[0]->value[0] = left; +	axff->report->field[1]->value[0] = right; +	axff->report->field[2]->value[0] = left; +	axff->report->field[3]->value[0] = right; +	dbg_hid("running with 0x%02x 0x%02x", left, right); +	usbhid_submit_report(hid, axff->report, USB_DIR_OUT); + +	return 0; +} + +static int axff_init(struct hid_device *hid) +{ +	struct axff_device *axff; +	struct hid_report *report; +	struct hid_input *hidinput = list_first_entry(&hid->inputs, struct hid_input, list); +	struct list_head *report_list =&hid->report_enum[HID_OUTPUT_REPORT].report_list; +	struct input_dev *dev = hidinput->input; +	int error; + +	if (list_empty(report_list)) { +		dev_err(&hid->dev, "no output reports found\n"); +		return -ENODEV; +	} + +	report = list_first_entry(report_list, struct hid_report, list); + +	if (report->maxfield < 4) { +		dev_err(&hid->dev, "no fields in the report: %d\n", report->maxfield); +		return -ENODEV; +	} + +	axff = kzalloc(sizeof(struct axff_device), GFP_KERNEL); +	if (!axff) +		return -ENOMEM; + +	set_bit(FF_RUMBLE, dev->ffbit); + +	error = input_ff_create_memless(dev, axff, axff_play); +	if (error) +		goto err_free_mem; + +	axff->report = report; +	axff->report->field[0]->value[0] = 0x00; +	axff->report->field[1]->value[0] = 0x00; +	axff->report->field[2]->value[0] = 0x00; +	axff->report->field[3]->value[0] = 0x00; +	usbhid_submit_report(hid, axff->report, USB_DIR_OUT); + +	dev_info(&hid->dev, "Force Feedback for ACRUX game controllers by Sergei Kolzun<x0r@dv-life.ru>\n"); + +	return 0; + +err_free_mem: +	kfree(axff); +	return error; +} + +static int ax_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ +	int error; + +	dev_dbg(&hdev->dev, "ACRUX HID hardware probe..."); + +	error = hid_parse(hdev); +	if (error) { +		dev_err(&hdev->dev, "parse failed\n"); +		return error; +	} + +	error = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); +	if (error) { +		dev_err(&hdev->dev, "hw start failed\n"); +		return error; +	} + +	error = axff_init(hdev); +	if (error) { +		/* +		 * Do not fail device initialization completely as device +		 * may still be partially operable, just warn. +		 */ +		dev_warn(&hdev->dev, +			 "Failed to enable force feedback support, error: %d\n", +			 error); +	} + +	return 0; +} + +static const struct hid_device_id ax_devices[] = { +	{ HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802), }, +	{ } +}; +MODULE_DEVICE_TABLE(hid, ax_devices); + +static struct hid_driver ax_driver = { +	.name = "acrux", +	.id_table = ax_devices, +	.probe = ax_probe, +}; + +static int __init ax_init(void) +{ +	return hid_register_driver(&ax_driver); +} + +static void __exit ax_exit(void) +{ +	hid_unregister_driver(&ax_driver); +} + +module_init(ax_init); +module_exit(ax_exit); + +MODULE_AUTHOR("Sergei Kolzun"); +MODULE_DESCRIPTION("Force feedback support for ACRUX game controllers"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index b54a9a608ac..e635199a0cd 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1157,6 +1157,8 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask)  	if (hdev->quirks & HID_QUIRK_HIDDEV_FORCE)  		connect_mask |= (HID_CONNECT_HIDDEV_FORCE | HID_CONNECT_HIDDEV); +	if (hdev->quirks & HID_QUIRK_HIDINPUT_FORCE) +		connect_mask |= HID_CONNECT_HIDINPUT_FORCE;  	if (hdev->bus != BUS_USB)  		connect_mask &= ~HID_CONNECT_HIDDEV;  	if (hid_hiddev(hdev)) @@ -1239,6 +1241,9 @@ static const struct hid_device_id hid_blacklist[] = {  	{ HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M2256) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) }, +#if defined(CONFIG_HID_ACRUX_FF) || defined(CONFIG_HID_ACRUX_FF_MODULE) +	{ HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802) }, +#endif  	{ 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) }, @@ -1294,6 +1299,7 @@ static const struct hid_device_id hid_blacklist[] = {  	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH) }, +	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR) }, @@ -1375,10 +1381,10 @@ static const struct hid_device_id hid_blacklist[] = {  	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb653) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED2, USB_DEVICE_ID_TOPSEED2_RF_COMBO) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_TWINHAN, USB_DEVICE_ID_TWINHAN_IR_REMOTE) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) },  	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) }, -	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_ZYDACRON, USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL) }, diff --git a/drivers/hid/hid-elecom.c b/drivers/hid/hid-elecom.c new file mode 100644 index 00000000000..7a40878f46b --- /dev/null +++ b/drivers/hid/hid-elecom.c @@ -0,0 +1,57 @@ +/* + *  HID driver for Elecom BM084 (bluetooth mouse). + *  Removes a non-existing horizontal wheel from + *  the HID descriptor. + *  (This module is based on "hid-ortek".) + * + *  Copyright (c) 2010 Richard Nauber <Richard.Nauber@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 elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc, +		unsigned int rsize) +{ +	if (rsize >= 48 && rdesc[46] == 0x05 && rdesc[47] == 0x0c) { +		dev_info(&hdev->dev, "Fixing up Elecom BM084 " +				"report descriptor.\n"); +		rdesc[47] = 0x00; +	} +} + +static const struct hid_device_id elecom_devices[] = { +	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084)}, +	{ } +}; +MODULE_DEVICE_TABLE(hid, elecom_devices); + +static struct hid_driver elecom_driver = { +	.name = "elecom", +	.id_table = elecom_devices, +	.report_fixup = elecom_report_fixup +}; + +static int __init elecom_init(void) +{ +	return hid_register_driver(&elecom_driver); +} + +static void __exit elecom_exit(void) +{ +	hid_unregister_driver(&elecom_driver); +} + +module_init(elecom_init); +module_exit(elecom_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 8aee2577c1a..d3fc13ae094 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -34,6 +34,8 @@  #define USB_DEVICE_ID_ACECAD_FLAIR	0x0004  #define USB_DEVICE_ID_ACECAD_302	0x0008 +#define USB_VENDOR_ID_ACRUX		0x1a34 +  #define USB_VENDOR_ID_ADS_TECH 		0x06e1  #define USB_DEVICE_ID_ADS_TECH_RADIO_SI470X	0xa155 @@ -81,12 +83,12 @@  #define USB_DEVICE_ID_APPLE_WELLSPRING_ANSI	0x0223  #define USB_DEVICE_ID_APPLE_WELLSPRING_ISO	0x0224  #define USB_DEVICE_ID_APPLE_WELLSPRING_JIS	0x0225 -#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI    0x0229 -#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO     0x022a -#define USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS     0x022b -#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI  0x022c -#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO   0x022d -#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS   0x022e +#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI	0x0229 +#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO	0x022a +#define USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS	0x022b +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI	0x022c +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO	0x022d +#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS	0x022e  #define USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI	0x0230  #define USB_DEVICE_ID_APPLE_WELLSPRING2_ISO	0x0231  #define USB_DEVICE_ID_APPLE_WELLSPRING2_JIS	0x0232 @@ -118,8 +120,8 @@  #define USB_VENDOR_ID_AVERMEDIA		0x07ca  #define USB_DEVICE_ID_AVER_FM_MR800	0xb800 -#define USB_VENDOR_ID_BELKIN           0x050d -#define USB_DEVICE_ID_FLIP_KVM         0x3201 +#define USB_VENDOR_ID_BELKIN		0x050d +#define USB_DEVICE_ID_FLIP_KVM		0x3201  #define USB_VENDOR_ID_BERKSHIRE		0x0c98  #define USB_DEVICE_ID_BERKSHIRE_PCWD	0x1140 @@ -128,7 +130,7 @@  #define USB_DEVICE_ID_BTC_EMPREX_REMOTE	0x5578  #define USB_VENDOR_ID_CANDO		0x2087 -#define USB_DEVICE_ID_CANDO_MULTI_TOUCH 0x0a01 +#define USB_DEVICE_ID_CANDO_MULTI_TOUCH	0x0a01  #define USB_DEVICE_ID_CANDO_MULTI_TOUCH_11_6 0x0b03  #define USB_VENDOR_ID_CH		0x068e @@ -175,7 +177,7 @@  #define USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701	0x819a  #define USB_VENDOR_ID_DELORME		0x1163 -#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100 +#define USB_DEVICE_ID_DELORME_EARTHMATE	0x0100  #define USB_DEVICE_ID_DELORME_EM_LT20	0x0200  #define USB_VENDOR_ID_DMI		0x0c0b @@ -187,20 +189,23 @@  #define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER	0x0001  #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH	0x480d +#define USB_VENDOR_ID_ELECOM		0x056e +#define USB_DEVICE_ID_ELECOM_BM084	0x0061 +  #define USB_VENDOR_ID_ELO		0x04E7  #define USB_DEVICE_ID_ELO_TS2700	0x0020  #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  #define USB_DEVICE_ID_TC4UM		0x0306 -#define USB_VENDOR_ID_EZKEY 		0x0518 +#define USB_VENDOR_ID_ETURBOTOUCH	0x22b9 +#define USB_DEVICE_ID_ETURBOTOUCH	0x0006 + +#define USB_VENDOR_ID_EZKEY		0x0518  #define USB_DEVICE_ID_BTC_8193		0x0002  #define USB_VENDOR_ID_GAMERON		0x0810 @@ -297,9 +302,16 @@  #define USB_VENDOR_ID_KBGEAR		0x084e  #define USB_DEVICE_ID_KBGEAR_JAMSTUDIO	0x1001 +#define USB_VENDOR_ID_KENSINGTON	0x047d +#define USB_DEVICE_ID_KS_SLIMBLADE	0x2041 +  #define USB_VENDOR_ID_KWORLD		0x1b80  #define USB_DEVICE_ID_KWORLD_RADIO_FM700	0xd700 +#define USB_VENDOR_ID_KYE		0x0458 +#define USB_DEVICE_ID_KYE_ERGO_525V	0x0087 +#define USB_DEVICE_ID_KYE_GPEN_560	0x5003 +  #define USB_VENDOR_ID_LABTEC		0x1020  #define USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD	0x0006 @@ -319,9 +331,6 @@  #define USB_DEVICE_ID_LD_POWERCONTROL	0x2030  #define USB_DEVICE_ID_LD_MACHINETEST	0x2040 -#define USB_VENDOR_ID_KENSINGTON	0x047d -#define USB_DEVICE_ID_KS_SLIMBLADE	0x2041 -  #define USB_VENDOR_ID_LOGITECH		0x046d  #define USB_DEVICE_ID_LOGITECH_RECEIVER	0xc101  #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST  0xc110 @@ -377,23 +386,23 @@  #define USB_VENDOR_ID_MONTEREY		0x0566  #define USB_DEVICE_ID_GENIUS_KB29E	0x3004 +#define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400 +#define USB_DEVICE_ID_N_S_HARMONY	0xc359 + +#define USB_VENDOR_ID_NATSU		0x08b7 +#define USB_DEVICE_ID_NATSU_GAMEPAD	0x0001 +  #define USB_VENDOR_ID_NCR		0x0404  #define USB_DEVICE_ID_NCR_FIRST		0x0300  #define USB_DEVICE_ID_NCR_LAST		0x03ff -#define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400 -#define USB_DEVICE_ID_N_S_HARMONY       0xc359 - -#define USB_VENDOR_ID_NATSU             0x08b7 -#define USB_DEVICE_ID_NATSU_GAMEPAD     0x0001 -  #define USB_VENDOR_ID_NEC		0x073e  #define USB_DEVICE_ID_NEC_USB_GAME_PAD	0x0301  #define USB_VENDOR_ID_NEXTWINDOW	0x1926  #define USB_DEVICE_ID_NEXTWINDOW_TOUCHSCREEN	0x0003 -#define USB_VENDOR_ID_NTRIG                0x1b96 +#define USB_VENDOR_ID_NTRIG		0x1b96  #define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN   0x0001  #define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1   0x0003  #define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2   0x0004 @@ -428,9 +437,12 @@  #define USB_VENDOR_ID_PETALYNX		0x18b1  #define USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE	0x0037 -#define USB_VENDOR_ID_PHILIPS       0x0471 +#define USB_VENDOR_ID_PHILIPS		0x0471  #define USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE 0x0617 +#define USB_VENDOR_ID_PI_ENGINEERING	0x05f3 +#define USB_DEVICE_ID_PI_ENGINEERING_VEC_USB_FOOTPEDAL	0xff +  #define USB_VENDOR_ID_PLAYDOTCOM	0x0b43  #define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII	0x0003 @@ -440,16 +452,16 @@  #define USB_VENDOR_ID_PRODIGE		0x05af  #define USB_DEVICE_ID_PRODIGE_CORDLESS	0x3062 +#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_ROCCAT		0x1e7d  #define USB_DEVICE_ID_ROCCAT_KONE	0x2ced  #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  #define USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE	0x0600 @@ -473,20 +485,23 @@  #define USB_VENDOR_ID_THRUSTMASTER	0x044f -#define USB_VENDOR_ID_TOUCHPACK		0x1bfd -#define USB_DEVICE_ID_TOUCHPACK_RTS	0x1688 +#define USB_VENDOR_ID_TOPSEED		0x0766 +#define USB_DEVICE_ID_TOPSEED_CYBERLINK	0x0204 + +#define USB_VENDOR_ID_TOPSEED2		0x1784 +#define USB_DEVICE_ID_TOPSEED2_RF_COMBO	0x0004  #define USB_VENDOR_ID_TOPMAX		0x0663  #define USB_DEVICE_ID_TOPMAX_COBRAPAD	0x0103 -#define USB_VENDOR_ID_TOPSEED		0x0766 -#define USB_DEVICE_ID_TOPSEED_CYBERLINK	0x0204 +#define USB_VENDOR_ID_TOUCHPACK		0x1bfd +#define USB_DEVICE_ID_TOUCHPACK_RTS	0x1688  #define USB_VENDOR_ID_TURBOX		0x062a  #define USB_DEVICE_ID_TURBOX_KEYBOARD	0x0201 -#define USB_VENDOR_ID_TWINHAN           0x6253 -#define USB_DEVICE_ID_TWINHAN_IR_REMOTE 0x0100 +#define USB_VENDOR_ID_TWINHAN		0x6253 +#define USB_DEVICE_ID_TWINHAN_IR_REMOTE	0x0100  #define USB_VENDOR_ID_UCLOGIC		0x5543  #define USB_DEVICE_ID_UCLOGIC_TABLET_PF1209	0x0042 @@ -501,7 +516,6 @@  #define USB_VENDOR_ID_WACOM		0x056a  #define USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH	0x81 -#define USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH	0xbd  #define USB_VENDOR_ID_WISEGROUP		0x0925  #define USB_DEVICE_ID_SMARTJOY_PLUS	0x0005 @@ -523,9 +537,4 @@  #define USB_VENDOR_ID_ZYDACRON	0x13EC  #define USB_DEVICE_ID_ZYDACRON_REMOTE_CONTROL	0x0006 -#define USB_VENDOR_ID_KYE		0x0458 -#define USB_DEVICE_ID_KYE_ERGO_525V	0x0087 -#define USB_DEVICE_ID_KYE_GPEN_560	0x5003 - -  #endif diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 69d152e16a6..6c03dcc5760 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -199,11 +199,11 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel  		case HID_GD_MOUSE:  		case HID_GD_POINTER:  code += 0x110; break;  		case HID_GD_JOYSTICK: -				      if (code <= 0xf) -					      code += BTN_JOYSTICK; -				      else -					      code += BTN_TRIGGER_HAPPY; -				      break; +				if (code <= 0xf) +					code += BTN_JOYSTICK; +				else +					code += BTN_TRIGGER_HAPPY; +				break;  		case HID_GD_GAMEPAD:  code += 0x130; break;  		default:  			switch (field->physical) { @@ -301,6 +301,9 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel  	case HID_UP_DIGITIZER:  		switch (usage->hid & 0xff) { +		case 0x00: /* Undefined */ +			goto ignore; +  		case 0x30: /* TipPressure */  			if (!test_bit(BTN_TOUCH, input->keybit)) {  				device->quirks |= HID_QUIRK_NOTOUCH; @@ -480,7 +483,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel  	case HID_UP_LOGIVENDOR:  		goto ignore; -	 +  	case HID_UP_PID:  		switch (usage->hid & HID_USAGE) {  		case 0xa4: map_key_clear(BTN_DEAD);	break; @@ -589,9 +592,9 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct  			hat_dir = (value - usage->hat_min) * 8 / (usage->hat_max - usage->hat_min + 1) + 1;  		if (hat_dir < 0 || hat_dir > 8) hat_dir = 0;  		input_event(input, usage->type, usage->code    , hid_hat_to_axis[hat_dir].x); -                input_event(input, usage->type, usage->code + 1, hid_hat_to_axis[hat_dir].y); -                return; -        } +		input_event(input, usage->type, usage->code + 1, hid_hat_to_axis[hat_dir].y); +		return; +	}  	if (usage->hid == (HID_UP_DIGITIZER | 0x003c)) { /* Invert */  		*quirks = value ? (*quirks | HID_QUIRK_INVERT) : (*quirks & ~HID_QUIRK_INVERT); diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index f10d56a15f2..319b0e57ee4 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -30,6 +30,21 @@ static bool emulate_scroll_wheel = true;  module_param(emulate_scroll_wheel, bool, 0644);  MODULE_PARM_DESC(emulate_scroll_wheel, "Emulate a scroll wheel"); +static unsigned int scroll_speed = 32; +static int param_set_scroll_speed(const char *val, struct kernel_param *kp) { +	unsigned long speed; +	if (!val || strict_strtoul(val, 0, &speed) || speed > 63) +		return -EINVAL; +	scroll_speed = speed; +	return 0; +} +module_param_call(scroll_speed, param_set_scroll_speed, param_get_uint, &scroll_speed, 0644); +MODULE_PARM_DESC(scroll_speed, "Scroll speed, value from 0 (slow) to 63 (fast)"); + +static bool scroll_acceleration = false; +module_param(scroll_acceleration, bool, 0644); +MODULE_PARM_DESC(scroll_acceleration, "Accelerate sequential scroll events"); +  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)"); @@ -50,6 +65,8 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie  #define TOUCH_STATE_START 0x30  #define TOUCH_STATE_DRAG  0x40 +#define SCROLL_ACCEL_DEFAULT 7 +  /**   * struct magicmouse_sc - Tracks Magic Mouse-specific data.   * @input: Input device through which we report events. @@ -78,8 +95,10 @@ struct magicmouse_sc {  	struct {  		short x;  		short y; +		short scroll_x;  		short scroll_y;  		u8 size; +		u8 down;  	} touches[16];  	int tracking_ids[16];  }; @@ -141,7 +160,7 @@ static void magicmouse_emit_buttons(struct magicmouse_sc *msc, int state)  	input_report_key(msc->input, BTN_RIGHT, state & 2);  	if (state != last_state) -		msc->scroll_accel = 0; +		msc->scroll_accel = SCROLL_ACCEL_DEFAULT;  }  static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tdata) @@ -152,6 +171,7 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda  	int id = (misc >> 6) & 15;  	int x = x_y << 12 >> 20;  	int y = -(x_y >> 20); +	int down = (tdata[7] & TOUCH_STATE_MASK) != TOUCH_STATE_NONE;  	/* Store tracking ID and other fields. */  	msc->tracking_ids[raw_id] = id; @@ -160,42 +180,54 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda  	msc->touches[id].size = misc & 63;  	/* If requested, emulate a scroll wheel by detecting small -	 * vertical touch motions along the middle of the mouse. +	 * vertical touch motions.  	 */ -	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, -		}; +	if (emulate_scroll_wheel) {  		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; +		int step_x = msc->touches[id].scroll_x - x; +		int step_y = msc->touches[id].scroll_y - y;  		/* Calculate and apply the scroll motion. */  		switch (tdata[7] & TOUCH_STATE_MASK) {  		case TOUCH_STATE_START: +			msc->touches[id].scroll_x = x;  			msc->touches[id].scroll_y = y; -			msc->scroll_accel = min_t(int, msc->scroll_accel + 1, -						ARRAY_SIZE(accel_profile) - 1); + +			/* Reset acceleration after half a second. */ +			if (scroll_acceleration && time_before(now, +						msc->scroll_jiffies + HZ / 2)) +				msc->scroll_accel = max_t(int, +						msc->scroll_accel - 1, 1); +			else +				msc->scroll_accel = SCROLL_ACCEL_DEFAULT; +  			break;  		case TOUCH_STATE_DRAG: -			step = step / accel_profile[msc->scroll_accel]; -			if (step != 0) { -				msc->touches[id].scroll_y = y; +			step_x /= (64 - (int)scroll_speed) * msc->scroll_accel; +			if (step_x != 0) { +				msc->touches[id].scroll_x -= step_x * +					(64 - scroll_speed) * msc->scroll_accel;  				msc->scroll_jiffies = now; -				input_report_rel(input, REL_WHEEL, step); +				input_report_rel(input, REL_HWHEEL, -step_x); +			} + +			step_y /= (64 - (int)scroll_speed) * msc->scroll_accel; +			if (step_y != 0) { +				msc->touches[id].scroll_y -= step_y * +					(64 - scroll_speed) * msc->scroll_accel; +				msc->scroll_jiffies = now; +				input_report_rel(input, REL_WHEEL, step_y);  			}  			break;  		}  	}  	/* Generate the input events for this touch. */ -	if (report_touches) { +	if (report_touches && down) {  		int orientation = (misc >> 10) - 32; +		msc->touches[id].down = 1; +  		input_report_abs(input, ABS_MT_TRACKING_ID, id);  		input_report_abs(input, ABS_MT_TOUCH_MAJOR, tdata[3]);  		input_report_abs(input, ABS_MT_TOUCH_MINOR, tdata[4]); @@ -215,7 +247,7 @@ static int magicmouse_raw_event(struct hid_device *hdev,  {  	struct magicmouse_sc *msc = hid_get_drvdata(hdev);  	struct input_dev *input = msc->input; -	int x, y, ts, ii, clicks; +	int x, y, ts, ii, clicks, last_up;  	switch (data[0]) {  	case 0x10: @@ -235,12 +267,26 @@ static int magicmouse_raw_event(struct hid_device *hdev,  		msc->ntouches = (size - 6) / 8;  		for (ii = 0; ii < msc->ntouches; ii++)  			magicmouse_emit_touch(msc, ii, data + ii * 8 + 6); + +		if (report_touches) { +			last_up = 1; +			for (ii = 0; ii < ARRAY_SIZE(msc->touches); ii++) { +				if (msc->touches[ii].down) { +					last_up = 0; +					msc->touches[ii].down = 0; +				} +			} +			if (last_up) { +				input_mt_sync(input); +			} +		} +  		/* 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]; +		x = (int)(((data[3] & 0x0c) << 28) | (data[1] << 22)) >> 22; +		y = (int)(((data[3] & 0x30) << 26) | (data[2] << 22)) >> 22;  		clicks = data[3];  		break;  	case 0x20: /* Theoretically battery status (0-100), but I have @@ -301,8 +347,10 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h  	__set_bit(EV_REL, input->evbit);  	__set_bit(REL_X, input->relbit);  	__set_bit(REL_Y, input->relbit); -	if (emulate_scroll_wheel) +	if (emulate_scroll_wheel) {  		__set_bit(REL_WHEEL, input->relbit); +		__set_bit(REL_HWHEEL, input->relbit); +	}  	if (report_touches) {  		__set_bit(EV_ABS, input->evbit); @@ -345,6 +393,8 @@ static int magicmouse_probe(struct hid_device *hdev,  		return -ENOMEM;  	} +	msc->scroll_accel = SCROLL_ACCEL_DEFAULT; +  	msc->quirks = id->driver_data;  	hid_set_drvdata(hdev, msc); diff --git a/drivers/hid/hid-picolcd.c b/drivers/hid/hid-picolcd.c index 7aabf65c48e..346f0e34987 100644 --- a/drivers/hid/hid-picolcd.c +++ b/drivers/hid/hid-picolcd.c @@ -127,6 +127,26 @@ static const struct fb_var_screeninfo picolcdfb_var = {  	.height         = 26,  	.bits_per_pixel = 1,  	.grayscale      = 1, +	.red            = { +		.offset = 0, +		.length = 1, +		.msb_right = 0, +	}, +	.green          = { +		.offset = 0, +		.length = 1, +		.msb_right = 0, +	}, +	.blue           = { +		.offset = 0, +		.length = 1, +		.msb_right = 0, +	}, +	.transp         = { +		.offset = 0, +		.length = 0, +		.msb_right = 0, +	},  };  #endif /* CONFIG_HID_PICOLCD_FB */ @@ -188,6 +208,7 @@ struct picolcd_data {  	/* Framebuffer stuff */  	u8 fb_update_rate;  	u8 fb_bpp; +	u8 fb_force;  	u8 *fb_vbitmap;		/* local copy of what was sent to PicoLCD */  	u8 *fb_bitmap;		/* framebuffer */  	struct fb_info *fb_info; @@ -346,7 +367,7 @@ static int picolcd_fb_update_tile(u8 *vbitmap, const u8 *bitmap, int bpp,  			const u8 *bdata = bitmap + tile * 256 + chip * 8 + b * 32;  			for (i = 0; i < 64; i++) {  				tdata[i] <<= 1; -				tdata[i] |= (bdata[i/8] >> (7 - i % 8)) & 0x01; +				tdata[i] |= (bdata[i/8] >> (i % 8)) & 0x01;  			}  		}  	} else if (bpp == 8) { @@ -399,13 +420,10 @@ static int picolcd_fb_reset(struct picolcd_data *data, int clear)  	if (data->fb_bitmap) {  		if (clear) { -			memset(data->fb_vbitmap, 0xff, PICOLCDFB_SIZE); +			memset(data->fb_vbitmap, 0, PICOLCDFB_SIZE);  			memset(data->fb_bitmap, 0, PICOLCDFB_SIZE*data->fb_bpp); -		} else { -			/* invert 1 byte in each tile to force resend */ -			for (i = 0; i < PICOLCDFB_SIZE; i += 64) -				data->fb_vbitmap[i] = ~data->fb_vbitmap[i];  		} +		data->fb_force = 1;  	}  	/* schedule first output of framebuffer */ @@ -421,6 +439,9 @@ static void picolcd_fb_update(struct picolcd_data *data)  	int chip, tile, n;  	unsigned long flags; +	if (!data) +		return; +  	spin_lock_irqsave(&data->lock, flags);  	if (!(data->status & PICOLCD_READY_FB)) {  		spin_unlock_irqrestore(&data->lock, flags); @@ -440,14 +461,18 @@ static void picolcd_fb_update(struct picolcd_data *data)  	for (chip = 0; chip < 4; chip++)  		for (tile = 0; tile < 8; tile++)  			if (picolcd_fb_update_tile(data->fb_vbitmap, -					data->fb_bitmap, data->fb_bpp, chip, tile)) { +					data->fb_bitmap, data->fb_bpp, chip, tile) || +				data->fb_force) {  				n += 2; +				if (!data->fb_info->par) +					return; /* device lost! */  				if (n >= HID_OUTPUT_FIFO_SIZE / 2) {  					usbhid_wait_io(data->hdev);  					n = 0;  				}  				picolcd_fb_send_tile(data->hdev, chip, tile);  			} +	data->fb_force = false;  	if (n)  		usbhid_wait_io(data->hdev);  } @@ -511,11 +536,23 @@ static int picolcd_fb_blank(int blank, struct fb_info *info)  static void picolcd_fb_destroy(struct fb_info *info)  {  	struct picolcd_data *data = info->par; +	u32 *ref_cnt = info->pseudo_palette; +	int may_release; +  	info->par = NULL;  	if (data)  		data->fb_info = NULL;  	fb_deferred_io_cleanup(info); -	framebuffer_release(info); + +	ref_cnt--; +	mutex_lock(&info->lock); +	(*ref_cnt)--; +	may_release = !ref_cnt; +	mutex_unlock(&info->lock); +	if (may_release) { +		framebuffer_release(info); +		vfree((u8 *)info->fix.smem_start); +	}  }  static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) @@ -526,29 +563,37 @@ static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *i  	/* only allow 1/8 bit depth (8-bit is grayscale) */  	*var = picolcdfb_var;  	var->activate = activate; -	if (bpp >= 8) +	if (bpp >= 8) {  		var->bits_per_pixel = 8; -	else +		var->red.length     = 8; +		var->green.length   = 8; +		var->blue.length    = 8; +	} else {  		var->bits_per_pixel = 1; +		var->red.length     = 1; +		var->green.length   = 1; +		var->blue.length    = 1; +	}  	return 0;  }  static int picolcd_set_par(struct fb_info *info)  {  	struct picolcd_data *data = info->par; -	u8 *o_fb, *n_fb; +	u8 *tmp_fb, *o_fb; +	if (!data) +		return -ENODEV;  	if (info->var.bits_per_pixel == data->fb_bpp)  		return 0;  	/* switch between 1/8 bit depths */  	if (info->var.bits_per_pixel != 1 && info->var.bits_per_pixel != 8)  		return -EINVAL; -	o_fb = data->fb_bitmap; -	n_fb = vmalloc(PICOLCDFB_SIZE*info->var.bits_per_pixel); -	if (!n_fb) +	o_fb   = data->fb_bitmap; +	tmp_fb = kmalloc(PICOLCDFB_SIZE*info->var.bits_per_pixel, GFP_KERNEL); +	if (!tmp_fb)  		return -ENOMEM; -	fb_deferred_io_cleanup(info);  	/* translate FB content to new bits-per-pixel */  	if (info->var.bits_per_pixel == 1) {  		int i, b; @@ -558,24 +603,87 @@ static int picolcd_set_par(struct fb_info *info)  				p <<= 1;  				p |= o_fb[i*8+b] ? 0x01 : 0x00;  			} +			tmp_fb[i] = p;  		} +		memcpy(o_fb, tmp_fb, PICOLCDFB_SIZE);  		info->fix.visual = FB_VISUAL_MONO01;  		info->fix.line_length = PICOLCDFB_WIDTH / 8;  	} else {  		int i; +		memcpy(tmp_fb, o_fb, PICOLCDFB_SIZE);  		for (i = 0; i < PICOLCDFB_SIZE * 8; i++) -			n_fb[i] = o_fb[i/8] & (0x01 << (7 - i % 8)) ? 0xff : 0x00; -		info->fix.visual = FB_VISUAL_TRUECOLOR; +			o_fb[i] = tmp_fb[i/8] & (0x01 << (7 - i % 8)) ? 0xff : 0x00; +		info->fix.visual = FB_VISUAL_DIRECTCOLOR;  		info->fix.line_length = PICOLCDFB_WIDTH;  	} -	data->fb_bitmap   = n_fb; +	kfree(tmp_fb);  	data->fb_bpp      = info->var.bits_per_pixel; -	info->screen_base = (char __force __iomem *)n_fb; -	info->fix.smem_start = (unsigned long)n_fb; -	info->fix.smem_len   = PICOLCDFB_SIZE*data->fb_bpp; -	fb_deferred_io_init(info); -	vfree(o_fb); +	return 0; +} + +/* Do refcounting on our FB and cleanup per worker if FB is + * closed after unplug of our device + * (fb_release holds info->lock and still touches info after + *  we return so we can't release it immediately. + */ +struct picolcd_fb_cleanup_item { +	struct fb_info *info; +	struct picolcd_fb_cleanup_item *next; +}; +static struct picolcd_fb_cleanup_item *fb_pending; +DEFINE_SPINLOCK(fb_pending_lock); + +static void picolcd_fb_do_cleanup(struct work_struct *data) +{ +	struct picolcd_fb_cleanup_item *item; +	unsigned long flags; + +	do { +		spin_lock_irqsave(&fb_pending_lock, flags); +		item = fb_pending; +		fb_pending = item ? item->next : NULL; +		spin_unlock_irqrestore(&fb_pending_lock, flags); + +		if (item) { +			u8 *fb = (u8 *)item->info->fix.smem_start; +			/* make sure we do not race against fb core when +			 * releasing */ +			mutex_lock(&item->info->lock); +			mutex_unlock(&item->info->lock); +			framebuffer_release(item->info); +			vfree(fb); +		} +	} while (item); +} + +DECLARE_WORK(picolcd_fb_cleanup, picolcd_fb_do_cleanup); + +static int picolcd_fb_open(struct fb_info *info, int u) +{ +	u32 *ref_cnt = info->pseudo_palette; +	ref_cnt--; + +	(*ref_cnt)++; +	return 0; +} + +static int picolcd_fb_release(struct fb_info *info, int u) +{ +	u32 *ref_cnt = info->pseudo_palette; +	ref_cnt--; + +	(*ref_cnt)++; +	if (!*ref_cnt) { +		unsigned long flags; +		struct picolcd_fb_cleanup_item *item = (struct picolcd_fb_cleanup_item *)ref_cnt; +		item--; +		spin_lock_irqsave(&fb_pending_lock, flags); +		item->next = fb_pending; +		fb_pending = item; +		spin_unlock_irqrestore(&fb_pending_lock, flags); +		schedule_work(&picolcd_fb_cleanup); +	}  	return 0;  } @@ -583,6 +691,8 @@ static int picolcd_set_par(struct fb_info *info)  static struct fb_ops picolcdfb_ops = {  	.owner        = THIS_MODULE,  	.fb_destroy   = picolcd_fb_destroy, +	.fb_open      = picolcd_fb_open, +	.fb_release   = picolcd_fb_release,  	.fb_read      = fb_sys_read,  	.fb_write     = picolcd_fb_write,  	.fb_blank     = picolcd_fb_blank, @@ -660,11 +770,12 @@ static int picolcd_init_framebuffer(struct picolcd_data *data)  {  	struct device *dev = &data->hdev->dev;  	struct fb_info *info = NULL; -	int error = -ENOMEM; +	int i, error = -ENOMEM;  	u8 *fb_vbitmap = NULL;  	u8 *fb_bitmap  = NULL; +	u32 *palette; -	fb_bitmap = vmalloc(PICOLCDFB_SIZE*picolcdfb_var.bits_per_pixel); +	fb_bitmap = vmalloc(PICOLCDFB_SIZE*8);  	if (fb_bitmap == NULL) {  		dev_err(dev, "can't get a free page for framebuffer\n");  		goto err_nomem; @@ -678,18 +789,29 @@ static int picolcd_init_framebuffer(struct picolcd_data *data)  	data->fb_update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT;  	data->fb_defio = picolcd_fb_defio; -	info = framebuffer_alloc(0, dev); +	/* The extra memory is: +	 * - struct picolcd_fb_cleanup_item +	 * - u32 for ref_count +	 * - 256*u32 for pseudo_palette +	 */ +	info = framebuffer_alloc(257 * sizeof(u32) + sizeof(struct picolcd_fb_cleanup_item), dev);  	if (info == NULL) {  		dev_err(dev, "failed to allocate a framebuffer\n");  		goto err_nomem;  	} +	palette  = info->par + sizeof(struct picolcd_fb_cleanup_item); +	*palette = 1; +	palette++; +	for (i = 0; i < 256; i++) +		palette[i] = i > 0 && i < 16 ? 0xff : 0; +	info->pseudo_palette = palette;  	info->fbdefio = &data->fb_defio;  	info->screen_base = (char __force __iomem *)fb_bitmap;  	info->fbops = &picolcdfb_ops;  	info->var = picolcdfb_var;  	info->fix = picolcdfb_fix; -	info->fix.smem_len   = PICOLCDFB_SIZE; +	info->fix.smem_len   = PICOLCDFB_SIZE*8;  	info->fix.smem_start = (unsigned long)fb_bitmap;  	info->par = data;  	info->flags = FBINFO_FLAG_DEFAULT; @@ -707,18 +829,20 @@ static int picolcd_init_framebuffer(struct picolcd_data *data)  		dev_err(dev, "failed to create sysfs attributes\n");  		goto err_cleanup;  	} +	fb_deferred_io_init(info);  	data->fb_info    = info;  	error = register_framebuffer(info);  	if (error) {  		dev_err(dev, "failed to register framebuffer\n");  		goto err_sysfs;  	} -	fb_deferred_io_init(info);  	/* schedule first output of framebuffer */ +	data->fb_force = 1;  	schedule_delayed_work(&info->deferred_work, 0);  	return 0;  err_sysfs: +	fb_deferred_io_cleanup(info);  	device_remove_file(dev, &dev_attr_fb_update_rate);  err_cleanup:  	data->fb_vbitmap = NULL; @@ -737,19 +861,17 @@ static void picolcd_exit_framebuffer(struct picolcd_data *data)  {  	struct fb_info *info = data->fb_info;  	u8 *fb_vbitmap = data->fb_vbitmap; -	u8 *fb_bitmap  = data->fb_bitmap;  	if (!info)  		return; +	info->par = NULL; +	device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate); +	unregister_framebuffer(info);  	data->fb_vbitmap = NULL;  	data->fb_bitmap  = NULL;  	data->fb_bpp     = 0;  	data->fb_info    = NULL; -	device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate); -	fb_deferred_io_cleanup(info); -	unregister_framebuffer(info); -	vfree(fb_bitmap);  	kfree(fb_vbitmap);  } @@ -2566,6 +2688,13 @@ static void picolcd_remove(struct hid_device *hdev)  	spin_lock_irqsave(&data->lock, flags);  	data->status |= PICOLCD_FAILED;  	spin_unlock_irqrestore(&data->lock, flags); +#ifdef CONFIG_HID_PICOLCD_FB +	/* short-circuit FB as early as possible in order to +	 * avoid long delays if we host console. +	 */ +	if (data->fb_info) +		data->fb_info->par = NULL; +#endif  	picolcd_exit_devfs(data);  	device_remove_file(&hdev->dev, &dev_attr_operation_mode); @@ -2623,6 +2752,10 @@ static int __init picolcd_init(void)  static void __exit picolcd_exit(void)  {  	hid_unregister_driver(&picolcd_driver); +#ifdef CONFIG_HID_PICOLCD_FB +	flush_scheduled_work(); +	WARN_ON(fb_pending); +#endif  }  module_init(picolcd_init); diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c index 17f2dc04f88..f77695762cb 100644 --- a/drivers/hid/hid-roccat-kone.c +++ b/drivers/hid/hid-roccat-kone.c @@ -22,11 +22,6 @@   *      Is it possible to remove and reinstall the urb in raw-event- or any   *      other handler, or to defer this action to be executed somewhere else?   * - * TODO implement notification mechanism for overlong macro execution - *      If user wants to execute an overlong macro only the names of macroset - *      and macro are given. Should userland tap hidraw or is there an - *      additional streaming mechanism? - *   * TODO is it possible to overwrite group for sysfs attributes via udev?   */ @@ -277,7 +272,7 @@ static ssize_t kone_sysfs_read_settings(struct file *fp, struct kobject *kobj,  		count = sizeof(struct kone_settings) - off;  	mutex_lock(&kone->kone_lock); -	memcpy(buf, &kone->settings + off, count); +	memcpy(buf, ((char const *)&kone->settings) + off, count);  	mutex_unlock(&kone->kone_lock);  	return count; @@ -337,7 +332,7 @@ static ssize_t kone_sysfs_read_profilex(struct kobject *kobj,  		count = sizeof(struct kone_profile) - off;  	mutex_lock(&kone->kone_lock); -	memcpy(buf, &kone->profiles[number - 1], sizeof(struct kone_profile)); +	memcpy(buf, ((char const *)&kone->profiles[number - 1]) + off, count);  	mutex_unlock(&kone->kone_lock);  	return count; @@ -623,18 +618,6 @@ static ssize_t kone_sysfs_set_startup_profile(struct device *dev,  }  /* - * This file is used by userland software to find devices that are handled by - * this driver. This provides a consistent way for actual and older kernels - * where this driver replaced usbhid instead of generic-usb. - * Driver capabilities are determined by version number. - */ -static ssize_t kone_sysfs_show_driver_version(struct device *dev, -		struct device_attribute *attr, char *buf) -{ -	return snprintf(buf, PAGE_SIZE, ROCCAT_KONE_DRIVER_VERSION "\n"); -} - -/*   * Read actual dpi settings.   * Returns raw value for further processing. Refer to enum kone_polling_rates to   * get real value. @@ -671,9 +654,6 @@ static DEVICE_ATTR(startup_profile, 0660,  		kone_sysfs_show_startup_profile,  		kone_sysfs_set_startup_profile); -static DEVICE_ATTR(kone_driver_version, 0440, -		kone_sysfs_show_driver_version, NULL); -  static struct attribute *kone_attributes[] = {  		&dev_attr_actual_dpi.attr,  		&dev_attr_actual_profile.attr, @@ -681,7 +661,6 @@ static struct attribute *kone_attributes[] = {  		&dev_attr_firmware_version.attr,  		&dev_attr_tcu.attr,  		&dev_attr_startup_profile.attr, -		&dev_attr_kone_driver_version.attr,  		NULL  }; diff --git a/drivers/hid/hid-roccat-kone.h b/drivers/hid/hid-roccat-kone.h index 003e6f81c19..130d6566ea8 100644 --- a/drivers/hid/hid-roccat-kone.h +++ b/drivers/hid/hid-roccat-kone.h @@ -14,8 +14,6 @@  #include <linux/types.h> -#define ROCCAT_KONE_DRIVER_VERSION "v0.3.1" -  #pragma pack(push)  #pragma pack(1) diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c index e05d48edb66..f6e80c7ca61 100644 --- a/drivers/hid/hid-roccat.c +++ b/drivers/hid/hid-roccat.c @@ -168,7 +168,7 @@ static int roccat_open(struct inode *inode, struct file *file)  		printk(KERN_EMERG "roccat device with minor %d doesn't exist\n",  				minor);  		error = -ENODEV; -		goto exit_unlock; +		goto exit_err;  	}  	if (!device->open++) { @@ -178,7 +178,7 @@ static int roccat_open(struct inode *inode, struct file *file)  					PM_HINT_FULLON);  			if (error < 0) {  				--device->open; -				goto exit_unlock; +				goto exit_err;  			}  		}  		error = device->hid->ll_driver->open(device->hid); @@ -187,7 +187,7 @@ static int roccat_open(struct inode *inode, struct file *file)  				device->hid->ll_driver->power(device->hid,  						PM_HINT_NORMAL);  			--device->open; -			goto exit_unlock; +			goto exit_err;  		}  	} @@ -202,6 +202,9 @@ exit_unlock:  	mutex_unlock(&device->readers_lock);  	mutex_unlock(&devices_lock);  	return error; +exit_err: +	kfree(reader); +	goto exit_unlock;  }  static int roccat_release(struct inode *inode, struct file *file) diff --git a/drivers/hid/hid-roccat.h b/drivers/hid/hid-roccat.h index d8aae0c1fa7..09e864e9f79 100644 --- a/drivers/hid/hid-roccat.h +++ b/drivers/hid/hid-roccat.h @@ -15,7 +15,7 @@  #include <linux/hid.h>  #include <linux/types.h> -#if defined(CONFIG_HID_ROCCAT) || defined (CONFIG_HID_ROCCAT_MODULE) +#if defined(CONFIG_HID_ROCCAT) || defined(CONFIG_HID_ROCCAT_MODULE)  int roccat_connect(struct hid_device *hid);  void roccat_disconnect(int minor);  int roccat_report_event(int minor, u8 const *data, int len); diff --git a/drivers/hid/hid-topseed.c b/drivers/hid/hid-topseed.c index 2eebdcc57bc..5771f851f85 100644 --- a/drivers/hid/hid-topseed.c +++ b/drivers/hid/hid-topseed.c @@ -6,6 +6,9 @@   *   *  Modified to also support BTC "Emprex 3009URF III Vista MCE Remote" by   *  Wayne Thomas 2010. + * + *  Modified to support Conceptronic CLLRCMCE by + *  Kees Bakker 2010.   */  /* @@ -34,6 +37,7 @@ static int ts_input_mapping(struct hid_device *hdev, struct hid_input *hi,  	case 0x00d: ts_map_key_clear(KEY_MEDIA);	break;  	case 0x024: ts_map_key_clear(KEY_MENU);		break;  	case 0x025: ts_map_key_clear(KEY_TV);		break; +	case 0x027: ts_map_key_clear(KEY_MODE);		break;  	case 0x031: ts_map_key_clear(KEY_AUDIO);	break;  	case 0x032: ts_map_key_clear(KEY_TEXT);		break;  	case 0x033: ts_map_key_clear(KEY_CHANNEL);	break; @@ -60,6 +64,7 @@ static int ts_input_mapping(struct hid_device *hdev, struct hid_input *hi,  static const struct hid_device_id ts_devices[] = {  	{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) },  	{ HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE) }, +	{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED2, USB_DEVICE_ID_TOPSEED2_RF_COMBO) },  	{ }  };  MODULE_DEVICE_TABLE(hid, ts_devices); diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c index 1e051f1171e..807dcd1555a 100644 --- a/drivers/hid/hid-wacom.c +++ b/drivers/hid/hid-wacom.c @@ -436,7 +436,7 @@ static void wacom_remove(struct hid_device *hdev)  static const struct hid_device_id wacom_devices[] = {  	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) }, -	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) }, +  	{ }  };  MODULE_DEVICE_TABLE(hid, wacom_devices); diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 3ccd4785067..47d70c523d9 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -46,7 +46,6 @@ static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count,  {  	struct hidraw_list *list = file->private_data;  	int ret = 0, len; -	char *report;  	DECLARE_WAITQUEUE(wait, current);  	mutex_lock(&list->read_mutex); @@ -84,7 +83,6 @@ static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count,  		if (ret)  			goto out; -		report = list->buffer[list->tail].value;  		len = list->buffer[list->tail].len > count ?  			count : list->buffer[list->tail].len; diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 5f5aa39b398..2643d314762 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -75,6 +75,8 @@ static const struct hid_blacklist {  	{ USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },  	{ USB_VENDOR_ID_WISEGROUP_LTD2, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, +	{ USB_VENDOR_ID_PI_ENGINEERING, USB_DEVICE_ID_PI_ENGINEERING_VEC_USB_FOOTPEDAL, HID_QUIRK_HIDINPUT_FORCE }, +  	{ 0, 0 }  }; diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index c24d2fa3e3b..254a003af04 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -67,7 +67,7 @@ struct hiddev_list {  	struct mutex thread_lock;  }; -static struct hiddev *hiddev_table[HIDDEV_MINORS]; +static struct usb_driver hiddev_driver;  /*   * Find a report, given the report's type and ID.  The ID can be specified @@ -265,22 +265,19 @@ static int hiddev_release(struct inode * inode, struct file * file)  static int hiddev_open(struct inode *inode, struct file *file)  {  	struct hiddev_list *list; -	int res, i; - -	/* See comment in hiddev_connect() for BKL explanation */ -	lock_kernel(); -	i = iminor(inode) - HIDDEV_MINOR_BASE; +	struct usb_interface *intf; +	struct hiddev *hiddev; +	int res; -	if (i >= HIDDEV_MINORS || i < 0 || !hiddev_table[i]) +	intf = usb_find_interface(&hiddev_driver, iminor(inode)); +	if (!intf)  		return -ENODEV; +	hiddev = usb_get_intfdata(intf);  	if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL)))  		return -ENOMEM;  	mutex_init(&list->thread_lock); - -	list->hiddev = hiddev_table[i]; - - +	list->hiddev = hiddev;  	file->private_data = list;  	/* @@ -289,7 +286,7 @@ static int hiddev_open(struct inode *inode, struct file *file)  	 */  	if (list->hiddev->exist) {  		if (!list->hiddev->open++) { -			res = usbhid_open(hiddev_table[i]->hid); +			res = usbhid_open(hiddev->hid);  			if (res < 0) {  				res = -EIO;  				goto bail; @@ -301,12 +298,12 @@ static int hiddev_open(struct inode *inode, struct file *file)  	}  	spin_lock_irq(&list->hiddev->list_lock); -	list_add_tail(&list->node, &hiddev_table[i]->list); +	list_add_tail(&list->node, &hiddev->list);  	spin_unlock_irq(&list->hiddev->list_lock);  	if (!list->hiddev->open++)  		if (list->hiddev->exist) { -			struct hid_device *hid = hiddev_table[i]->hid; +			struct hid_device *hid = hiddev->hid;  			res = usbhid_get_power(hid);  			if (res < 0) {  				res = -EIO; @@ -314,13 +311,10 @@ static int hiddev_open(struct inode *inode, struct file *file)  			}  			usbhid_open(hid);  		} - -	unlock_kernel();  	return 0;  bail:  	file->private_data = NULL;  	kfree(list); -	unlock_kernel();  	return res;  } @@ -894,37 +888,14 @@ int hiddev_connect(struct hid_device *hid, unsigned int force)  	hid->hiddev = hiddev;  	hiddev->hid = hid;  	hiddev->exist = 1; - -	/* -	 * BKL here is used to avoid race after usb_register_dev(). -	 * Once the device node has been created, open() could happen on it. -	 * The code below will then fail, as hiddev_table hasn't been -	 * updated. -	 * -	 * The obvious fix -- introducing mutex to guard hiddev_table[] -	 * doesn't work, as usb_open() and usb_register_dev() both take -	 * minor_rwsem, thus we'll have ABBA deadlock. -	 * -	 * Before BKL pushdown, usb_open() had been acquiring it in right -	 * order, so _open() was safe to use it to protect from this race. -	 * Now the order is different, but AB-BA deadlock still doesn't occur -	 * as BKL is dropped on schedule() (i.e. while sleeping on -	 * minor_rwsem). Fugly. -	 */ -	lock_kernel(); +	usb_set_intfdata(usbhid->intf, usbhid);  	retval = usb_register_dev(usbhid->intf, &hiddev_class);  	if (retval) {  		err_hid("Not able to get a minor for this device.");  		hid->hiddev = NULL; -		unlock_kernel();  		kfree(hiddev);  		return -1; -	} else { -		hid->minor = usbhid->intf->minor; -		hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev;  	} -	unlock_kernel(); -  	return 0;  } @@ -942,7 +913,6 @@ void hiddev_disconnect(struct hid_device *hid)  	hiddev->exist = 0;  	mutex_unlock(&hiddev->existancelock); -	hiddev_table[hiddev->hid->minor - HIDDEV_MINOR_BASE] = NULL;  	usb_deregister_dev(usbhid->intf, &hiddev_class);  	if (hiddev->open) { diff --git a/include/linux/hid.h b/include/linux/hid.h index 895001f7f4b..42a0f1d1136 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -311,6 +311,7 @@ struct hid_item {  #define HID_QUIRK_HIDDEV_FORCE			0x00000010  #define HID_QUIRK_BADPAD			0x00000020  #define HID_QUIRK_MULTI_INPUT			0x00000040 +#define HID_QUIRK_HIDINPUT_FORCE		0x00000080  #define HID_QUIRK_SKIP_OUTPUT_REPORTS		0x00010000  #define HID_QUIRK_FULLSPEED_INTERVAL		0x10000000  #define HID_QUIRK_NO_INIT_REPORTS		0x20000000  |