1
0
Fork 0

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:
  HID: remove compat stuff
  HID: constify arrays of struct apple_key_translation
  HID: add support for Kye/Genius Ergo 525V
  HID: Support Apple mini aluminum keyboard
  HID: support for Kensington slimblade device
  HID: DragonRise game controller force feedback driver
  HID: add support for another version of 0e8f:0003 device in hid-pl
  HID: fix race between usb_register_dev() and hiddev_open()
  HID: bring back possibility to specify vid/pid ignore on module load
  HID: make HID_DEBUG defaults consistent
  HID: autosuspend -- fix lockup of hid on reset
  HID: hid_reset_resume() needs to be defined only when CONFIG_PM is set
  HID: fix USB HID devices after STD with autosuspend
  HID: do not try to compile PM code with CONFIG_PM unset
  HID: autosuspend support for USB HID
hifive-unleashed-5.1
Linus Torvalds 2009-04-03 15:25:44 -07:00
commit 5fba0925fd
35 changed files with 854 additions and 318 deletions

View File

@ -283,13 +283,6 @@ Who: Glauber Costa <gcosta@redhat.com>
--------------------------- ---------------------------
What: remove HID compat support
When: 2.6.29
Why: needed only as a temporary solution until distros fix themselves up
Who: Jiri Slaby <jirislaby@gmail.com>
---------------------------
What: print_fn_descriptor_symbol() What: print_fn_descriptor_symbol()
When: October 2009 When: October 2009
Why: The %pF vsprintf format provides the same functionality in a Why: The %pF vsprintf format provides the same functionality in a

View File

@ -29,11 +29,11 @@ config HID
For docs and specs, see http://www.usb.org/developers/hidpage/ For docs and specs, see http://www.usb.org/developers/hidpage/
If unsure, say Y If unsure, say Y.
config HID_DEBUG config HID_DEBUG
bool "HID debugging support" bool "HID debugging support"
default y if !EMBEDDED default y
depends on HID depends on HID
---help--- ---help---
This option lets the HID layer output diagnostics about its internal This option lets the HID layer output diagnostics about its internal
@ -44,7 +44,7 @@ config HID_DEBUG
This feature is useful for those who are either debugging the HID parser This feature is useful for those who are either debugging the HID parser
or any HID hardware device. or any HID hardware device.
If unsure, say N If unsure, say Y.
config HIDRAW config HIDRAW
bool "/dev/hidraw raw HID device support" bool "/dev/hidraw raw HID device support"
@ -70,18 +70,6 @@ source "drivers/hid/usbhid/Kconfig"
menu "Special HID drivers" menu "Special HID drivers"
depends on HID depends on HID
config HID_COMPAT
bool "Load all HID drivers on hid core load"
default y
---help---
Compatible option for older userspace. If you have system without udev
support of module loading through aliases and also old
module-init-tools which can't handle hid bus, choose Y here. Otherwise
say N. If you say N and your userspace is old enough, the only
functionality you lose is modules autoloading.
If unsure, say Y.
config HID_A4TECH config HID_A4TECH
tristate "A4 tech" if EMBEDDED tristate "A4 tech" if EMBEDDED
depends on USB_HID depends on USB_HID
@ -128,6 +116,14 @@ config HID_CYPRESS
---help--- ---help---
Support for cypress mouse and barcode readers. Support for cypress mouse and barcode readers.
config DRAGONRISE_FF
tristate "DragonRise Inc. force feedback support"
depends on USB_HID
select INPUT_FF_MEMLESS
---help---
Say Y here if you want to enable force feedback support for DragonRise Inc.
game controllers.
config HID_EZKEY config HID_EZKEY
tristate "Ezkey" if EMBEDDED tristate "Ezkey" if EMBEDDED
depends on USB_HID depends on USB_HID
@ -135,6 +131,13 @@ config HID_EZKEY
---help--- ---help---
Support for Ezkey BTC 8193 keyboard. Support for Ezkey BTC 8193 keyboard.
config HID_KYE
tristate "Kye" if EMBEDDED
depends on USB_HID
default !EMBEDDED
---help---
Support for Kye/Genius Ergo Mouse.
config HID_GYRATION config HID_GYRATION
tristate "Gyration" if EMBEDDED tristate "Gyration" if EMBEDDED
depends on USB_HID depends on USB_HID
@ -142,6 +145,13 @@ config HID_GYRATION
---help--- ---help---
Support for Gyration remote control. Support for Gyration remote control.
config HID_KENSINGTON
tristate "Kensington" if EMBEDDED
depends on USB_HID
default !EMBEDDED
---help---
Support for Kensington Slimblade Trackball.
config HID_LOGITECH config HID_LOGITECH
tristate "Logitech" if EMBEDDED tristate "Logitech" if EMBEDDED
depends on USB_HID depends on USB_HID

View File

@ -8,10 +8,6 @@ obj-$(CONFIG_HID) += hid.o
hid-$(CONFIG_HID_DEBUG) += hid-debug.o hid-$(CONFIG_HID_DEBUG) += hid-debug.o
hid-$(CONFIG_HIDRAW) += hidraw.o hid-$(CONFIG_HIDRAW) += hidraw.o
ifdef CONFIG_HID_COMPAT
obj-m += hid-dummy.o
endif
hid-logitech-objs := hid-lg.o hid-logitech-objs := hid-lg.o
ifdef CONFIG_LOGITECH_FF ifdef CONFIG_LOGITECH_FF
hid-logitech-objs += hid-lgff.o hid-logitech-objs += hid-lgff.o
@ -26,8 +22,11 @@ obj-$(CONFIG_HID_BELKIN) += hid-belkin.o
obj-$(CONFIG_HID_CHERRY) += hid-cherry.o obj-$(CONFIG_HID_CHERRY) += hid-cherry.o
obj-$(CONFIG_HID_CHICONY) += hid-chicony.o obj-$(CONFIG_HID_CHICONY) += hid-chicony.o
obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o
obj-$(CONFIG_DRAGONRISE_FF) += hid-drff.o
obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o
obj-$(CONFIG_HID_GYRATION) += hid-gyration.o obj-$(CONFIG_HID_GYRATION) += hid-gyration.o
obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o
obj-$(CONFIG_HID_KYE) += hid-kye.o
obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o
obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o
obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o

View File

