summaryrefslogtreecommitdiff
path: root/drivers/hid
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/Kconfig70
-rw-r--r--drivers/hid/Makefile5
-rw-r--r--drivers/hid/hid-bright.c71
-rw-r--r--drivers/hid/hid-core.c120
-rw-r--r--drivers/hid/hid-dell.c76
-rw-r--r--drivers/hid/hid-dummy.c6
-rw-r--r--drivers/hid/hid-gaff.c185
-rw-r--r--drivers/hid/hid-ids.h21
-rw-r--r--drivers/hid/hid-lg.c7
-rw-r--r--drivers/hid/hid-ntrig.c82
-rw-r--r--drivers/hid/hid-sony.c2
-rw-r--r--drivers/hid/hid-topseed.c77
-rw-r--r--drivers/hid/hidraw.c32
-rw-r--r--drivers/hid/usbhid/Kconfig2
-rw-r--r--drivers/hid/usbhid/hid-core.c43
-rw-r--r--drivers/hid/usbhid/hid-quirks.c1
-rw-r--r--drivers/hid/usbhid/hiddev.c137
-rw-r--r--drivers/hid/usbhid/usbhid.h10
18 files changed, 682 insertions, 265 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index b4fd8ca701a4..e85c8fe9ffcf 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -85,14 +85,14 @@ config HID_COMPAT
config HID_A4TECH
tristate "A4 tech" if EMBEDDED
depends on USB_HID
- default y
+ default !EMBEDDED
---help---
Support for A4 tech X5 and WOP-35 / Trust 450L mice.
config HID_APPLE
tristate "Apple" if EMBEDDED
depends on (USB_HID || BT_HIDP)
- default y
+ default !EMBEDDED
---help---
Support for some Apple devices which less or more break
HID specification.
@@ -103,64 +103,49 @@ config HID_APPLE
config HID_BELKIN
tristate "Belkin" if EMBEDDED
depends on USB_HID
- default y
+ default !EMBEDDED
---help---
Support for Belkin Flip KVM and Wireless keyboard.
-config HID_BRIGHT
- tristate "Bright" if EMBEDDED
- depends on USB_HID
- default y
- ---help---
- Support for Bright ABNT-2 keyboard.
-
config HID_CHERRY
tristate "Cherry" if EMBEDDED
depends on USB_HID
- default y
+ default !EMBEDDED
---help---
Support for Cherry Cymotion keyboard.
config HID_CHICONY
tristate "Chicony" if EMBEDDED
depends on USB_HID
- default y
+ default !EMBEDDED
---help---
Support for Chicony Tactical pad.
config HID_CYPRESS
tristate "Cypress" if EMBEDDED
depends on USB_HID
- default y
+ default !EMBEDDED
---help---
Support for cypress mouse and barcode readers.
-config HID_DELL
- tristate "Dell" if EMBEDDED
- depends on USB_HID
- default y
- ---help---
- Support for quirky Dell HID hardware that require
- special LED handling (W7658 and SK8115 models)
-
config HID_EZKEY
tristate "Ezkey" if EMBEDDED
depends on USB_HID
- default y
+ default !EMBEDDED
---help---
Support for Ezkey BTC 8193 keyboard.
config HID_GYRATION
tristate "Gyration" if EMBEDDED
depends on USB_HID
- default y
+ default !EMBEDDED
---help---
Support for Gyration remote control.
config HID_LOGITECH
tristate "Logitech" if EMBEDDED
depends on USB_HID
- default y
+ default !EMBEDDED
---help---
Support for Logitech devices that are not fully compliant with HID standard.
@@ -191,21 +176,28 @@ config LOGIRUMBLEPAD2_FF
config HID_MICROSOFT
tristate "Microsoft" if EMBEDDED
depends on USB_HID
- default y
+ default !EMBEDDED
---help---
Support for Microsoft devices that are not fully compliant with HID standard.
config HID_MONTEREY
tristate "Monterey" if EMBEDDED
depends on USB_HID
- default y
+ default !EMBEDDED
---help---
Support for Monterey Genius KB29E.
+config HID_NTRIG
+ tristate "NTrig" if EMBEDDED
+ depends on USB_HID
+ default !EMBEDDED
+ ---help---
+ Support for N-Trig touch screen.
+
config HID_PANTHERLORD
tristate "Pantherlord devices support" if EMBEDDED
depends on USB_HID
- default y
+ default !EMBEDDED
---help---
Support for PantherLord/GreenAsia based device support.
@@ -220,31 +212,47 @@ config PANTHERLORD_FF
config HID_PETALYNX
tristate "Petalynx" if EMBEDDED
depends on USB_HID
- default y
+ default !EMBEDDED
---help---
Support for Petalynx Maxter remote control.
config HID_SAMSUNG
tristate "Samsung" if EMBEDDED
depends on USB_HID
- default y
+ default !EMBEDDED
---help---
Support for Samsung InfraRed remote control.
config HID_SONY
tristate "Sony" if EMBEDDED
depends on USB_HID
- default y
+ default !EMBEDDED
---help---
Support for Sony PS3 controller.
config HID_SUNPLUS
tristate "Sunplus" if EMBEDDED
depends on USB_HID
- default y
+ default !EMBEDDED
---help---
Support for Sunplus wireless desktop.
+config GREENASIA_FF
+ tristate "GreenAsia (Product ID 0x12) force feedback support"
+ depends on USB_HID
+ select INPUT_FF_MEMLESS
+ ---help---
+ Say Y here if you have a GreenAsia (Product ID 0x12) based game controller
+ (like MANTA Warior MM816 and SpeedLink Strike2 SL-6635) or adapter
+ and want to enable force feedback support for it.
+
+config HID_TOPSEED
+ tristate "TopSeed Cyberlink remote control support" if EMBEDDED
+ depends on USB_HID
+ default !EMBEDDED
+ ---help---
+ Say Y if you have a TopSeed Cyberlink remote control.
+
config THRUSTMASTER_FF
tristate "ThrustMaster devices support"
depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index b09e43e7413e..fbd021f153f1 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -23,22 +23,23 @@ endif
obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o
obj-$(CONFIG_HID_APPLE) += hid-apple.o
obj-$(CONFIG_HID_BELKIN) += hid-belkin.o
-obj-$(CONFIG_HID_BRIGHT) += hid-bright.o
obj-$(CONFIG_HID_CHERRY) += hid-cherry.o
obj-$(CONFIG_HID_CHICONY) += hid-chicony.o
obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o
-obj-$(CONFIG_HID_DELL) += hid-dell.o
obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o
obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o
obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o
+obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o
obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o
obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o
obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o
obj-$(CONFIG_HID_SONY) += hid-sony.o
obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o
+obj-$(CONFIG_GREENASIA_FF) += hid-gaff.o
obj-$(CONFIG_THRUSTMASTER_FF) += hid-tmff.o
+obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o
obj-$(CONFIG_ZEROPLUS_FF) += hid-zpff.o
obj-$(CONFIG_USB_HID) += usbhid/
diff --git a/drivers/hid/hid-bright.c b/drivers/hid/hid-bright.c
deleted file mode 100644
index 38517a117dfd..000000000000
--- a/drivers/hid/hid-bright.c
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * HID driver for some bright "special" devices
- *
- * Copyright (c) 2008 Mauro Carvalho Chehab <mchehab@redhat.com>
- *
- * Based on hid-dell driver
- */
-
-/*
- * 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 int bright_probe(struct hid_device *hdev, const struct hid_device_id *id)
-{
- int ret;
-
- ret = hid_parse(hdev);
- if (ret) {
- dev_err(&hdev->dev, "parse failed\n");
- goto err_free;
- }
-
- ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
- if (ret) {
- dev_err(&hdev->dev, "hw start failed\n");
- goto err_free;
- }
-
- usbhid_set_leds(hdev);
-
- return 0;
-err_free:
- return ret;
-}
-
-static const struct hid_device_id bright_devices[] = {
- { HID_USB_DEVICE(USB_VENDOR_ID_BRIGHT, USB_DEVICE_ID_BRIGHT_ABNT2) },
- { }
-};
-MODULE_DEVICE_TABLE(hid, bright_devices);
-
-static struct hid_driver bright_driver = {
- .name = "bright",
- .id_table = bright_devices,
- .probe = bright_probe,
-};
-
-static int bright_init(void)
-{
- return hid_register_driver(&bright_driver);
-}
-
-static void bright_exit(void)
-{
- hid_unregister_driver(&bright_driver);
-}
-
-module_init(bright_init);
-module_exit(bright_exit);
-MODULE_LICENSE("GPL");
-
-HID_COMPAT_LOAD_DRIVER(bright);
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 40df3e1b4bd1..5d7640e49dc5 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1256,19 +1256,16 @@ static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM) },
- { HID_USB_DEVICE(USB_VENDOR_ID_BRIGHT, USB_DEVICE_ID_BRIGHT_ABNT2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) },
- { HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658) },
- { HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_SK8115) },
{ HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) },
- { HID_USB_DEVICE(USB_VENDOR_ID_GENERIC_13BA, USB_DEVICE_ID_GENERIC_13BA_KBD_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0012) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) },
@@ -1279,7 +1276,6 @@ static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI) },
- { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D) },
@@ -1297,23 +1293,105 @@ static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 0x030c) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) },
{ }
};
+struct hid_dynid {
+ struct list_head list;
+ struct hid_device_id id;
+};
+
+/**
+ * store_new_id - add a new HID device ID to this driver and re-probe devices
+ * @driver: target device driver
+ * @buf: buffer for scanning device ID data
+ * @count: input size
+ *
+ * Adds a new dynamic hid device ID to this driver,
+ * and causes the driver to probe for all devices again.
+ */
+static ssize_t store_new_id(struct device_driver *drv, const char *buf,
+ size_t count)
+{
+ struct hid_driver *hdrv = container_of(drv, struct hid_driver, driver);
+ struct hid_dynid *dynid;
+ __u32 bus, vendor, product;
+ unsigned long driver_data = 0;
+ int ret;
+
+ ret = sscanf(buf, "%x %x %x %lx",
+ &bus, &vendor, &product, &driver_data);
+ if (ret < 3)
+ return -EINVAL;
+
+ dynid = kzalloc(sizeof(*dynid), GFP_KERNEL);
+ if (!dynid)
+ return -ENOMEM;
+
+ dynid->id.bus = bus;
+ dynid->id.vendor = vendor;
+ dynid->id.product = product;
+ dynid->id.driver_data = driver_data;
+
+ spin_lock(&hdrv->dyn_lock);
+ list_add_tail(&dynid->list, &hdrv->dyn_list);
+ spin_unlock(&hdrv->dyn_lock);
+
+ ret = 0;
+ if (get_driver(&hdrv->driver)) {
+ ret = driver_attach(&hdrv->driver);
+ put_driver(&hdrv->driver);
+ }
+
+ return ret ? : count;
+}
+static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
+
+static void hid_free_dynids(struct hid_driver *hdrv)
+{
+ struct hid_dynid *dynid, *n;
+
+ spin_lock(&hdrv->dyn_lock);
+ list_for_each_entry_safe(dynid, n, &hdrv->dyn_list, list) {
+ list_del(&dynid->list);
+ kfree(dynid);
+ }
+ spin_unlock(&hdrv->dyn_lock);
+}
+
+static const struct hid_device_id *hid_match_device(struct hid_device *hdev,
+ struct hid_driver *hdrv)
+{
+ struct hid_dynid *dynid;
+
+ spin_lock(&hdrv->dyn_lock);
+ list_for_each_entry(dynid, &hdrv->dyn_list, list) {
+ if (hid_match_one_id(hdev, &dynid->id)) {
+ spin_unlock(&hdrv->dyn_lock);
+ return &dynid->id;
+ }
+ }
+ spin_unlock(&hdrv->dyn_lock);
+
+ return hid_match_id(hdev, hdrv->id_table);
+}
+
static int hid_bus_match(struct device *dev, struct device_driver *drv)
{
struct hid_driver *hdrv = container_of(drv, struct hid_driver, driver);
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
- if (!hid_match_id(hdev, hdrv->id_table))
+ if (!hid_match_device(hdev, hdrv))
return 0;
/* generic wants all non-blacklisted */
@@ -1332,7 +1410,7 @@ static int hid_device_probe(struct device *dev)
int ret = 0;
if (!hdev->driver) {
- id = hid_match_id(hdev, hdrv->id_table);
+ id = hid_match_device(hdev, hdrv);
if (id == NULL)
return -ENODEV;
@@ -1420,6 +1498,7 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_CMEDIA, USB_DEVICE_ID_CM109) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_DEALEXTREAME, USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) },
@@ -1577,6 +1656,9 @@ static const struct hid_device_id hid_mouse_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING3_JIS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
{ }
@@ -1618,9 +1700,10 @@ int hid_add_device(struct hid_device *hdev)
if (hid_ignore(hdev))
return -ENODEV;
- /* XXX hack, any other cleaner solution < 20 bus_id bytes? */
- sprintf(hdev->dev.bus_id, "%04X:%04X:%04X.%04X", hdev->bus,
- hdev->vendor, hdev->product, atomic_inc_return(&id));
+ /* XXX hack, any other cleaner solution after the driver core
+ * is converted to allow more than 20 bytes as the device name? */
+ dev_set_name(&hdev->dev, "%04X:%04X:%04X.%04X", hdev->bus,
+ hdev->vendor, hdev->product, atomic_inc_return(&id));
ret = device_add(&hdev->dev);
if (!ret)
@@ -1695,18 +1778,33 @@ EXPORT_SYMBOL_GPL(hid_destroy_device);
int __hid_register_driver(struct hid_driver *hdrv, struct module *owner,
const char *mod_name)
{
+ int ret;
+
hdrv->driver.name = hdrv->name;
hdrv->driver.bus = &hid_bus_type;
hdrv->driver.owner = owner;
hdrv->driver.mod_name = mod_name;
- return driver_register(&hdrv->driver);
+ INIT_LIST_HEAD(&hdrv->dyn_list);
+ spin_lock_init(&hdrv->dyn_lock);
+
+ ret = driver_register(&hdrv->driver);
+ if (ret)
+ return ret;
+
+ ret = driver_create_file(&hdrv->driver, &driver_attr_new_id);
+ if (ret)
+ driver_unregister(&hdrv->driver);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(__hid_register_driver);
void hid_unregister_driver(struct hid_driver *hdrv)
{
+ driver_remove_file(&hdrv->driver, &driver_attr_new_id);
driver_unregister(&hdrv->driver);
+ hid_free_dynids(hdrv);
}
EXPORT_SYMBOL_GPL(hid_unregister_driver);
diff --git a/drivers/hid/hid-dell.c b/drivers/hid/hid-dell.c
deleted file mode 100644
index f5474300b83a..000000000000
--- a/drivers/hid/hid-dell.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * HID driver for some dell "special" devices
- *
- * Copyright (c) 1999 Andreas Gal
- * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
- * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
- * Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
- * Copyright (c) 2008 Jiri Slaby
- */
-
-/*
- * 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 int dell_probe(struct hid_device *hdev, const struct hid_device_id *id)
-{
- int ret;
-
- ret = hid_parse(hdev);
- if (ret) {
- dev_err(&hdev->dev, "parse failed\n");
- goto err_free;
- }
-
- ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
- if (ret) {
- dev_err(&hdev->dev, "hw start failed\n");
- goto err_free;
- }
-
- usbhid_set_leds(hdev);
-
- return 0;
-err_free:
- return ret;
-}
-
-static const struct hid_device_id dell_devices[] = {
- { HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658) },
- { HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_SK8115) },
- { HID_USB_DEVICE(USB_VENDOR_ID_GENERIC_13BA, USB_DEVICE_ID_GENERIC_13BA_KBD_MOUSE) },
- { }
-};
-MODULE_DEVICE_TABLE(hid, dell_devices);
-
-static struct hid_driver dell_driver = {
- .name = "dell",
- .id_table = dell_devices,
- .probe = dell_probe,
-};
-
-static int dell_init(void)
-{
- return hid_register_driver(&dell_driver);
-}
-
-static void dell_exit(void)
-{
- hid_unregister_driver(&dell_driver);
-}
-
-module_init(dell_init);
-module_exit(dell_exit);
-MODULE_LICENSE("GPL");
-
-HID_COMPAT_LOAD_DRIVER(dell);
diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c
index e148f86fb58e..b4cc0f743d63 100644
--- a/drivers/hid/hid-dummy.c
+++ b/drivers/hid/hid-dummy.c
@@ -43,6 +43,9 @@ static int __init hid_dummy_init(void)
#ifdef CONFIG_HID_MONTEREY_MODULE
HID_COMPAT_CALL_DRIVER(monterey);
#endif
+#ifdef CONFIG_HID_NTRIG_MODULE
+ HID_COMPAT_CALL_DRIVER(ntrig);
+#endif
#ifdef CONFIG_HID_PANTHERLORD_MODULE
HID_COMPAT_CALL_DRIVER(pantherlord);
#endif
@@ -58,6 +61,9 @@ static int __init hid_dummy_init(void)
#ifdef CONFIG_HID_SUNPLUS_MODULE
HID_COMPAT_CALL_DRIVER(sunplus);
#endif
+#ifdef CONFIG_GREENASIA_FF_MODULE
+ HID_COMPAT_CALL_DRIVER(greenasia);
+#endif
#ifdef CONFIG_THRUSTMASTER_FF_MODULE
HID_COMPAT_CALL_DRIVER(thrustmaster);
#endif
diff --git a/drivers/hid/hid-gaff.c b/drivers/hid/hid-gaff.c
new file mode 100644
index 000000000000..71211f6a4f02
--- /dev/null
+++ b/drivers/hid/hid-gaff.c
@@ -0,0 +1,185 @@
+/*
+ * Force feedback support for GreenAsia (Product ID 0x12) based devices
+ *
+ * The devices are distributed under various names and the same USB device ID
+ * can be used in many game controllers.
+ *
+ *
+ * 0e8f:0012 "GreenAsia Inc. USB Joystick "
+ * - tested with MANTA Warior MM816 and SpeedLink Strike2 SL-6635.
+ *
+ * Copyright (c) 2008 Lukasz Lubojanski <lukasz@lubojanski.info>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/input.h>
+#include <linux/usb.h>
+#include <linux/hid.h>
+#include "hid-ids.h"
+#include "usbhid/usbhid.h"
+
+struct gaff_device {
+ struct hid_report *report;
+};
+
+static int hid_gaff_play(struct input_dev *dev, void *data,
+ struct ff_effect *effect)
+{
+ struct hid_device *hid = input_get_drvdata(dev);
+ struct gaff_device *gaff = 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 * 0xfe / 0xffff;
+ right = right * 0xfe / 0xffff;
+
+ gaff->report->field[0]->value[0] = 0x51;
+ gaff->report->field[0]->value[1] = 0x0;
+ gaff->report->field[0]->value[2] = right;
+ gaff->report->field[0]->value[3] = 0;
+ gaff->report->field[0]->value[4] = left;
+ gaff->report->field[0]->value[5] = 0;
+ dbg_hid("running with 0x%02x 0x%02x", left, right);
+ usbhid_submit_report(hid, gaff->report, USB_DIR_OUT);
+
+ gaff->report->field[0]->value[0] = 0xfa;
+ gaff->report->field[0]->value[1] = 0xfe;
+ gaff->report->field[0]->value[2] = 0x0;
+ gaff->report->field[0]->value[4] = 0x0;
+
+ usbhid_submit_report(hid, gaff->report, USB_DIR_OUT);
+
+ return 0;
+}
+
+static int gaff_init(struct hid_device *hid)
+{
+ struct gaff_device *gaff;
+ struct hid_report *report;
+ struct hid_input *hidinput = list_entry(hid->inputs.next,
+ struct hid_input, list);
+ struct list_head *report_list =
+ &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+ struct list_head *report_ptr = 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_ptr = report_ptr->next;
+
+ report = list_entry(report_ptr, struct hid_report, list);
+ if (report->maxfield < 1) {
+ dev_err(&hid->dev, "no fields in the report\n");
+ return -ENODEV;
+ }
+
+ if (report->field[0]->report_count < 6) {
+ dev_err(&hid->dev, "not enough values in the field\n");
+ return -ENODEV;
+ }
+
+ gaff = kzalloc(sizeof(struct gaff_device), GFP_KERNEL);
+ if (!gaff)
+ return -ENOMEM;
+
+ set_bit(FF_RUMBLE, dev->ffbit);
+
+ error = input_ff_create_memless(dev, gaff, hid_gaff_play);
+ if (error) {
+ kfree(gaff);
+ return error;
+ }
+
+ gaff->report = report;
+ gaff->report->field[0]->value[0] = 0x51;
+ gaff->report->field[0]->value[1] = 0x00;
+ gaff->report->field[0]->value[2] = 0x00;
+ gaff->report->field[0]->value[3] = 0x00;
+ usbhid_submit_report(hid, gaff->report, USB_DIR_OUT);
+
+ gaff->report->field[0]->value[0] = 0xfa;
+ gaff->report->field[0]->value[1] = 0xfe;
+
+ usbhid_submit_report(hid, gaff->report, USB_DIR_OUT);
+
+ dev_info(&hid->dev, "Force Feedback for GreenAsia 0x12"
+ " devices by Lukasz Lubojanski <lukasz@lubojanski.info>\n");
+
+ return 0;
+}
+
+static int ga_probe(struct hid_device *hdev, const struct hid_device_id *id)
+{
+ int ret;
+
+ dev_dbg(&hdev->dev, "Greenasia HID hardware probe...");
+
+ ret = hid_parse(hdev);
+ if (ret) {
+ dev_err(&hdev->dev, "parse failed\n");
+ goto err;
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
+ if (ret) {
+ dev_err(&hdev->dev, "hw start failed\n");
+ goto err;
+ }
+
+ gaff_init(hdev);
+
+ return 0;
+err:
+ return ret;
+}
+
+static const struct hid_device_id ga_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0012), },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, ga_devices);
+
+static struct hid_driver ga_driver = {
+ .name = "greenasia",
+ .id_table = ga_devices,
+ .probe = ga_probe,
+};
+
+static int __init ga_init(void)
+{
+ return hid_register_driver(&ga_driver);
+}
+
+static void __exit ga_exit(void)
+{
+ hid_unregister_driver(&ga_driver);
+}
+
+module_init(ga_init);
+module_exit(ga_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(greenasia);
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 39289699c32f..acc1abc834a4 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -107,9 +107,6 @@
#define USB_VENDOR_ID_BELKIN 0x050d
#define USB_DEVICE_ID_FLIP_KVM 0x3201
-#define USB_VENDOR_ID_BRIGHT 0x1241
-#define USB_DEVICE_ID_BRIGHT_ABNT2 0x1503
-
#define USB_VENDOR_ID_BERKSHIRE 0x0c98
#define USB_DEVICE_ID_BERKSHIRE_PCWD 0x1140
@@ -141,9 +138,8 @@
#define USB_DEVICE_ID_CYPRESS_BARCODE_1 0xde61
#define USB_DEVICE_ID_CYPRESS_BARCODE_2 0xde64
-#define USB_VENDOR_ID_DELL 0x413c
-#define USB_DEVICE_ID_DELL_W7658 0x2005
-#define USB_DEVICE_ID_DELL_SK8115 0x2105
+#define USB_VENDOR_ID_DEALEXTREAME 0x10c5
+#define USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701 0x819a
#define USB_VENDOR_ID_DELORME 0x1163
#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100
@@ -167,9 +163,6 @@
#define USB_VENDOR_ID_GENERAL_TOUCH 0x0dfc
-#define USB_VENDOR_ID_GENERIC_13BA 0x13ba
-#define USB_DEVICE_ID_GENERIC_13BA_KBD_MOUSE 0x0017
-
#define USB_VENDOR_ID_GLAB 0x06c2
#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038
#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039
@@ -292,7 +285,6 @@
#define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294
#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295
#define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a
-#define USB_DEVICE_ID_LOGITECH_KBD 0xc311
#define USB_DEVICE_ID_S510_RECEIVER 0xc50c
#define USB_DEVICE_ID_S510_RECEIVER_2 0xc517
#define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500 0xc512
@@ -339,6 +331,9 @@
#define USB_VENDOR_ID_NEC 0x073e
#define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301
+#define USB_VENDOR_ID_NTRIG 0x1b96
+#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN 0x0001
+
#define USB_VENDOR_ID_ONTRAK 0x0a07
#define USB_DEVICE_ID_ONTRAK_ADU100 0x0064
@@ -383,9 +378,15 @@
#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_TURBOX 0x062a
#define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201
+#define USB_VENDOR_ID_UCLOGIC 0x5543
+#define USB_DEVICE_ID_UCLOGIC_TABLET_PF1209 0x0042
+
#define USB_VENDOR_ID_VERNIER 0x08f7
#define USB_DEVICE_ID_VERNIER_LABPRO 0x0001
#define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
index 2bae340eafe2..83e07c9f4144 100644
--- a/drivers/hid/hid-lg.c
+++ b/drivers/hid/hid-lg.c
@@ -26,7 +26,6 @@
#define LG_RDESC 0x001
#define LG_BAD_RELATIVE_KEYS 0x002
#define LG_DUPLICATE_USAGES 0x004
-#define LG_RESET_LEDS 0x008
#define LG_EXPANDED_KEYMAP 0x010
#define LG_IGNORE_DOUBLED_WHEEL 0x020
#define LG_WIRELESS 0x040
@@ -248,9 +247,6 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto err_free;
}
- if (quirks & LG_RESET_LEDS)
- usbhid_set_leds(hdev);
-
if (quirks & LG_FF)
lgff_init(hdev);
if (quirks & LG_FF2)
@@ -279,9 +275,6 @@ static const struct hid_device_id lg_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI),
.driver_data = LG_DUPLICATE_USAGES },
- { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KBD),
- .driver_data = LG_RESET_LEDS },
-
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD),
.driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500),
diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c
new file mode 100644
index 000000000000..db44fbd7bdf6
--- /dev/null
+++ b/drivers/hid/hid-ntrig.c
@@ -0,0 +1,82 @@
+/*
+ * HID driver for some ntrig "special" devices
+ *
+ * Copyright (c) 1999 Andreas Gal
+ * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
+ * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
+ * Copyright (c) 2006-2007 Jiri Kosina
+ * Copyright (c) 2007 Paul Walmsley
+ * Copyright (c) 2008 Jiri Slaby
+ * Copyright (c) 2008 Rafi Rubin
+ *
+ */
+
+/*
+ * 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"
+
+#define NTRIG_DUPLICATE_USAGES 0x001
+
+#define nt_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
+ EV_KEY, (c))
+
+static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ if ((usage->hid & HID_USAGE_PAGE) == HID_UP_DIGITIZER &&
+ (usage->hid & 0xff) == 0x47) {
+ nt_map_key_clear(BTN_TOOL_DOUBLETAP);
+ return 1;
+ }
+ return 0;
+}
+
+static int ntrig_input_mapped(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ if (usage->type == EV_KEY || usage->type == EV_REL
+ || usage->type == EV_ABS)
+ clear_bit(usage->code, *bit);
+
+ return 0;
+}
+static const struct hid_device_id ntrig_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN),
+ .driver_data = NTRIG_DUPLICATE_USAGES },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, ntrig_devices);
+
+static struct hid_driver ntrig_driver = {
+ .name = "ntrig",
+ .id_table = ntrig_devices,
+ .input_mapping = ntrig_input_mapping,
+ .input_mapped = ntrig_input_mapped,
+};
+
+static int ntrig_init(void)
+{
+ return hid_register_driver(&ntrig_driver);
+}
+
+static void ntrig_exit(void)
+{
+ hid_unregister_driver(&ntrig_driver);
+}
+
+module_init(ntrig_init);
+module_exit(ntrig_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(ntrig);
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 86e563b8d644..dd5a3979a4de 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -102,7 +102,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
ret = sony_set_operational(hdev);
- if (ret)
+ if (ret < 0)
goto err_stop;
return 0;
diff --git a/drivers/hid/hid-topseed.c b/drivers/hid/hid-topseed.c
new file mode 100644
index 000000000000..cca64a0564a9
--- /dev/null
+++ b/drivers/hid/hid-topseed.c
@@ -0,0 +1,77 @@
+/*
+ * HID driver for TopSeed Cyberlink remote
+ *
+ * Copyright (c) 2008 Lev Babiev
+ * based on hid-cherry driver
+ */
+
+/*
+ * 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"
+
+#define ts_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
+ EV_KEY, (c))
+static int ts_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ if ((usage->hid & HID_USAGE_PAGE) != 0x0ffbc0000)
+ return 0;
+
+ switch (usage->hid & HID_USAGE) {
+ case 0x00d: ts_map_key_clear(KEY_HOME); break;
+ case 0x024: ts_map_key_clear(KEY_MENU); break;
+ case 0x025: ts_map_key_clear(KEY_TV); break;
+ case 0x048: ts_map_key_clear(KEY_RED); break;
+ case 0x047: ts_map_key_clear(KEY_GREEN); break;
+ case 0x049: ts_map_key_clear(KEY_YELLOW); break;
+ case 0x04a: ts_map_key_clear(KEY_BLUE); break;
+ case 0x04b: ts_map_key_clear(KEY_ANGLE); break;
+ case 0x04c: ts_map_key_clear(KEY_LANGUAGE); break;
+ case 0x04d: ts_map_key_clear(KEY_SUBTITLE); 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;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+static const struct hid_device_id ts_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, ts_devices);
+
+static struct hid_driver ts_driver = {
+ .name = "topseed",
+ .id_table = ts_devices,
+ .input_mapping = ts_input_mapping,
+};
+
+static int ts_init(void)
+{
+ return hid_register_driver(&ts_driver);
+}
+
+static void ts_exit(void)
+{
+ hid_unregister_driver(&ts_driver);
+}
+
+module_init(ts_init);
+module_exit(ts_exit);
+MODULE_LICENSE("GPL");
+
+HID_COMPAT_LOAD_DRIVER(topseed);
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index 7685ae6808c4..732449628971 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -208,7 +208,7 @@ static int hidraw_release(struct inode * inode, struct file * file)
list_del(&list->node);
dev = hidraw_table[minor];
- if (!dev->open--) {
+ if (!--dev->open) {
if (list->hidraw->exist)
dev->hid->ll_driver->close(dev->hid);
else
@@ -265,6 +265,34 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
break;
}
default:
+ {
+ struct hid_device *hid = dev->hid;
+ if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ)
+ return -EINVAL;
+
+ if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWNAME(0))) {
+ int len;
+ if (!hid->name)
+ return 0;
+ len = strlen(hid->name) + 1;
+ if (len > _IOC_SIZE(cmd))
+ len = _IOC_SIZE(cmd);
+ return copy_to_user(user_arg, hid->name, len) ?
+ -EFAULT : len;
+ }
+
+ if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWPHYS(0))) {
+ int len;
+ if (!hid->phys)
+ return 0;
+ len = strlen(hid->phys) + 1;
+ if (len > _IOC_SIZE(cmd))
+ len = _IOC_SIZE(cmd);
+ return copy_to_user(user_arg, hid->phys, len) ?
+ -EFAULT : len;
+ }
+ }
+
ret = -ENOTTY;
}
unlock_kernel();
@@ -329,7 +357,7 @@ int hidraw_connect(struct hid_device *hid)
goto out;
}
- dev->dev = device_create(hidraw_class, NULL, MKDEV(hidraw_major, minor),
+ dev->dev = device_create(hidraw_class, &hid->dev, MKDEV(hidraw_major, minor),
NULL, "%s%d", "hidraw", minor);
if (IS_ERR(dev->dev)) {
diff --git a/drivers/hid/usbhid/Kconfig b/drivers/hid/usbhid/Kconfig
index 5d9aa95fc3ef..4edb3bef94a6 100644
--- a/drivers/hid/usbhid/Kconfig
+++ b/drivers/hid/usbhid/Kconfig
@@ -45,7 +45,7 @@ config USB_HIDDEV
If unsure, say Y.
menu "USB HID Boot Protocol drivers"
- depends on USB!=n && USB_HID!=y
+ depends on USB!=n && USB_HID!=y && EMBEDDED
config USB_KBD
tristate "USB HIDBP Keyboard (simple Boot) support"
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 606369ea24ca..f0a0f72238ab 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -4,7 +4,7 @@
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
- * Copyright (c) 2006-2007 Jiri Kosina
+ * Copyright (c) 2006-2008 Jiri Kosina
*/
/*
@@ -102,7 +102,7 @@ static void hid_reset(struct work_struct *work)
struct usbhid_device *usbhid =
container_of(work, struct usbhid_device, reset_work);
struct hid_device *hid = usbhid->hid;
- int rc_lock, rc = 0;
+ int rc = 0;
if (test_bit(HID_CLEAR_HALT, &usbhid->iofl)) {
dev_dbg(&usbhid->intf->dev, "clear halt\n");
@@ -113,11 +113,10 @@ static void hid_reset(struct work_struct *work)
else if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) {
dev_dbg(&usbhid->intf->dev, "resetting device\n");
- rc = rc_lock = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf);
- if (rc_lock >= 0) {
+ rc = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf);
+ if (rc == 0) {
rc = usb_reset_device(hid_to_usb_dev(hid));
- if (rc_lock)
- usb_unlock_device(hid_to_usb_dev(hid));
+ usb_unlock_device(hid_to_usb_dev(hid));
}
clear_bit(HID_RESET_PENDING, &usbhid->iofl);
}
@@ -641,9 +640,7 @@ static void hid_find_max_report(struct hid_device *hid, unsigned int type,
unsigned int size;
list_for_each_entry(report, &hid->report_enum[type].report_list, list) {
- size = ((report->size - 1) >> 3) + 1;
- if (type == HID_INPUT_REPORT && hid->report_enum[type].numbered)
- size++;
+ size = ((report->size - 1) >> 3) + 1 + hid->report_enum[type].numbered;
if (*max < size)
*max = size;
}
@@ -653,13 +650,16 @@ static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid)
{
struct usbhid_device *usbhid = hid->driver_data;
- if (!(usbhid->inbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->inbuf_dma)))
- return -1;
- if (!(usbhid->outbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->outbuf_dma)))
- return -1;
- if (!(usbhid->cr = usb_buffer_alloc(dev, sizeof(*(usbhid->cr)), GFP_ATOMIC, &usbhid->cr_dma)))
- return -1;
- if (!(usbhid->ctrlbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->ctrlbuf_dma)))
+ usbhid->inbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_KERNEL,
+ &usbhid->inbuf_dma);
+ usbhid->outbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_KERNEL,
+ &usbhid->outbuf_dma);
+ usbhid->cr = usb_buffer_alloc(dev, sizeof(*usbhid->cr), GFP_KERNEL,
+ &usbhid->cr_dma);
+ usbhid->ctrlbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_KERNEL,
+ &usbhid->ctrlbuf_dma);
+ if (!usbhid->inbuf || !usbhid->outbuf || !usbhid->cr ||
+ !usbhid->ctrlbuf)
return -1;
return 0;
@@ -807,7 +807,7 @@ static int usbhid_start(struct hid_device *hid)
int interval;
endpoint = &interface->endpoint[n].desc;
- if ((endpoint->bmAttributes & 3) != 3) /* Not an interrupt endpoint */
+ if (!usb_endpoint_xfer_int(endpoint))
continue;
interval = endpoint->bInterval;
@@ -876,6 +876,15 @@ static int usbhid_start(struct hid_device *hid)
set_bit(HID_STARTED, &usbhid->iofl);
+ /* Some keyboards don't work until their LEDs have been set.
+ * Since BIOSes do set the LEDs, it must be safe for any device
+ * that supports the keyboard boot protocol.
+ */
+ if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT &&
+ interface->desc.bInterfaceProtocol ==
+ USB_INTERFACE_PROTOCOL_KEYBOARD)
+ usbhid_set_leds(hid);
+
return 0;
fail:
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 47ebe045f9b5..4391717d2519 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -54,6 +54,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_PF1209, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c
index 83e851a5ed30..d73eea382ab3 100644
--- a/drivers/hid/usbhid/hiddev.c
+++ b/drivers/hid/usbhid/hiddev.c
@@ -49,6 +49,7 @@
struct hiddev {
int exist;
int open;
+ struct mutex existancelock;
wait_queue_head_t wait;
struct hid_device *hid;
struct list_head list;
@@ -63,6 +64,7 @@ struct hiddev_list {
struct fasync_struct *fasync;
struct hiddev *hiddev;
struct list_head node;
+ struct mutex thread_lock;
};
static struct hiddev *hiddev_table[HIDDEV_MINORS];
@@ -264,29 +266,48 @@ static int hiddev_release(struct inode * inode, struct file * file)
static int hiddev_open(struct inode *inode, struct file *file)
{
struct hiddev_list *list;
- unsigned long flags;
+ int res;
int i = iminor(inode) - HIDDEV_MINOR_BASE;
- if (i >= HIDDEV_MINORS || !hiddev_table[i])
+ if (i >= HIDDEV_MINORS || i < 0 || !hiddev_table[i])
return -ENODEV;
if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL)))
return -ENOMEM;
+ mutex_init(&list->thread_lock);
list->hiddev = hiddev_table[i];
- spin_lock_irqsave(&list->hiddev->list_lock, flags);
- list_add_tail(&list->node, &hiddev_table[i]->list);
- spin_unlock_irqrestore(&list->hiddev->list_lock, flags);
file->private_data = list;
- if (!list->hiddev->open++)
- if (list->hiddev->exist)
- usbhid_open(hiddev_table[i]->hid);
+ /*
+ * no need for locking because the USB major number
+ * is shared which usbcore guards against disconnect
+ */
+ if (list->hiddev->exist) {
+ if (!list->hiddev->open++) {
+ res = usbhid_open(hiddev_table[i]->hid);
+ if (res < 0) {
+ res = -EIO;
+ goto bail;
+ }
+ }
+ } else {
+ res = -ENODEV;
+ goto bail;
+ }
+
+ spin_lock_irq(&list->hiddev->list_lock);
+ list_add_tail(&list->node, &hiddev_table[i]->list);
+ spin_unlock_irq(&list->hiddev->list_lock);
return 0;
+bail:
+ file->private_data = NULL;
+ kfree(list->hiddev);
+ return res;
}
/*
@@ -305,7 +326,7 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun
DECLARE_WAITQUEUE(wait, current);
struct hiddev_list *list = file->private_data;
int event_size;
- int retval = 0;
+ int retval;
event_size = ((list->flags & HIDDEV_FLAG_UREF) != 0) ?
sizeof(struct hiddev_usage_ref) : sizeof(struct hiddev_event);
@@ -313,10 +334,14 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun
if (count < event_size)
return 0;
+ /* lock against other threads */
+ retval = mutex_lock_interruptible(&list->thread_lock);
+ if (retval)
+ return -ERESTARTSYS;
+
while (retval == 0) {
if (list->head == list->tail) {
- add_wait_queue(&list->hiddev->wait, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
+ prepare_to_wait(&list->hiddev->wait, &wait, TASK_INTERRUPTIBLE);
while (list->head == list->tail) {
if (file->f_flags & O_NONBLOCK) {
@@ -332,35 +357,45 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun
break;
}
+ /* let O_NONBLOCK tasks run */
+ mutex_unlock(&list->thread_lock);
schedule();
+ if (mutex_lock_interruptible(&list->thread_lock))
+ return -EINTR;
set_current_state(TASK_INTERRUPTIBLE);
}
+ finish_wait(&list->hiddev->wait, &wait);
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&list->hiddev->wait, &wait);
}
- if (retval)
+ if (retval) {
+ mutex_unlock(&list->thread_lock);
return retval;
+ }
while (list->head != list->tail &&
retval + event_size <= count) {
if ((list->flags & HIDDEV_FLAG_UREF) == 0) {
- if (list->buffer[list->tail].field_index !=
- HID_FIELD_INDEX_NONE) {
+ if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE) {
struct hiddev_event event;
+
event.hid = list->buffer[list->tail].usage_code;
event.value = list->buffer[list->tail].value;
- if (copy_to_user(buffer + retval, &event, sizeof(struct hiddev_event)))
+ if (copy_to_user(buffer + retval, &event, sizeof(struct hiddev_event))) {
+ mutex_unlock(&list->thread_lock);
return -EFAULT;
+ }
retval += sizeof(struct hiddev_event);
}
} else {
if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE ||
(list->flags & HIDDEV_FLAG_REPORT) != 0) {
- if (copy_to_user(buffer + retval, list->buffer + list->tail, sizeof(struct hiddev_usage_ref)))
+
+ if (copy_to_user(buffer + retval, list->buffer + list->tail, sizeof(struct hiddev_usage_ref))) {
+ mutex_unlock(&list->thread_lock);
return -EFAULT;
+ }
retval += sizeof(struct hiddev_usage_ref);
}
}
@@ -368,6 +403,7 @@ static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t coun
}
}
+ mutex_unlock(&list->thread_lock);
return retval;
}
@@ -555,7 +591,7 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
struct hid_field *field;
struct usbhid_device *usbhid = hid->driver_data;
void __user *user_arg = (void __user *)arg;
- int i;
+ int i, r;
/* Called without BKL by compat methods so no BKL taken */
@@ -619,10 +655,22 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
}
case HIDIOCGSTRING:
- return hiddev_ioctl_string(hiddev, cmd, user_arg);
+ mutex_lock(&hiddev->existancelock);
+ if (!hiddev->exist)
+ r = hiddev_ioctl_string(hiddev, cmd, user_arg);
+ else
+ r = -ENODEV;
+ mutex_unlock(&hiddev->existancelock);
+ return r;
case HIDIOCINITREPORT:
+ mutex_lock(&hiddev->existancelock);
+ if (!hiddev->exist) {
+ mutex_unlock(&hiddev->existancelock);
+ return -ENODEV;
+ }
usbhid_init_reports(hid);
+ mutex_unlock(&hiddev->existancelock);
return 0;
@@ -636,8 +684,12 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
return -EINVAL;
- usbhid_submit_report(hid, report, USB_DIR_IN);
- usbhid_wait_io(hid);
+ mutex_lock(&hiddev->existancelock);
+ if (hiddev->exist) {
+ usbhid_submit_report(hid, report, USB_DIR_IN);
+ usbhid_wait_io(hid);
+ }
+ mutex_unlock(&hiddev->existancelock);
return 0;
@@ -651,8 +703,12 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
return -EINVAL;
- usbhid_submit_report(hid, report, USB_DIR_OUT);
- usbhid_wait_io(hid);
+ mutex_lock(&hiddev->existancelock);
+ if (hiddev->exist) {
+ usbhid_submit_report(hid, report, USB_DIR_OUT);
+ usbhid_wait_io(hid);
+ }
+ mutex_unlock(&hiddev->existancelock);
return 0;
@@ -710,7 +766,13 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case HIDIOCGUSAGES:
case HIDIOCSUSAGES:
case HIDIOCGCOLLECTIONINDEX:
- return hiddev_ioctl_usage(hiddev, cmd, user_arg);
+ mutex_lock(&hiddev->existancelock);
+ if (hiddev->exist)
+ r = hiddev_ioctl_usage(hiddev, cmd, user_arg);
+ else
+ r = -ENODEV;
+ mutex_unlock(&hiddev->existancelock);
+ return r;
case HIDIOCGCOLLECTIONINFO:
if (copy_from_user(&cinfo, user_arg, sizeof(cinfo)))
@@ -808,23 +870,24 @@ int hiddev_connect(struct hid_device *hid, unsigned int force)
if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL)))
return -1;
- retval = usb_register_dev(usbhid->intf, &hiddev_class);
- if (retval) {
- err_hid("Not able to get a minor for this device.");
- kfree(hiddev);
- return -1;
- }
-
init_waitqueue_head(&hiddev->wait);
INIT_LIST_HEAD(&hiddev->list);
spin_lock_init(&hiddev->list_lock);
+ mutex_init(&hiddev->existancelock);
+ hid->hiddev = hiddev;
hiddev->hid = hid;
hiddev->exist = 1;
- hid->minor = usbhid->intf->minor;
- hid->hiddev = hiddev;
-
- hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev;
+ retval = usb_register_dev(usbhid->intf, &hiddev_class);
+ if (retval) {
+ err_hid("Not able to get a minor for this device.");
+ hid->hiddev = NULL;
+ kfree(hiddev);
+ return -1;
+ } else {
+ hid->minor = usbhid->intf->minor;
+ hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev;
+ }
return 0;
}
@@ -839,7 +902,9 @@ void hiddev_disconnect(struct hid_device *hid)
struct hiddev *hiddev = hid->hiddev;
struct usbhid_device *usbhid = hid->driver_data;
+ mutex_lock(&hiddev->existancelock);
hiddev->exist = 0;
+ mutex_unlock(&hiddev->existancelock);
hiddev_table[hiddev->hid->minor - HIDDEV_MINOR_BASE] = NULL;
usb_deregister_dev(usbhid->intf, &hiddev_class);
diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h
index 332abcdf9956..9eb30564be9c 100644
--- a/drivers/hid/usbhid/usbhid.h
+++ b/drivers/hid/usbhid/usbhid.h
@@ -40,6 +40,16 @@ int usbhid_open(struct hid_device *hid);
void usbhid_init_reports(struct hid_device *hid);
void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir);
+/* iofl flags */
+#define HID_CTRL_RUNNING 1
+#define HID_OUT_RUNNING 2
+#define HID_IN_RUNNING 3
+#define HID_RESET_PENDING 4
+#define HID_SUSPENDED 5
+#define HID_CLEAR_HALT 6
+#define HID_DISCONNECTED 7
+#define HID_STARTED 8
+
/*
* USB-specific HID struct, to be pointed to
* from struct hid_device->driver_data