diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-04 15:23:49 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-04 15:23:49 -0700 |
commit | 5cf65713f87775c548e3eb48dbafa32e12f28000 (patch) | |
tree | 117442e28cddebb79246b1c4b871428e50b27fb1 | |
parent | b7c8e55db7141dcbb9d5305a3260fa0ed62a1bcc (diff) | |
parent | 9f17d516416c88dfe18f4deee508fce763ddedb5 (diff) | |
download | linux-next-5cf65713f87775c548e3eb48dbafa32e12f28000.tar.gz |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (30 commits)
Revert "HID: add support for the Wacom Intuos 4 wireless"
HID: fix up Kconfig entry for ACRUX driver
HID: add ACRUX game controller force feedback support
HID: Force input registration for "VEC footpedal"
HID: add HID_QUIRK_HIDINPUT_FORCE
HID: hid-input.c: indentation fixes
HID: hiddev: use usb_find_interface, get rid of BKL
HID: ignore digitizer usage Undefined (0x00)
HID: Add support for Conceptronic CLLRCMCE
HID: hid-ids.h: Whitespace fixup, align using TABs
HID: picolcd: implement refcounting of framebuffer
HID: picolcd: do not reallocate memory on depth change
HID: picolcd: Add minimal palette required by fbcon on 8bpp
HID: magicmouse: Correct parsing of large X and Y motions.
HID: magicmouse: report last touch up
HID: picolcd: fix deferred_io init/cleanup to fb ordering
HID: hid-ids.h: keep vendor ids in alphabetical order
HID: add proper support for Elecom BM084 bluetooth mouse
HID: magicmouse: enable horizontal scrolling
HID: magicmouse: add param for scroll speed
...
-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 88340a23ce91..063bda7fe707 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 434099369058..6369ba7f96f8 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 987fa0627367..46f037f3df80 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 000000000000..f42ee140738a --- /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 b54a9a608ac2..e635199a0cd2 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 000000000000..7a40878f46b4 --- /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 8aee2577c1ad..d3fc13ae094d 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 69d152e16a6a..6c03dcc5760a 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 f10d56a15f21..319b0e57ee41 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 7aabf65c48ef..346f0e34987e 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 17f2dc04f883..f77695762cb5 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 003e6f81c195..130d6566ea82 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 e05d48edb66f..f6e80c7ca61e 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 d8aae0c1fa7e..09e864e9f79d 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 2eebdcc57bcf..5771f851f856 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 1e051f1171e4..807dcd1555a6 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 3ccd47850677..47d70c523d93 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 5f5aa39b3988..2643d3147621 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 c24d2fa3e3b6..254a003af048 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 895001f7f4b2..42a0f1d11365 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 |