@ -158,5 +158,3 @@ static void a4_exit(void)
module_init(a4_init); module_init(a4_init);
module_exit(a4_exit); module_exit(a4_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
HID_COMPAT_LOAD_DRIVER(a4tech);

View File

@ -53,7 +53,7 @@ struct apple_key_translation {
u8 flags; u8 flags;
}; };
static struct apple_key_translation apple_fn_keys[] = { static const struct apple_key_translation apple_fn_keys[] = {
{ KEY_BACKSPACE, KEY_DELETE }, { KEY_BACKSPACE, KEY_DELETE },
{ KEY_ENTER, KEY_INSERT }, { KEY_ENTER, KEY_INSERT },
{ KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY }, { KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY },
@ -75,7 +75,7 @@ static struct apple_key_translation apple_fn_keys[] = {
{ } { }
}; };
static struct apple_key_translation powerbook_fn_keys[] = { static const struct apple_key_translation powerbook_fn_keys[] = {
{ KEY_BACKSPACE, KEY_DELETE }, { KEY_BACKSPACE, KEY_DELETE },
{ KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY }, { KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY },
{ KEY_F2, KEY_BRIGHTNESSUP, APPLE_FLAG_FKEY }, { KEY_F2, KEY_BRIGHTNESSUP, APPLE_FLAG_FKEY },
@ -94,7 +94,7 @@ static struct apple_key_translation powerbook_fn_keys[] = {
{ } { }
}; };
static struct apple_key_translation powerbook_numlock_keys[] = { static const struct apple_key_translation powerbook_numlock_keys[] = {
{ KEY_J, KEY_KP1 }, { KEY_J, KEY_KP1 },
{ KEY_K, KEY_KP2 }, { KEY_K, KEY_KP2 },
{ KEY_L, KEY_KP3 }, { KEY_L, KEY_KP3 },
@ -117,16 +117,16 @@ static struct apple_key_translation powerbook_numlock_keys[] = {
{ } { }
}; };
static struct apple_key_translation apple_iso_keyboard[] = { static const struct apple_key_translation apple_iso_keyboard[] = {
{ KEY_GRAVE, KEY_102ND }, { KEY_GRAVE, KEY_102ND },
{ KEY_102ND, KEY_GRAVE }, { KEY_102ND, KEY_GRAVE },
{ } { }
}; };
static struct apple_key_translation *apple_find_translation( static const struct apple_key_translation *apple_find_translation(
struct apple_key_translation *table, u16 from) const struct apple_key_translation *table, u16 from)
{ {
struct apple_key_translation *trans; const struct apple_key_translation *trans;
/* Look for the translation */ /* Look for the translation */
for (trans = table; trans->from; trans++) for (trans = table; trans->from; trans++)
@ -140,7 +140,7 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
struct hid_usage *usage, __s32 value) struct hid_usage *usage, __s32 value)
{ {
struct apple_sc *asc = hid_get_drvdata(hid); struct apple_sc *asc = hid_get_drvdata(hid);
struct apple_key_translation *trans; const struct apple_key_translation *trans;
if (usage->code == KEY_FN) { if (usage->code == KEY_FN) {
asc->fn_on = !!value; asc->fn_on = !!value;
@ -253,7 +253,7 @@ static void apple_report_fixup(struct hid_device *hdev, __u8 *rdesc,
static void apple_setup_input(struct input_dev *input) static void apple_setup_input(struct input_dev *input)
{ {
struct apple_key_translation *trans; const struct apple_key_translation *trans;
set_bit(KEY_NUMLOCK, input->keybit); set_bit(KEY_NUMLOCK, input->keybit);
@ -387,6 +387,12 @@ static const struct hid_device_id apple_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS), { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
APPLE_RDESC_JIS }, APPLE_RDESC_JIS },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_ANSI),
.driver_data = APPLE_HAS_FN },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_ISO),
.driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_JIS),
.driver_data = APPLE_HAS_FN },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI), { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI),
.driver_data = APPLE_HAS_FN }, .driver_data = APPLE_HAS_FN },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO), { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO),
@ -468,5 +474,3 @@ static void apple_exit(void)
module_init(apple_init); module_init(apple_init);
module_exit(apple_exit); module_exit(apple_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
HID_COMPAT_LOAD_DRIVER(apple);

View File

@ -101,5 +101,3 @@ static void belkin_exit(void)
module_init(belkin_init); module_init(belkin_init);
module_exit(belkin_exit); module_exit(belkin_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
HID_COMPAT_LOAD_DRIVER(belkin);

View File

@ -83,5 +83,3 @@ static void ch_exit(void)
module_init(ch_init); module_init(ch_init);
module_exit(ch_exit); module_exit(ch_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
HID_COMPAT_LOAD_DRIVER(cherry);

View File

@ -76,5 +76,3 @@ static void ch_exit(void)
module_init(ch_init); module_init(ch_init);
module_exit(ch_exit); module_exit(ch_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
HID_COMPAT_LOAD_DRIVER(chicony);

View File

@ -1236,6 +1236,9 @@ static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_ANSI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_ISO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_JIS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS) },
@ -1262,6 +1265,7 @@ static const struct hid_device_id hid_blacklist[] = {
{ 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_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_BARCODE_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) }, { 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_EZKEY, USB_DEVICE_ID_BTC_8193) }, { 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_PSX_ADAPTOR) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR) }, { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR) },
@ -1269,6 +1273,8 @@ static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0012) }, { 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) },
{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) },
@ -1813,14 +1819,21 @@ void hid_unregister_driver(struct hid_driver *hdrv)
} }
EXPORT_SYMBOL_GPL(hid_unregister_driver); EXPORT_SYMBOL_GPL(hid_unregister_driver);
#ifdef CONFIG_HID_COMPAT int hid_check_keys_pressed(struct hid_device *hid)
static void hid_compat_load(struct work_struct *ws)
{ {
request_module("hid-dummy"); struct hid_input *hidinput;
int i;
list_for_each_entry(hidinput, &hid->inputs, list) {
for (i = 0; i < BITS_TO_LONGS(KEY_MAX); i++)
if (hidinput->input->key[i])
return 1;
}
return 0;
} }
static DECLARE_WORK(hid_compat_work, hid_compat_load);
static struct workqueue_struct *hid_compat_wq; EXPORT_SYMBOL_GPL(hid_check_keys_pressed);
#endif
static int __init hid_init(void) static int __init hid_init(void)
{ {
@ -1836,15 +1849,6 @@ static int __init hid_init(void)
if (ret) if (ret)
goto err_bus; goto err_bus;
#ifdef CONFIG_HID_COMPAT
hid_compat_wq = create_singlethread_workqueue("hid_compat");
if (!hid_compat_wq) {
hidraw_exit();
goto err;
}
queue_work(hid_compat_wq, &hid_compat_work);
#endif
return 0; return 0;
err_bus: err_bus:
bus_unregister(&hid_bus_type); bus_unregister(&hid_bus_type);
@ -1854,9 +1858,6 @@ err:
static void __exit hid_exit(void) static void __exit hid_exit(void)
{ {
#ifdef CONFIG_HID_COMPAT
destroy_workqueue(hid_compat_wq);
#endif
hidraw_exit(); hidraw_exit();
bus_unregister(&hid_bus_type); bus_unregister(&hid_bus_type);
} }

View File

@ -154,5 +154,3 @@ static void cp_exit(void)
module_init(cp_init); module_init(cp_init);
module_exit(cp_exit); module_exit(cp_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
HID_COMPAT_LOAD_DRIVER(cypress);

View File

@ -0,0 +1,188 @@
/*
* Force feedback support for DragonRise Inc. game controllers
*
* From what I have gathered, these devices are mass produced in China and are
* distributed under several vendors. They often share the same design as
* the original PlayStation DualShock controller.
*
* 0079:0006 "DragonRise Inc. Generic USB Joystick "
* - tested with a Tesun USB-703 game controller.
*
* Copyright (c) 2009 Richard Walmsley <richwalm@gmail.com>
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/input.h>
#include <linux/usb.h>
#include <linux/hid.h>
#include "hid-ids.h"
#include "usbhid/usbhid.h"
struct drff_device {
struct hid_report *report;
};
static int drff_play(struct input_dev *dev, void *data,
struct ff_effect *effect)
{
struct hid_device *hid = input_get_drvdata(dev);
struct drff_device *drff = data;
int strong, weak;
strong = effect->u.rumble.strong_magnitude;
weak = effect->u.rumble.weak_magnitude;
dbg_hid("called with 0x%04x 0x%04x", strong, weak);
if (strong || weak) {
strong = strong * 0xff / 0xffff;
weak = weak * 0xff / 0xffff;
/* While reverse engineering this device, I found that when
this value is set, it causes the strong rumble to function
at a near maximum speed, so we'll bypass it. */
if (weak == 0x0a)
weak = 0x0b;
drff->report->field[0]->value[0] = 0x51;
drff->report->field[0]->value[1] = 0x00;
drff->report->field[0]->value[2] = weak;
drff->report->field[0]->value[4] = strong;
usbhid_submit_report(hid, drff->report, USB_DIR_OUT);
drff->report->field[0]->value[0] = 0xfa;
drff->report->field[0]->value[1] = 0xfe;
} else {
drff->report->field[0]->value[0] = 0xf3;
drff->report->field[0]->value[1] = 0x00;
}
drff->report->field[0]->value[2] = 0x00;
drff->report->field[0]->value[4] = 0x00;
dbg_hid("running with 0x%02x 0x%02x", strong, weak);
usbhid_submit_report(hid, drff->report, USB_DIR_OUT);
return 0;
}
static int drff_init(struct hid_device *hid)
{
struct drff_device *drff;
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 < 1) {
dev_err(&hid->dev, "no fields in the report\n");
return -ENODEV;
}
if (report->field[0]->report_count < 7) {
dev_err(&hid->dev, "not enough values in the field\n");
return -ENODEV;
}
drff = kzalloc(sizeof(struct drff_device), GFP_KERNEL);
if (!drff)
return -ENOMEM;
set_bit(FF_RUMBLE, dev->ffbit);
error = input_ff_create_memless(dev, drff, drff_play);
if (error) {
kfree(drff);
return error;
}
drff->report = report;
drff->report->field[0]->value[0] = 0xf3;
drff->report->field[0]->value[1] = 0x00;
drff->report->field[0]->value[2] = 0x00;
drff->report->field[0]->value[3] = 0x00;
drff->report->field[0]->value[4] = 0x00;
drff->report->field[0]->value[5] = 0x00;
drff->report->field[0]->value[6] = 0x00;
usbhid_submit_report(hid, drff->report, USB_DIR_OUT);
dev_info(&hid->dev, "Force Feedback for DragonRise Inc. game "
"controllers by Richard Walmsley <richwalm@gmail.com>\n");
return 0;
}
static int dr_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret;
dev_dbg(&hdev->dev, "DragonRise Inc. 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;
}
drff_init(hdev);
return 0;
err:
return ret;
}
static const struct hid_device_id dr_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006), },
{ }
};
MODULE_DEVICE_TABLE(hid, dr_devices);
static struct hid_driver dr_driver = {
.name = "dragonrise",
.id_table = dr_devices,
.probe = dr_probe,
};
static int __init dr_init(void)
{
return hid_register_driver(&dr_driver);
}
static void __exit dr_exit(void)
{
hid_unregister_driver(&dr_driver);
}
module_init(dr_init);
module_exit(dr_exit);
MODULE_LICENSE("GPL");

View File

@ -1,78 +0,0 @@
#include <linux/autoconf.h>
#include <linux/module.h>
#include <linux/hid.h>
static int __init hid_dummy_init(void)
{
#ifdef CONFIG_HID_A4TECH_MODULE
HID_COMPAT_CALL_DRIVER(a4tech);
#endif
#ifdef CONFIG_HID_APPLE_MODULE
HID_COMPAT_CALL_DRIVER(apple);
#endif
#ifdef CONFIG_HID_BELKIN_MODULE
HID_COMPAT_CALL_DRIVER(belkin);
#endif
#ifdef CONFIG_HID_BRIGHT_MODULE
HID_COMPAT_CALL_DRIVER(bright);
#endif
#ifdef CONFIG_HID_CHERRY_MODULE
HID_COMPAT_CALL_DRIVER(cherry);
#endif
#ifdef CONFIG_HID_CHICONY_MODULE
HID_COMPAT_CALL_DRIVER(chicony);
#endif
#ifdef CONFIG_HID_CYPRESS_MODULE
HID_COMPAT_CALL_DRIVER(cypress);
#endif
#ifdef CONFIG_HID_DELL_MODULE
HID_COMPAT_CALL_DRIVER(dell);
#endif
#ifdef CONFIG_HID_EZKEY_MODULE
HID_COMPAT_CALL_DRIVER(ezkey);
#endif
#ifdef CONFIG_HID_GYRATION_MODULE
HID_COMPAT_CALL_DRIVER(gyration);
#endif
#ifdef CONFIG_HID_LOGITECH_MODULE
HID_COMPAT_CALL_DRIVER(logitech);
#endif
#ifdef CONFIG_HID_MICROSOFT_MODULE
HID_COMPAT_CALL_DRIVER(microsoft);
#endif
#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
#ifdef CONFIG_HID_PETALYNX_MODULE
HID_COMPAT_CALL_DRIVER(petalynx);
#endif
#ifdef CONFIG_HID_SAMSUNG_MODULE
HID_COMPAT_CALL_DRIVER(samsung);
#endif
#ifdef CONFIG_HID_SONY_MODULE
HID_COMPAT_CALL_DRIVER(sony);
#endif
#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
#ifdef CONFIG_ZEROPLUS_FF_MODULE
HID_COMPAT_CALL_DRIVER(zeroplus);
#endif
return -EIO;
}
module_init(hid_dummy_init);
MODULE_LICENSE("GPL");

View File

@ -91,5 +91,3 @@ static void ez_exit(void)
module_init(ez_init); module_init(ez_init);
module_exit(ez_exit); module_exit(ez_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
HID_COMPAT_LOAD_DRIVER(ezkey);

View File

@ -181,5 +181,3 @@ static void __exit ga_exit(void)
module_init(ga_init); module_init(ga_init);
module_exit(ga_exit); module_exit(ga_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
HID_COMPAT_LOAD_DRIVER(greenasia);

View File

@ -94,5 +94,3 @@ static void gyration_exit(void)
module_init(gyration_init); module_init(gyration_init);
module_exit(gyration_exit); module_exit(gyration_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
HID_COMPAT_LOAD_DRIVER(gyration);

View File

@ -67,6 +67,9 @@
#define USB_DEVICE_ID_APPLE_GEYSER4_ANSI 0x021a #define USB_DEVICE_ID_APPLE_GEYSER4_ANSI 0x021a
#define USB_DEVICE_ID_APPLE_GEYSER4_ISO 0x021b #define USB_DEVICE_ID_APPLE_GEYSER4_ISO 0x021b
#define USB_DEVICE_ID_APPLE_GEYSER4_JIS 0x021c #define USB_DEVICE_ID_APPLE_GEYSER4_JIS 0x021c
#define USB_DEVICE_ID_APPLE_ALU_MINI_ANSI 0x021d
#define USB_DEVICE_ID_APPLE_ALU_MINI_ISO 0x021e
#define USB_DEVICE_ID_APPLE_ALU_MINI_JIS 0x021f
#define USB_DEVICE_ID_APPLE_ALU_ANSI 0x0220 #define USB_DEVICE_ID_APPLE_ALU_ANSI 0x0220
#define USB_DEVICE_ID_APPLE_ALU_ISO 0x0221 #define USB_DEVICE_ID_APPLE_ALU_ISO 0x0221
#define USB_DEVICE_ID_APPLE_ALU_JIS 0x0222 #define USB_DEVICE_ID_APPLE_ALU_JIS 0x0222
@ -148,6 +151,8 @@
#define USB_VENDOR_ID_DMI 0x0c0b #define USB_VENDOR_ID_DMI 0x0c0b
#define USB_DEVICE_ID_DMI_ENC 0x5fab #define USB_DEVICE_ID_DMI_ENC 0x5fab
#define USB_VENDOR_ID_DRAGONRISE 0x0079
#define USB_VENDOR_ID_ELO 0x04E7 #define USB_VENDOR_ID_ELO 0x04E7
#define USB_DEVICE_ID_ELO_TS2700 0x0020 #define USB_DEVICE_ID_ELO_TS2700 0x0020
@ -272,6 +277,9 @@
#define USB_DEVICE_ID_LD_POWERCONTROL 0x2030 #define USB_DEVICE_ID_LD_POWERCONTROL 0x2030
#define USB_DEVICE_ID_LD_MACHINETEST 0x2040 #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_VENDOR_ID_LOGITECH 0x046d
#define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101
#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110 #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110
@ -418,6 +426,8 @@
#define USB_VENDOR_ID_ZEROPLUS 0x0c12 #define USB_VENDOR_ID_ZEROPLUS 0x0c12
#define USB_VENDOR_ID_KYE 0x0458 #define USB_VENDOR_ID_KYE 0x0458
#define USB_DEVICE_ID_KYE_ERGO_525V 0x0087
#define USB_DEVICE_ID_KYE_GPEN_560 0x5003 #define USB_DEVICE_ID_KYE_GPEN_560 0x5003
#endif #endif

View File

@ -0,0 +1,63 @@
/*
* HID driver for Kensigton Slimblade Trackball
*
* Copyright (c) 2009 Jiri Kosina
*/
/*
* 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/input.h>
#include <linux/hid.h>
#include <linux/module.h>
#include "hid-ids.h"
#define ks_map_key(c) hid_map_usage(hi, usage, bit, max, EV_KEY, (c))
static int ks_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_MSVENDOR)
return 0;
switch (usage->hid & HID_USAGE) {
case 0x01: ks_map_key(BTN_MIDDLE); break;
case 0x02: ks_map_key(BTN_SIDE); break;
default:
return 0;
}
return 1;
}
static const struct hid_device_id ks_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) },
{ }
};
MODULE_DEVICE_TABLE(hid, ks_devices);
static struct hid_driver ks_driver = {
.name = "kensington",
.id_table = ks_devices,
.input_mapping = ks_input_mapping,
};
static int ks_init(void)
{
return hid_register_driver(&ks_driver);
}
static void ks_exit(void)
{
hid_unregister_driver(&ks_driver);
}
module_init(ks_init);
module_exit(ks_exit);
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,69 @@
/*
* HID driver for Kye/Genius devices not fully compliant with HID standard
*
* Copyright (c) 2009 Jiri Kosina
* Copyright (c) 2009 Tomas Hanak
*/
/*
* 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"
/* the fixups that need to be done:
* - change led usage page to button for extra buttons
* - report size 8 count 1 must be size 1 count 8 for button bitfield
* - change the button usage range to 4-7 for the extra buttons
*/
static void kye_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int rsize)
{
if (rsize >= 74 &&
rdesc[61] == 0x05 && rdesc[62] == 0x08 &&
rdesc[63] == 0x19 && rdesc[64] == 0x08 &&
rdesc[65] == 0x29 && rdesc[66] == 0x0f &&
rdesc[71] == 0x75 && rdesc[72] == 0x08 &&
rdesc[73] == 0x95 && rdesc[74] == 0x01) {
dev_info(&hdev->dev, "fixing up Kye/Genius Ergo Mouse report "
"descriptor\n");
rdesc[62] = 0x09;
rdesc[64] = 0x04;
rdesc[66] = 0x07;
rdesc[72] = 0x01;
rdesc[74] = 0x08;
}
}
static const struct hid_device_id kye_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) },
{ }
};
MODULE_DEVICE_TABLE(hid, kye_devices);
static struct hid_driver kye_driver = {
.name = "kye",
.id_table = kye_devices,
.report_fixup = kye_report_fixup,
};
static int kye_init(void)
{
return hid_register_driver(&kye_driver);
}
static void kye_exit(void)
{
hid_unregister_driver(&kye_driver);
}
module_init(kye_init);
module_exit(kye_exit);
MODULE_LICENSE("GPL");

View File

@ -326,5 +326,3 @@ static void lg_exit(void)
module_init(lg_init); module_init(lg_init);
module_exit(lg_exit); module_exit(lg_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
HID_COMPAT_LOAD_DRIVER(logitech);

View File

@ -210,5 +210,3 @@ static void ms_exit(void)
module_init(ms_init); module_init(ms_init);
module_exit(ms_exit); module_exit(ms_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
HID_COMPAT_LOAD_DRIVER(microsoft);

View File

@ -78,5 +78,3 @@ static void mr_exit(void)
module_init(mr_init); module_init(mr_init);
module_exit(mr_exit); module_exit(mr_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
HID_COMPAT_LOAD_DRIVER(monterey);

View File

@ -78,5 +78,3 @@ static void ntrig_exit(void)
module_init(ntrig_init); module_init(ntrig_init);
module_exit(ntrig_exit); module_exit(ntrig_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
HID_COMPAT_LOAD_DRIVER(ntrig);

View File

@ -118,5 +118,3 @@ static void pl_exit(void)
module_init(pl_init); module_init(pl_init);
module_exit(pl_exit); module_exit(pl_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
HID_COMPAT_LOAD_DRIVER(petalynx);

View File

@ -9,9 +9,12 @@
* - contains two reports, one for each port (HID_QUIRK_MULTI_INPUT) * - contains two reports, one for each port (HID_QUIRK_MULTI_INPUT)
* *
* 0e8f:0003 "GreenAsia Inc. USB Joystick " * 0e8f:0003 "GreenAsia Inc. USB Joystick "
* - tested with K??ng Gaming gamepad * - tested with König Gaming gamepad
* *
* Copyright (c) 2007 Anssi Hannula <anssi.hannula@gmail.com> * 0e8f:0003 "GASIA USB Gamepad"
* - another version of the König gamepad
*
* Copyright (c) 2007, 2009 Anssi Hannula <anssi.hannula@gmail.com>
*/ */
/* /*
@ -46,6 +49,8 @@
struct plff_device { struct plff_device {
struct hid_report *report; struct hid_report *report;
s32 *strong;
s32 *weak;
}; };
static int hid_plff_play(struct input_dev *dev, void *data, static int hid_plff_play(struct input_dev *dev, void *data,
@ -62,8 +67,8 @@ static int hid_plff_play(struct input_dev *dev, void *data,
left = left * 0x7f / 0xffff; left = left * 0x7f / 0xffff;
right = right * 0x7f / 0xffff; right = right * 0x7f / 0xffff;
plff->report->field[0]->value[2] = left; *plff->strong = left;
plff->report->field[0]->value[3] = right; *plff->weak = right;
debug("running with 0x%02x 0x%02x", left, right); debug("running with 0x%02x 0x%02x", left, right);
usbhid_submit_report(hid, plff->report, USB_DIR_OUT); usbhid_submit_report(hid, plff->report, USB_DIR_OUT);
@ -80,6 +85,8 @@ static int plff_init(struct hid_device *hid)
struct list_head *report_ptr = report_list; struct list_head *report_ptr = report_list;
struct input_dev *dev; struct input_dev *dev;
int error; int error;
s32 *strong;
s32 *weak;
/* The device contains one output report per physical device, all /* The device contains one output report per physical device, all
containing 1 field, which contains 4 ff00.0002 usages and 4 16bit containing 1 field, which contains 4 ff00.0002 usages and 4 16bit
@ -87,7 +94,12 @@ static int plff_init(struct hid_device *hid)
The input reports also contain a field which contains The input reports also contain a field which contains
8 ff00.0001 usages and 8 boolean values. Their meaning is 8 ff00.0001 usages and 8 boolean values. Their meaning is
currently unknown. */ currently unknown.
A version of the 0e8f:0003 exists that has all the values in
separate fields and misses the extra input field, thus resembling
Zeroplus (hid-zpff) devices.
*/
if (list_empty(report_list)) { if (list_empty(report_list)) {
dev_err(&hid->dev, "no output reports found\n"); dev_err(&hid->dev, "no output reports found\n");
@ -110,8 +122,21 @@ static int plff_init(struct hid_device *hid)
return -ENODEV; return -ENODEV;
} }
if (report->field[0]->report_count < 4) { if (report->field[0]->report_count >= 4) {
dev_err(&hid->dev, "not enough values in the field\n"); report->field[0]->value[0] = 0x00;
report->field[0]->value[1] = 0x00;
strong = &report->field[0]->value[2];
weak = &report->field[0]->value[3];
debug("detected single-field device");
} else if (report->maxfield >= 4 && report->field[0]->maxusage == 1 &&
report->field[0]->usage[0].hid == (HID_UP_LED | 0x43)) {
report->field[0]->value[0] = 0x00;
report->field[1]->value[0] = 0x00;
strong = &report->field[2]->value[0];
weak = &report->field[3]->value[0];
debug("detected 4-field device");
} else {
dev_err(&hid->dev, "not enough fields or values\n");
return -ENODEV; return -ENODEV;
} }
@ -130,10 +155,11 @@ static int plff_init(struct hid_device *hid)
} }
plff->report = report; plff->report = report;
plff->report->field[0]->value[0] = 0x00; plff->strong = strong;
plff->report->field[0]->value[1] = 0x00; plff->weak = weak;
plff->report->field[0]->value[2] = 0x00;
plff->report->field[0]->value[3] = 0x00; *strong = 0x00;
*weak = 0x00;
usbhid_submit_report(hid, plff->report, USB_DIR_OUT); usbhid_submit_report(hid, plff->report, USB_DIR_OUT);
} }
@ -180,7 +206,7 @@ static const struct hid_device_id pl_devices[] = {
.driver_data = 1 }, /* Twin USB Joystick */ .driver_data = 1 }, /* Twin USB Joystick */
{ HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR), { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR),
.driver_data = 1 }, /* Twin USB Joystick */ .driver_data = 1 }, /* Twin USB Joystick */
{ HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003), }, /* GreenAsia Inc. USB Joystick */ { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003), },
{ } { }
}; };
MODULE_DEVICE_TABLE(hid, pl_devices); MODULE_DEVICE_TABLE(hid, pl_devices);
@ -204,5 +230,3 @@ static void pl_exit(void)
module_init(pl_init); module_init(pl_init);
module_exit(pl_exit); module_exit(pl_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
HID_COMPAT_LOAD_DRIVER(pantherlord);

View File

@ -96,5 +96,3 @@ static void samsung_exit(void)
module_init(samsung_init); module_init(samsung_init);
module_exit(samsung_exit); module_exit(samsung_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
HID_COMPAT_LOAD_DRIVER(samsung);

View File

@ -148,5 +148,3 @@ static void sony_exit(void)
module_init(sony_init); module_init(sony_init);
module_exit(sony_exit); module_exit(sony_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
HID_COMPAT_LOAD_DRIVER(sony);

View File

@ -78,5 +78,3 @@ static void sp_exit(void)
module_init(sp_init); module_init(sp_init);
module_exit(sp_exit); module_exit(sp_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
HID_COMPAT_LOAD_DRIVER(sunplus);

View File

@ -265,5 +265,3 @@ static void tm_exit(void)
module_init(tm_init); module_init(tm_init);
module_exit(tm_exit); module_exit(tm_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
HID_COMPAT_LOAD_DRIVER(thrustmaster);

View File

@ -73,5 +73,3 @@ static void ts_exit(void)
module_init(ts_init); module_init(ts_init);
module_exit(ts_exit); module_exit(ts_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
HID_COMPAT_LOAD_DRIVER(topseed);

View File

@ -158,5 +158,3 @@ static void zp_exit(void)
module_init(zp_init); module_init(zp_init);
module_exit(zp_exit); module_exit(zp_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
HID_COMPAT_LOAD_DRIVER(zeroplus);

View File

@ -181,9 +181,17 @@ static int hidraw_open(struct inode *inode, struct file *file)
dev = hidraw_table[minor]; dev = hidraw_table[minor];
if (!dev->open++) { if (!dev->open++) {
if (dev->hid->ll_driver->power) {
err = dev->hid->ll_driver->power(dev->hid, PM_HINT_FULLON);
if (err < 0)
goto out_unlock;
}
err = dev->hid->ll_driver->open(dev->hid); err = dev->hid->ll_driver->open(dev->hid);
if (err < 0) if (err < 0) {
if (dev->hid->ll_driver->power)
dev->hid->ll_driver->power(dev->hid, PM_HINT_NORMAL);
dev->open--; dev->open--;
}
} }
out_unlock: out_unlock:
@ -209,10 +217,13 @@ static int hidraw_release(struct inode * inode, struct file * file)
list_del(&list->node); list_del(&list->node);
dev = hidraw_table[minor]; dev = hidraw_table[minor];
if (!--dev->open) { if (!--dev->open) {
if (list->hidraw->exist) if (list->hidraw->exist) {
if (dev->hid->ll_driver->power)
dev->hid->ll_driver->power(dev->hid, PM_HINT_NORMAL);
dev->hid->ll_driver->close(dev->hid); dev->hid->ll_driver->close(dev->hid);
else } else {
kfree(list->hidraw); kfree(list->hidraw);
}
} }
kfree(list); kfree(list);

View File

@ -5,6 +5,7 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2008 Jiri Kosina * Copyright (c) 2006-2008 Jiri Kosina
* Copyright (c) 2007-2008 Oliver Neukum
*/ */
/* /*
@ -27,6 +28,7 @@
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/workqueue.h>
#include <linux/usb.h> #include <linux/usb.h>
@ -53,6 +55,10 @@ static unsigned int hid_mousepoll_interval;
module_param_named(mousepoll, hid_mousepoll_interval, uint, 0644); module_param_named(mousepoll, hid_mousepoll_interval, uint, 0644);
MODULE_PARM_DESC(mousepoll, "Polling interval of mice"); MODULE_PARM_DESC(mousepoll, "Polling interval of mice");
static unsigned int ignoreled;
module_param_named(ignoreled, ignoreled, uint, 0644);
MODULE_PARM_DESC(ignoreled, "Autosuspend with active leds");
/* Quirks specified at module load time */ /* Quirks specified at module load time */
static char *quirks_param[MAX_USBHID_BOOT_QUIRKS] = { [ 0 ... (MAX_USBHID_BOOT_QUIRKS - 1) ] = NULL }; static char *quirks_param[MAX_USBHID_BOOT_QUIRKS] = { [ 0 ... (MAX_USBHID_BOOT_QUIRKS - 1) ] = NULL };
module_param_array_named(quirks, quirks_param, charp, NULL, 0444); module_param_array_named(quirks, quirks_param, charp, NULL, 0444);
@ -63,8 +69,13 @@ MODULE_PARM_DESC(quirks, "Add/modify USB HID quirks by specifying "
/* /*
* Input submission and I/O error handler. * Input submission and I/O error handler.
*/ */
static DEFINE_MUTEX(hid_open_mut);
static struct workqueue_struct *resumption_waker;
static void hid_io_error(struct hid_device *hid); static void hid_io_error(struct hid_device *hid);
static int hid_submit_out(struct hid_device *hid);
static int hid_submit_ctrl(struct hid_device *hid);
static void hid_cancel_delayed_stuff(struct usbhid_device *usbhid);
/* Start up the input URB */ /* Start up the input URB */
static int hid_start_in(struct hid_device *hid) static int hid_start_in(struct hid_device *hid)
@ -73,15 +84,16 @@ static int hid_start_in(struct hid_device *hid)
int rc = 0; int rc = 0;
struct usbhid_device *usbhid = hid->driver_data; struct usbhid_device *usbhid = hid->driver_data;
spin_lock_irqsave(&usbhid->inlock, flags); spin_lock_irqsave(&usbhid->lock, flags);
if (hid->open > 0 && !test_bit(HID_SUSPENDED, &usbhid->iofl) && if (hid->open > 0 &&
!test_bit(HID_DISCONNECTED, &usbhid->iofl) && !test_bit(HID_DISCONNECTED, &usbhid->iofl) &&
!test_bit(HID_REPORTED_IDLE, &usbhid->iofl) &&
!test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) { !test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) {
rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC); rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC);
if (rc != 0) if (rc != 0)
clear_bit(HID_IN_RUNNING, &usbhid->iofl); clear_bit(HID_IN_RUNNING, &usbhid->iofl);
} }
spin_unlock_irqrestore(&usbhid->inlock, flags); spin_unlock_irqrestore(&usbhid->lock, flags);
return rc; return rc;
} }
@ -145,7 +157,7 @@ static void hid_io_error(struct hid_device *hid)
unsigned long flags; unsigned long flags;
struct usbhid_device *usbhid = hid->driver_data; struct usbhid_device *usbhid = hid->driver_data;
spin_lock_irqsave(&usbhid->inlock, flags); spin_lock_irqsave(&usbhid->lock, flags);
/* Stop when disconnected */ /* Stop when disconnected */
if (test_bit(HID_DISCONNECTED, &usbhid->iofl)) if (test_bit(HID_DISCONNECTED, &usbhid->iofl))
@ -175,7 +187,51 @@ static void hid_io_error(struct hid_device *hid)
mod_timer(&usbhid->io_retry, mod_timer(&usbhid->io_retry,
jiffies + msecs_to_jiffies(usbhid->retry_delay)); jiffies + msecs_to_jiffies(usbhid->retry_delay));
done: done:
spin_unlock_irqrestore(&usbhid->inlock, flags); spin_unlock_irqrestore(&usbhid->lock, flags);
}
static void usbhid_mark_busy(struct usbhid_device *usbhid)
{
struct usb_interface *intf = usbhid->intf;
usb_mark_last_busy(interface_to_usbdev(intf));
}
static int usbhid_restart_out_queue(struct usbhid_device *usbhid)
{
struct hid_device *hid = usb_get_intfdata(usbhid->intf);
int kicked;
if (!hid)
return 0;
if ((kicked = (usbhid->outhead != usbhid->outtail))) {
dbg("Kicking head %d tail %d", usbhid->outhead, usbhid->outtail);
if (hid_submit_out(hid)) {
clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
wake_up(&usbhid->wait);
}
}
return kicked;
}
static int usbhid_restart_ctrl_queue(struct usbhid_device *usbhid)
{
struct hid_device *hid = usb_get_intfdata(usbhid->intf);
int kicked;
WARN_ON(hid == NULL);
if (!hid)
return 0;
if ((kicked = (usbhid->ctrlhead != usbhid->ctrltail))) {
dbg("Kicking head %d tail %d", usbhid->ctrlhead, usbhid->ctrltail);
if (hid_submit_ctrl(hid)) {
clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
wake_up(&usbhid->wait);
}
}
return kicked;
} }
/* /*
@ -190,12 +246,23 @@ static void hid_irq_in(struct urb *urb)
switch (urb->status) { switch (urb->status) {
case 0: /* success */ case 0: /* success */
usbhid_mark_busy(usbhid);
usbhid->retry_delay = 0; usbhid->retry_delay = 0;
hid_input_report(urb->context, HID_INPUT_REPORT, hid_input_report(urb->context, HID_INPUT_REPORT,
urb->transfer_buffer, urb->transfer_buffer,
urb->actual_length, 1); urb->actual_length, 1);
/*
* autosuspend refused while keys are pressed
* because most keyboards don't wake up when
* a key is released
*/
if (hid_check_keys_pressed(hid))
set_bit(HID_KEYS_PRESSED, &usbhid->iofl);
else
clear_bit(HID_KEYS_PRESSED, &usbhid->iofl);
break; break;
case -EPIPE: /* stall */ case -EPIPE: /* stall */
usbhid_mark_busy(usbhid);
clear_bit(HID_IN_RUNNING, &usbhid->iofl); clear_bit(HID_IN_RUNNING, &usbhid->iofl);
set_bit(HID_CLEAR_HALT, &usbhid->iofl); set_bit(HID_CLEAR_HALT, &usbhid->iofl);
schedule_work(&usbhid->reset_work); schedule_work(&usbhid->reset_work);
@ -209,6 +276,7 @@ static void hid_irq_in(struct urb *urb)
case -EPROTO: /* protocol error or unplug */ case -EPROTO: /* protocol error or unplug */
case -ETIME: /* protocol error or unplug */ case -ETIME: /* protocol error or unplug */
case -ETIMEDOUT: /* Should never happen, but... */ case -ETIMEDOUT: /* Should never happen, but... */
usbhid_mark_busy(usbhid);
clear_bit(HID_IN_RUNNING, &usbhid->iofl); clear_bit(HID_IN_RUNNING, &usbhid->iofl);
hid_io_error(hid); hid_io_error(hid);
return; return;
@ -239,16 +307,25 @@ static int hid_submit_out(struct hid_device *hid)
report = usbhid->out[usbhid->outtail].report; report = usbhid->out[usbhid->outtail].report;
raw_report = usbhid->out[usbhid->outtail].raw_report; raw_report = usbhid->out[usbhid->outtail].raw_report;
usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0); if (!test_bit(HID_REPORTED_IDLE, &usbhid->iofl)) {
usbhid->urbout->dev = hid_to_usb_dev(hid); usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0);
memcpy(usbhid->outbuf, raw_report, usbhid->urbout->transfer_buffer_length); usbhid->urbout->dev = hid_to_usb_dev(hid);
kfree(raw_report); memcpy(usbhid->outbuf, raw_report, usbhid->urbout->transfer_buffer_length);
kfree(raw_report);
dbg_hid("submitting out urb\n"); dbg_hid("submitting out urb\n");
if (usb_submit_urb(usbhid->urbout, GFP_ATOMIC)) { if (usb_submit_urb(usbhid->urbout, GFP_ATOMIC)) {
err_hid("usb_submit_urb(out) failed"); err_hid("usb_submit_urb(out) failed");
return -1; return -1;
}
} else {
/*
* queue work to wake up the device.
* as the work queue is freezeable, this is safe
* with respect to STD and STR
*/
queue_work(resumption_waker, &usbhid->restart_work);
} }
return 0; return 0;
@ -266,41 +343,50 @@ static int hid_submit_ctrl(struct hid_device *hid)
raw_report = usbhid->ctrl[usbhid->ctrltail].raw_report; raw_report = usbhid->ctrl[usbhid->ctrltail].raw_report;
dir = usbhid->ctrl[usbhid->ctrltail].dir; dir = usbhid->ctrl[usbhid->ctrltail].dir;
len = ((report->size - 1) >> 3) + 1 + (report->id > 0); if (!test_bit(HID_REPORTED_IDLE, &usbhid->iofl)) {
if (dir == USB_DIR_OUT) { len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0); if (dir == USB_DIR_OUT) {
usbhid->urbctrl->transfer_buffer_length = len; usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0);
memcpy(usbhid->ctrlbuf, raw_report, len); usbhid->urbctrl->transfer_buffer_length = len;
kfree(raw_report); memcpy(usbhid->ctrlbuf, raw_report, len);
kfree(raw_report);
} else {
int maxpacket, padlen;
usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0);
maxpacket = usb_maxpacket(hid_to_usb_dev(hid), usbhid->urbctrl->pipe, 0);
if (maxpacket > 0) {
padlen = DIV_ROUND_UP(len, maxpacket);
padlen *= maxpacket;
if (padlen > usbhid->bufsize)
padlen = usbhid->bufsize;
} else
padlen = 0;
usbhid->urbctrl->transfer_buffer_length = padlen;
}
usbhid->urbctrl->dev = hid_to_usb_dev(hid);
usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir;
usbhid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : HID_REQ_GET_REPORT;
usbhid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) | report->id);
usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum);
usbhid->cr->wLength = cpu_to_le16(len);
dbg_hid("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u\n",
usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report",
usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength);
if (usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC)) {
err_hid("usb_submit_urb(ctrl) failed");
return -1;
}
} else { } else {
int maxpacket, padlen; /*
* queue work to wake up the device.
usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0); * as the work queue is freezeable, this is safe
maxpacket = usb_maxpacket(hid_to_usb_dev(hid), usbhid->urbctrl->pipe, 0); * with respect to STD and STR
if (maxpacket > 0) { */
padlen = DIV_ROUND_UP(len, maxpacket); queue_work(resumption_waker, &usbhid->restart_work);
padlen *= maxpacket;
if (padlen > usbhid->bufsize)
padlen = usbhid->bufsize;
} else
padlen = 0;
usbhid->urbctrl->transfer_buffer_length = padlen;
}
usbhid->urbctrl->dev = hid_to_usb_dev(hid);
usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir;
usbhid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : HID_REQ_GET_REPORT;
usbhid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) | report->id);
usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum);
usbhid->cr->wLength = cpu_to_le16(len);
dbg_hid("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u\n",
usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report",
usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength);
if (usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC)) {
err_hid("usb_submit_urb(ctrl) failed");
return -1;
} }
return 0; return 0;
@ -332,7 +418,7 @@ static void hid_irq_out(struct urb *urb)
"received\n", urb->status); "received\n", urb->status);
} }
spin_lock_irqsave(&usbhid->outlock, flags); spin_lock_irqsave(&usbhid->lock, flags);
if (unplug) if (unplug)
usbhid->outtail = usbhid->outhead; usbhid->outtail = usbhid->outhead;
@ -344,12 +430,12 @@ static void hid_irq_out(struct urb *urb)
clear_bit(HID_OUT_RUNNING, &usbhid->iofl); clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
wake_up(&usbhid->wait); wake_up(&usbhid->wait);
} }
spin_unlock_irqrestore(&usbhid->outlock, flags); spin_unlock_irqrestore(&usbhid->lock, flags);
return; return;
} }
clear_bit(HID_OUT_RUNNING, &usbhid->iofl); clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
spin_unlock_irqrestore(&usbhid->outlock, flags); spin_unlock_irqrestore(&usbhid->lock, flags);
wake_up(&usbhid->wait); wake_up(&usbhid->wait);
} }
@ -361,12 +447,11 @@ static void hid_ctrl(struct urb *urb)
{ {
struct hid_device *hid = urb->context; struct hid_device *hid = urb->context;
struct usbhid_device *usbhid = hid->driver_data; struct usbhid_device *usbhid = hid->driver_data;
unsigned long flags; int unplug = 0, status = urb->status;
int unplug = 0;
spin_lock_irqsave(&usbhid->ctrllock, flags); spin_lock(&usbhid->lock);
switch (urb->status) { switch (status) {
case 0: /* success */ case 0: /* success */
if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN) if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN)
hid_input_report(urb->context, hid_input_report(urb->context,
@ -383,7 +468,7 @@ static void hid_ctrl(struct urb *urb)
break; break;
default: /* error */ default: /* error */
dev_warn(&urb->dev->dev, "ctrl urb status %d " dev_warn(&urb->dev->dev, "ctrl urb status %d "
"received\n", urb->status); "received\n", status);
} }
if (unplug) if (unplug)
@ -396,19 +481,18 @@ static void hid_ctrl(struct urb *urb)
clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
wake_up(&usbhid->wait); wake_up(&usbhid->wait);
} }
spin_unlock_irqrestore(&usbhid->ctrllock, flags); spin_unlock(&usbhid->lock);
return; return;
} }
clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
spin_unlock_irqrestore(&usbhid->ctrllock, flags); spin_unlock(&usbhid->lock);
wake_up(&usbhid->wait); wake_up(&usbhid->wait);
} }
void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir) void __usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir)
{ {
int head; int head;
unsigned long flags;
struct usbhid_device *usbhid = hid->driver_data; struct usbhid_device *usbhid = hid->driver_data;
int len = ((report->size - 1) >> 3) + 1 + (report->id > 0); int len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
@ -416,18 +500,13 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns
return; return;
if (usbhid->urbout && dir == USB_DIR_OUT && report->type == HID_OUTPUT_REPORT) { if (usbhid->urbout && dir == USB_DIR_OUT && report->type == HID_OUTPUT_REPORT) {
spin_lock_irqsave(&usbhid->outlock, flags);
if ((head = (usbhid->outhead + 1) & (HID_OUTPUT_FIFO_SIZE - 1)) == usbhid->outtail) { if ((head = (usbhid->outhead + 1) & (HID_OUTPUT_FIFO_SIZE - 1)) == usbhid->outtail) {
spin_unlock_irqrestore(&usbhid->outlock, flags);
dev_warn(&hid->dev, "output queue full\n"); dev_warn(&hid->dev, "output queue full\n");
return; return;
} }
usbhid->out[usbhid->outhead].raw_report = kmalloc(len, GFP_ATOMIC); usbhid->out[usbhid->outhead].raw_report = kmalloc(len, GFP_ATOMIC);
if (!usbhid->out[usbhid->outhead].raw_report) { if (!usbhid->out[usbhid->outhead].raw_report) {
spin_unlock_irqrestore(&usbhid->outlock, flags);
dev_warn(&hid->dev, "output queueing failed\n"); dev_warn(&hid->dev, "output queueing failed\n");
return; return;
} }
@ -438,15 +517,10 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns
if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl))
if (hid_submit_out(hid)) if (hid_submit_out(hid))
clear_bit(HID_OUT_RUNNING, &usbhid->iofl); clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
spin_unlock_irqrestore(&usbhid->outlock, flags);
return; return;
} }
spin_lock_irqsave(&usbhid->ctrllock, flags);
if ((head = (usbhid->ctrlhead + 1) & (HID_CONTROL_FIFO_SIZE - 1)) == usbhid->ctrltail) { if ((head = (usbhid->ctrlhead + 1) & (HID_CONTROL_FIFO_SIZE - 1)) == usbhid->ctrltail) {
spin_unlock_irqrestore(&usbhid->ctrllock, flags);
dev_warn(&hid->dev, "control queue full\n"); dev_warn(&hid->dev, "control queue full\n");
return; return;
} }
@ -454,7 +528,6 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns
if (dir == USB_DIR_OUT) { if (dir == USB_DIR_OUT) {
usbhid->ctrl[usbhid->ctrlhead].raw_report = kmalloc(len, GFP_ATOMIC); usbhid->ctrl[usbhid->ctrlhead].raw_report = kmalloc(len, GFP_ATOMIC);
if (!usbhid->ctrl[usbhid->ctrlhead].raw_report) { if (!usbhid->ctrl[usbhid->ctrlhead].raw_report) {
spin_unlock_irqrestore(&usbhid->ctrllock, flags);
dev_warn(&hid->dev, "control queueing failed\n"); dev_warn(&hid->dev, "control queueing failed\n");
return; return;
} }
@ -467,15 +540,25 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns
if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl)) if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl))
if (hid_submit_ctrl(hid)) if (hid_submit_ctrl(hid))
clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
}
spin_unlock_irqrestore(&usbhid->ctrllock, flags); void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir)
{
struct usbhid_device *usbhid = hid->driver_data;
unsigned long flags;
spin_lock_irqsave(&usbhid->lock, flags);
__usbhid_submit_report(hid, report, dir);
spin_unlock_irqrestore(&usbhid->lock, flags);
} }
EXPORT_SYMBOL_GPL(usbhid_submit_report); EXPORT_SYMBOL_GPL(usbhid_submit_report);
static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{ {
struct hid_device *hid = input_get_drvdata(dev); struct hid_device *hid = input_get_drvdata(dev);
struct usbhid_device *usbhid = hid->driver_data;
struct hid_field *field; struct hid_field *field;
unsigned long flags;
int offset; int offset;
if (type == EV_FF) if (type == EV_FF)
@ -490,6 +573,15 @@ static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, un
} }
hid_set_field(field, offset, value); hid_set_field(field, offset, value);
if (value) {
spin_lock_irqsave(&usbhid->lock, flags);
usbhid->ledcount++;
spin_unlock_irqrestore(&usbhid->lock, flags);
} else {
spin_lock_irqsave(&usbhid->lock, flags);
usbhid->ledcount--;
spin_unlock_irqrestore(&usbhid->lock, flags);
}
usbhid_submit_report(hid, field->report, USB_DIR_OUT); usbhid_submit_report(hid, field->report, USB_DIR_OUT);
return 0; return 0;
@ -538,15 +630,22 @@ int usbhid_open(struct hid_device *hid)
struct usbhid_device *usbhid = hid->driver_data; struct usbhid_device *usbhid = hid->driver_data;
int res; int res;
mutex_lock(&hid_open_mut);
if (!hid->open++) { if (!hid->open++) {
res = usb_autopm_get_interface(usbhid->intf); res = usb_autopm_get_interface(usbhid->intf);
/* the device must be awake to reliable request remote wakeup */
if (res < 0) { if (res < 0) {
hid->open--; hid->open--;
mutex_unlock(&hid_open_mut);
return -EIO; return -EIO;
} }
usbhid->intf->needs_remote_wakeup = 1;
if (hid_start_in(hid))
hid_io_error(hid);
usb_autopm_put_interface(usbhid->intf);
} }
if (hid_start_in(hid)) mutex_unlock(&hid_open_mut);
hid_io_error(hid);
return 0; return 0;
} }
@ -554,10 +653,22 @@ void usbhid_close(struct hid_device *hid)
{ {
struct usbhid_device *usbhid = hid->driver_data; struct usbhid_device *usbhid = hid->driver_data;
mutex_lock(&hid_open_mut);
/* protecting hid->open to make sure we don't restart
* data acquistion due to a resumption we no longer
* care about
*/
spin_lock_irq(&usbhid->lock);
if (!--hid->open) { if (!--hid->open) {
spin_unlock_irq(&usbhid->lock);
usb_kill_urb(usbhid->urbin); usb_kill_urb(usbhid->urbin);
usb_autopm_put_interface(usbhid->intf); flush_scheduled_work();
usbhid->intf->needs_remote_wakeup = 0;
} else {
spin_unlock_irq(&usbhid->lock);
} }
mutex_unlock(&hid_open_mut);
} }
/* /*
@ -687,6 +798,25 @@ static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t co
return ret; return ret;
} }
static void usbhid_restart_queues(struct usbhid_device *usbhid)
{
if (usbhid->urbout)
usbhid_restart_out_queue(usbhid);
usbhid_restart_ctrl_queue(usbhid);
}
static void __usbhid_restart_queues(struct work_struct *work)
{
struct usbhid_device *usbhid =
container_of(work, struct usbhid_device, restart_work);
int r;
r = usb_autopm_get_interface(usbhid->intf);
if (r < 0)
return;
usb_autopm_put_interface(usbhid->intf);
}
static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid) static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid)
{ {
struct usbhid_device *usbhid = hid->driver_data; struct usbhid_device *usbhid = hid->driver_data;
@ -711,6 +841,9 @@ static int usbhid_parse(struct hid_device *hid)
quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor), quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct)); le16_to_cpu(dev->descriptor.idProduct));
if (quirks & HID_QUIRK_IGNORE)
return -ENODEV;
/* Many keyboards and mice don't like to be polled for reports, /* Many keyboards and mice don't like to be polled for reports,
* so we will always set the HID_QUIRK_NOGET flag for them. */ * so we will always set the HID_QUIRK_NOGET flag for them. */
if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT) { if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT) {
@ -850,11 +983,11 @@ static int usbhid_start(struct hid_device *hid)
init_waitqueue_head(&usbhid->wait); init_waitqueue_head(&usbhid->wait);
INIT_WORK(&usbhid->reset_work, hid_reset); INIT_WORK(&usbhid->reset_work, hid_reset);
INIT_WORK(&usbhid->restart_work, __usbhid_restart_queues);
setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid); setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid);
spin_lock_init(&usbhid->inlock); spin_lock_init(&usbhid->lock);
spin_lock_init(&usbhid->outlock); spin_lock_init(&usbhid->lock);
spin_lock_init(&usbhid->ctrllock);
usbhid->intf = intf; usbhid->intf = intf;
usbhid->ifnum = interface->desc.bInterfaceNumber; usbhid->ifnum = interface->desc.bInterfaceNumber;
@ -906,15 +1039,14 @@ static void usbhid_stop(struct hid_device *hid)
return; return;
clear_bit(HID_STARTED, &usbhid->iofl); clear_bit(HID_STARTED, &usbhid->iofl);
spin_lock_irq(&usbhid->inlock); /* Sync with error handler */ spin_lock_irq(&usbhid->lock); /* Sync with error handler */
set_bit(HID_DISCONNECTED, &usbhid->iofl); set_bit(HID_DISCONNECTED, &usbhid->iofl);
spin_unlock_irq(&usbhid->inlock); spin_unlock_irq(&usbhid->lock);
usb_kill_urb(usbhid->urbin); usb_kill_urb(usbhid->urbin);
usb_kill_urb(usbhid->urbout); usb_kill_urb(usbhid->urbout);
usb_kill_urb(usbhid->urbctrl); usb_kill_urb(usbhid->urbctrl);
del_timer_sync(&usbhid->io_retry); hid_cancel_delayed_stuff(usbhid);
cancel_work_sync(&usbhid->reset_work);
if (hid->claimed & HID_CLAIMED_INPUT) if (hid->claimed & HID_CLAIMED_INPUT)
hidinput_disconnect(hid); hidinput_disconnect(hid);
@ -935,12 +1067,28 @@ static void usbhid_stop(struct hid_device *hid)
hid_free_buffers(hid_to_usb_dev(hid), hid); hid_free_buffers(hid_to_usb_dev(hid), hid);
} }
static int usbhid_power(struct hid_device *hid, int lvl)
{
int r = 0;
switch (lvl) {
case PM_HINT_FULLON:
r = usbhid_get_power(hid);
break;
case PM_HINT_NORMAL:
usbhid_put_power(hid);
break;
}
return r;
}
static struct hid_ll_driver usb_hid_driver = { static struct hid_ll_driver usb_hid_driver = {
.parse = usbhid_parse, .parse = usbhid_parse,
.start = usbhid_start, .start = usbhid_start,
.stop = usbhid_stop, .stop = usbhid_stop,
.open = usbhid_open, .open = usbhid_open,
.close = usbhid_close, .close = usbhid_close,
.power = usbhid_power,
.hidinput_input_event = usb_hidinput_input_event, .hidinput_input_event = usb_hidinput_input_event,
}; };
@ -1049,19 +1197,126 @@ static void hid_disconnect(struct usb_interface *intf)
kfree(usbhid); kfree(usbhid);
} }
static int hid_suspend(struct usb_interface *intf, pm_message_t message) static void hid_cancel_delayed_stuff(struct usbhid_device *usbhid)
{ {
struct hid_device *hid = usb_get_intfdata (intf); del_timer_sync(&usbhid->io_retry);
cancel_work_sync(&usbhid->restart_work);
cancel_work_sync(&usbhid->reset_work);
}
static void hid_cease_io(struct usbhid_device *usbhid)
{
del_timer(&usbhid->io_retry);
usb_kill_urb(usbhid->urbin);
usb_kill_urb(usbhid->urbctrl);
usb_kill_urb(usbhid->urbout);
}
/* Treat USB reset pretty much the same as suspend/resume */
static int hid_pre_reset(struct usb_interface *intf)
{
struct hid_device *hid = usb_get_intfdata(intf);
struct usbhid_device *usbhid = hid->driver_data; struct usbhid_device *usbhid = hid->driver_data;
if (!test_bit(HID_STARTED, &usbhid->iofl)) spin_lock_irq(&usbhid->lock);
return 0; set_bit(HID_RESET_PENDING, &usbhid->iofl);
spin_unlock_irq(&usbhid->lock);
cancel_work_sync(&usbhid->restart_work);
hid_cease_io(usbhid);
spin_lock_irq(&usbhid->inlock); /* Sync with error handler */ return 0;
set_bit(HID_SUSPENDED, &usbhid->iofl); }
spin_unlock_irq(&usbhid->inlock);
del_timer_sync(&usbhid->io_retry); /* Same routine used for post_reset and reset_resume */
usb_kill_urb(usbhid->urbin); static int hid_post_reset(struct usb_interface *intf)
{
struct usb_device *dev = interface_to_usbdev (intf);
struct hid_device *hid = usb_get_intfdata(intf);
struct usbhid_device *usbhid = hid->driver_data;
int status;
spin_lock_irq(&usbhid->lock);
clear_bit(HID_RESET_PENDING, &usbhid->iofl);
spin_unlock_irq(&usbhid->lock);
hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0);
/* FIXME: Any more reinitialization needed? */
status = hid_start_in(hid);
if (status < 0)
hid_io_error(hid);
usbhid_restart_queues(usbhid);
return 0;
}
int usbhid_get_power(struct hid_device *hid)
{
struct usbhid_device *usbhid = hid->driver_data;
return usb_autopm_get_interface(usbhid->intf);
}
void usbhid_put_power(struct hid_device *hid)
{
struct usbhid_device *usbhid = hid->driver_data;
usb_autopm_put_interface(usbhid->intf);
}
#ifdef CONFIG_PM
static int hid_suspend(struct usb_interface *intf, pm_message_t message)
{
struct hid_device *hid = usb_get_intfdata(intf);
struct usbhid_device *usbhid = hid->driver_data;
struct usb_device *udev = interface_to_usbdev(intf);
int status;
if (udev->auto_pm) {
spin_lock_irq(&usbhid->lock); /* Sync with error handler */
if (!test_bit(HID_RESET_PENDING, &usbhid->iofl)
&& !test_bit(HID_CLEAR_HALT, &usbhid->iofl)
&& !test_bit(HID_OUT_RUNNING, &usbhid->iofl)
&& !test_bit(HID_CTRL_RUNNING, &usbhid->iofl)
&& !test_bit(HID_KEYS_PRESSED, &usbhid->iofl)
&& (!usbhid->ledcount || ignoreled))
{
set_bit(HID_REPORTED_IDLE, &usbhid->iofl);
spin_unlock_irq(&usbhid->lock);
} else {
usbhid_mark_busy(usbhid);
spin_unlock_irq(&usbhid->lock);
return -EBUSY;
}
} else {
spin_lock_irq(&usbhid->lock);
set_bit(HID_REPORTED_IDLE, &usbhid->iofl);
spin_unlock_irq(&usbhid->lock);
if (usbhid_wait_io(hid) < 0)
return -EIO;
}
if (!ignoreled && udev->auto_pm) {
spin_lock_irq(&usbhid->lock);
if (test_bit(HID_LED_ON, &usbhid->iofl)) {
spin_unlock_irq(&usbhid->lock);
usbhid_mark_busy(usbhid);
return -EBUSY;
}
spin_unlock_irq(&usbhid->lock);
}
hid_cancel_delayed_stuff(usbhid);
hid_cease_io(usbhid);
if (udev->auto_pm && test_bit(HID_KEYS_PRESSED, &usbhid->iofl)) {
/* lost race against keypresses */
status = hid_start_in(hid);
if (status < 0)
hid_io_error(hid);
usbhid_mark_busy(usbhid);
return -EBUSY;
}
dev_dbg(&intf->dev, "suspend\n"); dev_dbg(&intf->dev, "suspend\n");
return 0; return 0;
} }
@ -1075,32 +1330,33 @@ static int hid_resume(struct usb_interface *intf)
if (!test_bit(HID_STARTED, &usbhid->iofl)) if (!test_bit(HID_STARTED, &usbhid->iofl))
return 0; return 0;
clear_bit(HID_SUSPENDED, &usbhid->iofl); clear_bit(HID_REPORTED_IDLE, &usbhid->iofl);
usbhid_mark_busy(usbhid);
if (test_bit(HID_CLEAR_HALT, &usbhid->iofl) ||
test_bit(HID_RESET_PENDING, &usbhid->iofl))
schedule_work(&usbhid->reset_work);
usbhid->retry_delay = 0; usbhid->retry_delay = 0;
status = hid_start_in(hid); status = hid_start_in(hid);
dev_dbg(&intf->dev, "resume status %d\n", status); if (status < 0)
return status; hid_io_error(hid);
} usbhid_restart_queues(usbhid);
/* Treat USB reset pretty much the same as suspend/resume */ dev_dbg(&intf->dev, "resume status %d\n", status);
static int hid_pre_reset(struct usb_interface *intf)
{
/* FIXME: What if the interface is already suspended? */
hid_suspend(intf, PMSG_ON);
return 0; return 0;
} }
/* Same routine used for post_reset and reset_resume */ static int hid_reset_resume(struct usb_interface *intf)
static int hid_post_reset(struct usb_interface *intf)
{ {
struct usb_device *dev = interface_to_usbdev (intf); struct hid_device *hid = usb_get_intfdata(intf);
struct usbhid_device *usbhid = hid->driver_data;
hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0); clear_bit(HID_REPORTED_IDLE, &usbhid->iofl);
/* FIXME: Any more reinitialization needed? */ return hid_post_reset(intf);
return hid_resume(intf);
} }
#endif /* CONFIG_PM */
static struct usb_device_id hid_usb_ids [] = { static struct usb_device_id hid_usb_ids [] = {
{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS, { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
.bInterfaceClass = USB_INTERFACE_CLASS_HID }, .bInterfaceClass = USB_INTERFACE_CLASS_HID },
@ -1113,9 +1369,11 @@ static struct usb_driver hid_driver = {
.name = "usbhid", .name = "usbhid",
.probe = hid_probe, .probe = hid_probe,
.disconnect = hid_disconnect, .disconnect = hid_disconnect,
#ifdef CONFIG_PM
.suspend = hid_suspend, .suspend = hid_suspend,
.resume = hid_resume, .resume = hid_resume,
.reset_resume = hid_post_reset, .reset_resume = hid_reset_resume,
#endif
.pre_reset = hid_pre_reset, .pre_reset = hid_pre_reset,
.post_reset = hid_post_reset, .post_reset = hid_post_reset,
.id_table = hid_usb_ids, .id_table = hid_usb_ids,
@ -1134,7 +1392,11 @@ static struct hid_driver hid_usb_driver = {
static int __init hid_init(void) static int __init hid_init(void)
{ {
int retval; int retval = -ENOMEM;
resumption_waker = create_freezeable_workqueue("usbhid_resumer");
if (!resumption_waker)
goto no_queue;
retval = hid_register_driver(&hid_usb_driver); retval = hid_register_driver(&hid_usb_driver);
if (retval) if (retval)
goto hid_register_fail; goto hid_register_fail;
@ -1158,6 +1420,8 @@ hiddev_init_fail:
usbhid_quirks_init_fail: usbhid_quirks_init_fail:
hid_unregister_driver(&hid_usb_driver); hid_unregister_driver(&hid_usb_driver);
hid_register_fail: hid_register_fail:
destroy_workqueue(resumption_waker);
no_queue:
return retval; return retval;
} }
@ -1167,6 +1431,7 @@ static void __exit hid_exit(void)
hiddev_exit(); hiddev_exit();
usbhid_quirks_exit(); usbhid_quirks_exit();
hid_unregister_driver(&hid_usb_driver); hid_unregister_driver(&hid_usb_driver);
destroy_workqueue(resumption_waker);
} }
module_init(hid_init); module_init(hid_init);

View File

@ -246,10 +246,12 @@ static int hiddev_release(struct inode * inode, struct file * file)
spin_unlock_irqrestore(&list->hiddev->list_lock, flags); spin_unlock_irqrestore(&list->hiddev->list_lock, flags);
if (!--list->hiddev->open) { if (!--list->hiddev->open) {
if (list->hiddev->exist) if (list->hiddev->exist) {
usbhid_close(list->hiddev->hid); usbhid_close(list->hiddev->hid);
else usbhid_put_power(list->hiddev->hid);
} else {
kfree(list->hiddev); kfree(list->hiddev);
}
} }
kfree(list); kfree(list);
@ -300,6 +302,17 @@ static int hiddev_open(struct inode *inode, struct file *file)
list_add_tail(&list->node, &hiddev_table[i]->list); list_add_tail(&list->node, &hiddev_table[i]->list);
spin_unlock_irq(&list->hiddev->list_lock); spin_unlock_irq(&list->hiddev->list_lock);
if (!list->hiddev->open++)
if (list->hiddev->exist) {
struct hid_device *hid = hiddev_table[i]->hid;
res = usbhid_get_power(hid);
if (res < 0) {
res = -EIO;
goto bail;
}
usbhid_open(hid);
}
return 0; return 0;
bail: bail:
file->private_data = NULL; file->private_data = NULL;
@ -875,16 +888,21 @@ int hiddev_connect(struct hid_device *hid, unsigned int force)
hiddev->hid = hid; hiddev->hid = hid;
hiddev->exist = 1; hiddev->exist = 1;
/* when lock_kernel() usage is fixed in usb_open(),
* we could also fix it here */
lock_kernel();
retval = usb_register_dev(usbhid->intf, &hiddev_class); retval = usb_register_dev(usbhid->intf, &hiddev_class);
if (retval) { if (retval) {
err_hid("Not able to get a minor for this device."); err_hid("Not able to get a minor for this device.");
hid->hiddev = NULL; hid->hiddev = NULL;
unlock_kernel();
kfree(hiddev); kfree(hiddev);
return -1; return -1;
} else { } else {
hid->minor = usbhid->intf->minor; hid->minor = usbhid->intf->minor;
hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev; hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev;
} }
unlock_kernel();
return 0; return 0;
} }

View File

@ -38,7 +38,10 @@ int usbhid_wait_io(struct hid_device* hid);
void usbhid_close(struct hid_device *hid); void usbhid_close(struct hid_device *hid);
int usbhid_open(struct hid_device *hid); int usbhid_open(struct hid_device *hid);
void usbhid_init_reports(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); void usbhid_submit_report
(struct hid_device *hid, struct hid_report *report, unsigned char dir);
int usbhid_get_power(struct hid_device *hid);
void usbhid_put_power(struct hid_device *hid);
/* iofl flags */ /* iofl flags */
#define HID_CTRL_RUNNING 1 #define HID_CTRL_RUNNING 1
@ -49,6 +52,9 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns
#define HID_CLEAR_HALT 6 #define HID_CLEAR_HALT 6
#define HID_DISCONNECTED 7 #define HID_DISCONNECTED 7
#define HID_STARTED 8 #define HID_STARTED 8
#define HID_REPORTED_IDLE 9
#define HID_KEYS_PRESSED 10
#define HID_LED_ON 11
/* /*
* USB-specific HID struct, to be pointed to * USB-specific HID struct, to be pointed to
@ -66,7 +72,6 @@ struct usbhid_device {
struct urb *urbin; /* Input URB */ struct urb *urbin; /* Input URB */
char *inbuf; /* Input buffer */ char *inbuf; /* Input buffer */
dma_addr_t inbuf_dma; /* Input buffer dma */ dma_addr_t inbuf_dma; /* Input buffer dma */
spinlock_t inlock; /* Input fifo spinlock */
struct urb *urbctrl; /* Control URB */ struct urb *urbctrl; /* Control URB */
struct usb_ctrlrequest *cr; /* Control request struct */ struct usb_ctrlrequest *cr; /* Control request struct */
@ -75,21 +80,22 @@ struct usbhid_device {
unsigned char ctrlhead, ctrltail; /* Control fifo head & tail */ unsigned char ctrlhead, ctrltail; /* Control fifo head & tail */
char *ctrlbuf; /* Control buffer */ char *ctrlbuf; /* Control buffer */
dma_addr_t ctrlbuf_dma; /* Control buffer dma */ dma_addr_t ctrlbuf_dma; /* Control buffer dma */
spinlock_t ctrllock; /* Control fifo spinlock */
struct urb *urbout; /* Output URB */ struct urb *urbout; /* Output URB */
struct hid_output_fifo out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */ struct hid_output_fifo out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */
unsigned char outhead, outtail; /* Output pipe fifo head & tail */ unsigned char outhead, outtail; /* Output pipe fifo head & tail */
char *outbuf; /* Output buffer */ char *outbuf; /* Output buffer */
dma_addr_t outbuf_dma; /* Output buffer dma */ dma_addr_t outbuf_dma; /* Output buffer dma */
spinlock_t outlock; /* Output fifo spinlock */
spinlock_t lock; /* fifo spinlock */
unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */ unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */
struct timer_list io_retry; /* Retry timer */ struct timer_list io_retry; /* Retry timer */
unsigned long stop_retry; /* Time to give up, in jiffies */ unsigned long stop_retry; /* Time to give up, in jiffies */
unsigned int retry_delay; /* Delay length in ms */ unsigned int retry_delay; /* Delay length in ms */
struct work_struct reset_work; /* Task context for resets */ struct work_struct reset_work; /* Task context for resets */
struct work_struct restart_work; /* waking up for output to be done in a task */
wait_queue_head_t wait; /* For sleeping */ wait_queue_head_t wait; /* For sleeping */
int ledcount; /* counting the number of active leds */
}; };
#define hid_to_usb_dev(hid_dev) \ #define hid_to_usb_dev(hid_dev) \

View File

@ -270,6 +270,7 @@ struct hid_item {
#define HID_QUIRK_INVERT 0x00000001 #define HID_QUIRK_INVERT 0x00000001
#define HID_QUIRK_NOTOUCH 0x00000002 #define HID_QUIRK_NOTOUCH 0x00000002
#define HID_QUIRK_IGNORE 0x00000004
#define HID_QUIRK_NOGET 0x00000008 #define HID_QUIRK_NOGET 0x00000008
#define HID_QUIRK_BADPAD 0x00000020 #define HID_QUIRK_BADPAD 0x00000020
#define HID_QUIRK_MULTI_INPUT 0x00000040 #define HID_QUIRK_MULTI_INPUT 0x00000040
@ -603,12 +604,17 @@ struct hid_ll_driver {
int (*open)(struct hid_device *hdev); int (*open)(struct hid_device *hdev);
void (*close)(struct hid_device *hdev); void (*close)(struct hid_device *hdev);
int (*power)(struct hid_device *hdev, int level);
int (*hidinput_input_event) (struct input_dev *idev, unsigned int type, int (*hidinput_input_event) (struct input_dev *idev, unsigned int type,
unsigned int code, int value); unsigned int code, int value);
int (*parse)(struct hid_device *hdev); int (*parse)(struct hid_device *hdev);
}; };
#define PM_HINT_FULLON 1<<5
#define PM_HINT_NORMAL 1<<1
/* Applications from HID Usage Tables 4/8/99 Version 1.1 */ /* Applications from HID Usage Tables 4/8/99 Version 1.1 */
/* We ignore a few input applications that are not widely used */ /* We ignore a few input applications that are not widely used */
#define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001) || (a == 0x000d0002)) #define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001) || (a == 0x000d0002))
@ -641,6 +647,7 @@ int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int
void hid_output_report(struct hid_report *report, __u8 *data); void hid_output_report(struct hid_report *report, __u8 *data);
struct hid_device *hid_allocate_device(void); struct hid_device *hid_allocate_device(void);
int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size); int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size);
int hid_check_keys_pressed(struct hid_device *hid);
int hid_connect(struct hid_device *hid, unsigned int connect_mask); int hid_connect(struct hid_device *hid, unsigned int connect_mask);
/** /**
@ -791,21 +798,5 @@ dbg_hid(const char *fmt, ...)
__FILE__ , ## arg) __FILE__ , ## arg)
#endif /* HID_FF */ #endif /* HID_FF */
#ifdef __KERNEL__
#ifdef CONFIG_HID_COMPAT
#define HID_COMPAT_LOAD_DRIVER(name) \
/* prototype to avoid sparse warning */ \
extern void hid_compat_##name(void); \
void hid_compat_##name(void) { } \
EXPORT_SYMBOL(hid_compat_##name)
#else
#define HID_COMPAT_LOAD_DRIVER(name)
#endif /* HID_COMPAT */
#define HID_COMPAT_CALL_DRIVER(name) do { \
extern void hid_compat_##name(void); \
hid_compat_##name(); \
} while (0)
#endif /* __KERNEL__ */
#endif #endif