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
This commit is contained in:
Коммит
5fba0925fd
|
@ -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()
|
||||
When: October 2009
|
||||
Why: The %pF vsprintf format provides the same functionality in a
|
||||
|
|
|
@ -29,11 +29,11 @@ config HID
|
|||
|
||||
For docs and specs, see http://www.usb.org/developers/hidpage/
|
||||
|
||||
If unsure, say Y
|
||||
If unsure, say Y.
|
||||
|
||||
config HID_DEBUG
|
||||
bool "HID debugging support"
|
||||
default y if !EMBEDDED
|
||||
default y
|
||||
depends on HID
|
||||
---help---
|
||||
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
|
||||
or any HID hardware device.
|
||||
|
||||
If unsure, say N
|
||||
If unsure, say Y.
|
||||
|
||||
config HIDRAW
|
||||
bool "/dev/hidraw raw HID device support"
|
||||
|
@ -70,18 +70,6 @@ source "drivers/hid/usbhid/Kconfig"
|
|||
menu "Special HID drivers"
|
||||
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
|
||||
tristate "A4 tech" if EMBEDDED
|
||||
depends on USB_HID
|
||||
|
@ -128,6 +116,14 @@ config HID_CYPRESS
|
|||
---help---
|
||||
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
|
||||
tristate "Ezkey" if EMBEDDED
|
||||
depends on USB_HID
|
||||
|
@ -135,6 +131,13 @@ config HID_EZKEY
|
|||
---help---
|
||||
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
|
||||
tristate "Gyration" if EMBEDDED
|
||||
depends on USB_HID
|
||||
|
@ -142,6 +145,13 @@ config HID_GYRATION
|
|||
---help---
|
||||
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
|
||||
tristate "Logitech" if EMBEDDED
|
||||
depends on USB_HID
|
||||
|
|
|
@ -8,10 +8,6 @@ obj-$(CONFIG_HID) += hid.o
|
|||
hid-$(CONFIG_HID_DEBUG) += hid-debug.o
|
||||
hid-$(CONFIG_HIDRAW) += hidraw.o
|
||||
|
||||
ifdef CONFIG_HID_COMPAT
|
||||
obj-m += hid-dummy.o
|
||||
endif
|
||||
|
||||
hid-logitech-objs := hid-lg.o
|
||||
ifdef CONFIG_LOGITECH_FF
|
||||
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_CHICONY) += hid-chicony.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_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_MICROSOFT) += hid-microsoft.o
|
||||
obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o
|
||||
|
|
|
@ -158,5 +158,3 @@ static void a4_exit(void)
|
|||
module_init(a4_init);
|
||||
module_exit(a4_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
HID_COMPAT_LOAD_DRIVER(a4tech);
|
||||
|
|
|
@ -53,7 +53,7 @@ struct apple_key_translation {
|
|||
u8 flags;
|
||||
};
|
||||
|
||||
static struct apple_key_translation apple_fn_keys[] = {
|
||||
static const struct apple_key_translation apple_fn_keys[] = {
|
||||
{ KEY_BACKSPACE, KEY_DELETE },
|
||||
{ KEY_ENTER, KEY_INSERT },
|
||||
{ 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_F1, KEY_BRIGHTNESSDOWN, 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_K, KEY_KP2 },
|
||||
{ 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_102ND, KEY_GRAVE },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct apple_key_translation *apple_find_translation(
|
||||
struct apple_key_translation *table, u16 from)
|
||||
static const struct apple_key_translation *apple_find_translation(
|
||||
const struct apple_key_translation *table, u16 from)
|
||||
{
|
||||
struct apple_key_translation *trans;
|
||||
const struct apple_key_translation *trans;
|
||||
|
||||
/* Look for the translation */
|
||||
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 apple_sc *asc = hid_get_drvdata(hid);
|
||||
struct apple_key_translation *trans;
|
||||
const struct apple_key_translation *trans;
|
||||
|
||||
if (usage->code == KEY_FN) {
|
||||
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)
|
||||
{
|
||||
struct apple_key_translation *trans;
|
||||
const struct apple_key_translation *trans;
|
||||
|
||||
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),
|
||||
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN |
|
||||
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),
|
||||
.driver_data = APPLE_HAS_FN },
|
||||
{ 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_exit(apple_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
HID_COMPAT_LOAD_DRIVER(apple);
|
||||
|
|
|
@ -101,5 +101,3 @@ static void belkin_exit(void)
|
|||
module_init(belkin_init);
|
||||
module_exit(belkin_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
HID_COMPAT_LOAD_DRIVER(belkin);
|
||||
|
|
|
@ -83,5 +83,3 @@ static void ch_exit(void)
|
|||
module_init(ch_init);
|
||||
module_exit(ch_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
HID_COMPAT_LOAD_DRIVER(cherry);
|
||||
|
|
|
@ -76,5 +76,3 @@ static void ch_exit(void)
|
|||
module_init(ch_init);
|
||||
module_exit(ch_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
HID_COMPAT_LOAD_DRIVER(chicony);
|
||||
|
|
|
@ -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_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_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_ISO) },
|
||||
{ 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_2) },
|
||||
{ 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_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_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_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_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_LOGITECH, USB_DEVICE_ID_MX3000_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);
|
||||
|
||||
#ifdef CONFIG_HID_COMPAT
|
||||
static void hid_compat_load(struct work_struct *ws)
|
||||
int hid_check_keys_pressed(struct hid_device *hid)
|
||||
{
|
||||
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;
|
||||
}
|
||||
static DECLARE_WORK(hid_compat_work, hid_compat_load);
|
||||
static struct workqueue_struct *hid_compat_wq;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(hid_check_keys_pressed);
|
||||
|
||||
static int __init hid_init(void)
|
||||
{
|
||||
|
@ -1836,15 +1849,6 @@ static int __init hid_init(void)
|
|||
if (ret)
|
||||
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;
|
||||
err_bus:
|
||||
bus_unregister(&hid_bus_type);
|
||||
|
@ -1854,9 +1858,6 @@ err:
|
|||
|
||||
static void __exit hid_exit(void)
|
||||
{
|
||||
#ifdef CONFIG_HID_COMPAT
|
||||
destroy_workqueue(hid_compat_wq);
|
||||
#endif
|
||||
hidraw_exit();
|
||||
bus_unregister(&hid_bus_type);
|
||||
}
|
||||
|
|
|
@ -154,5 +154,3 @@ static void cp_exit(void)
|
|||
module_init(cp_init);
|
||||
module_exit(cp_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
HID_COMPAT_LOAD_DRIVER(cypress);
|
||||
|
|
|
@ -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");
|
|
@ -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");
|
|
@ -91,5 +91,3 @@ static void ez_exit(void)
|
|||
module_init(ez_init);
|
||||
module_exit(ez_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
HID_COMPAT_LOAD_DRIVER(ezkey);
|
||||
|
|
|
@ -181,5 +181,3 @@ static void __exit ga_exit(void)
|
|||
module_init(ga_init);
|
||||
module_exit(ga_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
HID_COMPAT_LOAD_DRIVER(greenasia);
|
||||
|
|
|
@ -94,5 +94,3 @@ static void gyration_exit(void)
|
|||
module_init(gyration_init);
|
||||
module_exit(gyration_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
HID_COMPAT_LOAD_DRIVER(gyration);
|
||||
|
|
|
@ -67,6 +67,9 @@
|
|||
#define USB_DEVICE_ID_APPLE_GEYSER4_ANSI 0x021a
|
||||
#define USB_DEVICE_ID_APPLE_GEYSER4_ISO 0x021b
|
||||
#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_ISO 0x0221
|
||||
#define USB_DEVICE_ID_APPLE_ALU_JIS 0x0222
|
||||
|
@ -148,6 +151,8 @@
|
|||
#define USB_VENDOR_ID_DMI 0x0c0b
|
||||
#define USB_DEVICE_ID_DMI_ENC 0x5fab
|
||||
|
||||
#define USB_VENDOR_ID_DRAGONRISE 0x0079
|
||||
|
||||
#define USB_VENDOR_ID_ELO 0x04E7
|
||||
#define USB_DEVICE_ID_ELO_TS2700 0x0020
|
||||
|
||||
|
@ -272,6 +277,9 @@
|
|||
#define USB_DEVICE_ID_LD_POWERCONTROL 0x2030
|
||||
#define USB_DEVICE_ID_LD_MACHINETEST 0x2040
|
||||
|
||||
#define USB_VENDOR_ID_KENSINGTON 0x047d
|
||||
#define USB_DEVICE_ID_KS_SLIMBLADE 0x2041
|
||||
|
||||
#define USB_VENDOR_ID_LOGITECH 0x046d
|
||||
#define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101
|
||||
#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110
|
||||
|
@ -418,6 +426,8 @@
|
|||
#define USB_VENDOR_ID_ZEROPLUS 0x0c12
|
||||
|
||||
#define USB_VENDOR_ID_KYE 0x0458
|
||||
#define USB_DEVICE_ID_KYE_ERGO_525V 0x0087
|
||||
#define USB_DEVICE_ID_KYE_GPEN_560 0x5003
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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");
|
|
@ -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");
|
|
@ -326,5 +326,3 @@ static void lg_exit(void)
|
|||
module_init(lg_init);
|
||||
module_exit(lg_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
HID_COMPAT_LOAD_DRIVER(logitech);
|
||||
|
|
|
@ -210,5 +210,3 @@ static void ms_exit(void)
|
|||
module_init(ms_init);
|
||||
module_exit(ms_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
HID_COMPAT_LOAD_DRIVER(microsoft);
|
||||
|
|
|
@ -78,5 +78,3 @@ static void mr_exit(void)
|
|||
module_init(mr_init);
|
||||
module_exit(mr_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
HID_COMPAT_LOAD_DRIVER(monterey);
|
||||
|
|
|
@ -78,5 +78,3 @@ static void ntrig_exit(void)
|
|||
module_init(ntrig_init);
|
||||
module_exit(ntrig_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
HID_COMPAT_LOAD_DRIVER(ntrig);
|
||||
|
|
|
@ -118,5 +118,3 @@ static void pl_exit(void)
|
|||
module_init(pl_init);
|
||||
module_exit(pl_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
HID_COMPAT_LOAD_DRIVER(petalynx);
|
||||
|
|
|
@ -9,9 +9,12 @@
|
|||
* - contains two reports, one for each port (HID_QUIRK_MULTI_INPUT)
|
||||
*
|
||||
* 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 hid_report *report;
|
||||
s32 *strong;
|
||||
s32 *weak;
|
||||
};
|
||||
|
||||
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;
|
||||
right = right * 0x7f / 0xffff;
|
||||
|
||||
plff->report->field[0]->value[2] = left;
|
||||
plff->report->field[0]->value[3] = right;
|
||||
*plff->strong = left;
|
||||
*plff->weak = right;
|
||||
debug("running with 0x%02x 0x%02x", left, right);
|
||||
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 input_dev *dev;
|
||||
int error;
|
||||
s32 *strong;
|
||||
s32 *weak;
|
||||
|
||||
/* The device contains one output report per physical device, all
|
||||
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
|
||||
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)) {
|
||||
dev_err(&hid->dev, "no output reports found\n");
|
||||
|
@ -110,8 +122,21 @@ static int plff_init(struct hid_device *hid)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (report->field[0]->report_count < 4) {
|
||||
dev_err(&hid->dev, "not enough values in the field\n");
|
||||
if (report->field[0]->report_count >= 4) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -130,10 +155,11 @@ static int plff_init(struct hid_device *hid)
|
|||
}
|
||||
|
||||
plff->report = report;
|
||||
plff->report->field[0]->value[0] = 0x00;
|
||||
plff->report->field[0]->value[1] = 0x00;
|
||||
plff->report->field[0]->value[2] = 0x00;
|
||||
plff->report->field[0]->value[3] = 0x00;
|
||||
plff->strong = strong;
|
||||
plff->weak = weak;
|
||||
|
||||
*strong = 0x00;
|
||||
*weak = 0x00;
|
||||
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 */
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR),
|
||||
.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);
|
||||
|
@ -204,5 +230,3 @@ static void pl_exit(void)
|
|||
module_init(pl_init);
|
||||
module_exit(pl_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
HID_COMPAT_LOAD_DRIVER(pantherlord);
|
||||
|
|
|
@ -96,5 +96,3 @@ static void samsung_exit(void)
|
|||
module_init(samsung_init);
|
||||
module_exit(samsung_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
HID_COMPAT_LOAD_DRIVER(samsung);
|
||||
|
|
|
@ -148,5 +148,3 @@ static void sony_exit(void)
|
|||
module_init(sony_init);
|
||||
module_exit(sony_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
HID_COMPAT_LOAD_DRIVER(sony);
|
||||
|
|
|
@ -78,5 +78,3 @@ static void sp_exit(void)
|
|||
module_init(sp_init);
|
||||
module_exit(sp_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
HID_COMPAT_LOAD_DRIVER(sunplus);
|
||||
|
|
|
@ -265,5 +265,3 @@ static void tm_exit(void)
|
|||
module_init(tm_init);
|
||||
module_exit(tm_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
HID_COMPAT_LOAD_DRIVER(thrustmaster);
|
||||
|
|
|
@ -73,5 +73,3 @@ static void ts_exit(void)
|
|||
module_init(ts_init);
|
||||
module_exit(ts_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
HID_COMPAT_LOAD_DRIVER(topseed);
|
||||
|
|
|
@ -158,5 +158,3 @@ static void zp_exit(void)
|
|||
module_init(zp_init);
|
||||
module_exit(zp_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
HID_COMPAT_LOAD_DRIVER(zeroplus);
|
||||
|
|
|
@ -181,10 +181,18 @@ static int hidraw_open(struct inode *inode, struct file *file)
|
|||
|
||||
dev = hidraw_table[minor];
|
||||
if (!dev->open++) {
|
||||
err = dev->hid->ll_driver->open(dev->hid);
|
||||
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);
|
||||
if (err < 0) {
|
||||
if (dev->hid->ll_driver->power)
|
||||
dev->hid->ll_driver->power(dev->hid, PM_HINT_NORMAL);
|
||||
dev->open--;
|
||||
}
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&minors_lock);
|
||||
|
@ -209,11 +217,14 @@ static int hidraw_release(struct inode * inode, struct file * file)
|
|||
list_del(&list->node);
|
||||
dev = hidraw_table[minor];
|
||||
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);
|
||||
else
|
||||
} else {
|
||||
kfree(list->hidraw);
|
||||
}
|
||||
}
|
||||
|
||||
kfree(list);
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
|
||||
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
|
||||
* Copyright (c) 2006-2008 Jiri Kosina
|
||||
* Copyright (c) 2007-2008 Oliver Neukum
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -27,6 +28,7 @@
|
|||
#include <asm/byteorder.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/workqueue.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_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 */
|
||||
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);
|
||||
|
@ -63,8 +69,13 @@ MODULE_PARM_DESC(quirks, "Add/modify USB HID quirks by specifying "
|
|||
/*
|
||||
* 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 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 */
|
||||
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;
|
||||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
|
||||
spin_lock_irqsave(&usbhid->inlock, flags);
|
||||
if (hid->open > 0 && !test_bit(HID_SUSPENDED, &usbhid->iofl) &&
|
||||
spin_lock_irqsave(&usbhid->lock, flags);
|
||||
if (hid->open > 0 &&
|
||||
!test_bit(HID_DISCONNECTED, &usbhid->iofl) &&
|
||||
!test_bit(HID_REPORTED_IDLE, &usbhid->iofl) &&
|
||||
!test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) {
|
||||
rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC);
|
||||
if (rc != 0)
|
||||
clear_bit(HID_IN_RUNNING, &usbhid->iofl);
|
||||
}
|
||||
spin_unlock_irqrestore(&usbhid->inlock, flags);
|
||||
spin_unlock_irqrestore(&usbhid->lock, flags);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -145,7 +157,7 @@ static void hid_io_error(struct hid_device *hid)
|
|||
unsigned long flags;
|
||||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
|
||||
spin_lock_irqsave(&usbhid->inlock, flags);
|
||||
spin_lock_irqsave(&usbhid->lock, flags);
|
||||
|
||||
/* Stop when disconnected */
|
||||
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,
|
||||
jiffies + msecs_to_jiffies(usbhid->retry_delay));
|
||||
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) {
|
||||
case 0: /* success */
|
||||
usbhid_mark_busy(usbhid);
|
||||
usbhid->retry_delay = 0;
|
||||
hid_input_report(urb->context, HID_INPUT_REPORT,
|
||||
urb->transfer_buffer,
|
||||
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;
|
||||
case -EPIPE: /* stall */
|
||||
usbhid_mark_busy(usbhid);
|
||||
clear_bit(HID_IN_RUNNING, &usbhid->iofl);
|
||||
set_bit(HID_CLEAR_HALT, &usbhid->iofl);
|
||||
schedule_work(&usbhid->reset_work);
|
||||
|
@ -209,6 +276,7 @@ static void hid_irq_in(struct urb *urb)
|
|||
case -EPROTO: /* protocol error or unplug */
|
||||
case -ETIME: /* protocol error or unplug */
|
||||
case -ETIMEDOUT: /* Should never happen, but... */
|
||||
usbhid_mark_busy(usbhid);
|
||||
clear_bit(HID_IN_RUNNING, &usbhid->iofl);
|
||||
hid_io_error(hid);
|
||||
return;
|
||||
|
@ -239,6 +307,7 @@ static int hid_submit_out(struct hid_device *hid)
|
|||
report = usbhid->out[usbhid->outtail].report;
|
||||
raw_report = usbhid->out[usbhid->outtail].raw_report;
|
||||
|
||||
if (!test_bit(HID_REPORTED_IDLE, &usbhid->iofl)) {
|
||||
usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0);
|
||||
usbhid->urbout->dev = hid_to_usb_dev(hid);
|
||||
memcpy(usbhid->outbuf, raw_report, usbhid->urbout->transfer_buffer_length);
|
||||
|
@ -250,6 +319,14 @@ static int hid_submit_out(struct hid_device *hid)
|
|||
err_hid("usb_submit_urb(out) failed");
|
||||
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;
|
||||
}
|
||||
|
@ -266,6 +343,7 @@ static int hid_submit_ctrl(struct hid_device *hid)
|
|||
raw_report = usbhid->ctrl[usbhid->ctrltail].raw_report;
|
||||
dir = usbhid->ctrl[usbhid->ctrltail].dir;
|
||||
|
||||
if (!test_bit(HID_REPORTED_IDLE, &usbhid->iofl)) {
|
||||
len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
|
||||
if (dir == USB_DIR_OUT) {
|
||||
usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0);
|
||||
|
@ -302,6 +380,14 @@ static int hid_submit_ctrl(struct hid_device *hid)
|
|||
err_hid("usb_submit_urb(ctrl) failed");
|
||||
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;
|
||||
}
|
||||
|
@ -332,7 +418,7 @@ static void hid_irq_out(struct urb *urb)
|
|||
"received\n", urb->status);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&usbhid->outlock, flags);
|
||||
spin_lock_irqsave(&usbhid->lock, flags);
|
||||
|
||||
if (unplug)
|
||||
usbhid->outtail = usbhid->outhead;
|
||||
|
@ -344,12 +430,12 @@ static void hid_irq_out(struct urb *urb)
|
|||
clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
|
||||
wake_up(&usbhid->wait);
|
||||
}
|
||||
spin_unlock_irqrestore(&usbhid->outlock, flags);
|
||||
spin_unlock_irqrestore(&usbhid->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
|
||||
spin_unlock_irqrestore(&usbhid->outlock, flags);
|
||||
spin_unlock_irqrestore(&usbhid->lock, flags);
|
||||
wake_up(&usbhid->wait);
|
||||
}
|
||||
|
||||
|
@ -361,12 +447,11 @@ static void hid_ctrl(struct urb *urb)
|
|||
{
|
||||
struct hid_device *hid = urb->context;
|
||||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
unsigned long flags;
|
||||
int unplug = 0;
|
||||
int unplug = 0, status = urb->status;
|
||||
|
||||
spin_lock_irqsave(&usbhid->ctrllock, flags);
|
||||
spin_lock(&usbhid->lock);
|
||||
|
||||
switch (urb->status) {
|
||||
switch (status) {
|
||||
case 0: /* success */
|
||||
if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN)
|
||||
hid_input_report(urb->context,
|
||||
|
@ -383,7 +468,7 @@ static void hid_ctrl(struct urb *urb)
|
|||
break;
|
||||
default: /* error */
|
||||
dev_warn(&urb->dev->dev, "ctrl urb status %d "
|
||||
"received\n", urb->status);
|
||||
"received\n", status);
|
||||
}
|
||||
|
||||
if (unplug)
|
||||
|
@ -396,19 +481,18 @@ static void hid_ctrl(struct urb *urb)
|
|||
clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
|
||||
wake_up(&usbhid->wait);
|
||||
}
|
||||
spin_unlock_irqrestore(&usbhid->ctrllock, flags);
|
||||
spin_unlock(&usbhid->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
|
||||
spin_unlock_irqrestore(&usbhid->ctrllock, flags);
|
||||
spin_unlock(&usbhid->lock);
|
||||
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;
|
||||
unsigned long flags;
|
||||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
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;
|
||||
|
||||
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) {
|
||||
spin_unlock_irqrestore(&usbhid->outlock, flags);
|
||||
dev_warn(&hid->dev, "output queue full\n");
|
||||
return;
|
||||
}
|
||||
|
||||
usbhid->out[usbhid->outhead].raw_report = kmalloc(len, GFP_ATOMIC);
|
||||
if (!usbhid->out[usbhid->outhead].raw_report) {
|
||||
spin_unlock_irqrestore(&usbhid->outlock, flags);
|
||||
dev_warn(&hid->dev, "output queueing failed\n");
|
||||
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 (hid_submit_out(hid))
|
||||
clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
|
||||
|
||||
spin_unlock_irqrestore(&usbhid->outlock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&usbhid->ctrllock, flags);
|
||||
|
||||
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");
|
||||
return;
|
||||
}
|
||||
|
@ -454,7 +528,6 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns
|
|||
if (dir == USB_DIR_OUT) {
|
||||
usbhid->ctrl[usbhid->ctrlhead].raw_report = kmalloc(len, GFP_ATOMIC);
|
||||
if (!usbhid->ctrl[usbhid->ctrlhead].raw_report) {
|
||||
spin_unlock_irqrestore(&usbhid->ctrllock, flags);
|
||||
dev_warn(&hid->dev, "control queueing failed\n");
|
||||
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 (hid_submit_ctrl(hid))
|
||||
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);
|
||||
|
||||
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 usbhid_device *usbhid = hid->driver_data;
|
||||
struct hid_field *field;
|
||||
unsigned long flags;
|
||||
int offset;
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
return 0;
|
||||
|
@ -538,15 +630,22 @@ int usbhid_open(struct hid_device *hid)
|
|||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
int res;
|
||||
|
||||
mutex_lock(&hid_open_mut);
|
||||
if (!hid->open++) {
|
||||
res = usb_autopm_get_interface(usbhid->intf);
|
||||
/* the device must be awake to reliable request remote wakeup */
|
||||
if (res < 0) {
|
||||
hid->open--;
|
||||
mutex_unlock(&hid_open_mut);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
usbhid->intf->needs_remote_wakeup = 1;
|
||||
if (hid_start_in(hid))
|
||||
hid_io_error(hid);
|
||||
|
||||
usb_autopm_put_interface(usbhid->intf);
|
||||
}
|
||||
mutex_unlock(&hid_open_mut);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -554,10 +653,22 @@ void usbhid_close(struct hid_device *hid)
|
|||
{
|
||||
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) {
|
||||
spin_unlock_irq(&usbhid->lock);
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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),
|
||||
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,
|
||||
* so we will always set the HID_QUIRK_NOGET flag for them. */
|
||||
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_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);
|
||||
|
||||
spin_lock_init(&usbhid->inlock);
|
||||
spin_lock_init(&usbhid->outlock);
|
||||
spin_lock_init(&usbhid->ctrllock);
|
||||
spin_lock_init(&usbhid->lock);
|
||||
spin_lock_init(&usbhid->lock);
|
||||
|
||||
usbhid->intf = intf;
|
||||
usbhid->ifnum = interface->desc.bInterfaceNumber;
|
||||
|
@ -906,15 +1039,14 @@ static void usbhid_stop(struct hid_device *hid)
|
|||
return;
|
||||
|
||||
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);
|
||||
spin_unlock_irq(&usbhid->inlock);
|
||||
spin_unlock_irq(&usbhid->lock);
|
||||
usb_kill_urb(usbhid->urbin);
|
||||
usb_kill_urb(usbhid->urbout);
|
||||
usb_kill_urb(usbhid->urbctrl);
|
||||
|
||||
del_timer_sync(&usbhid->io_retry);
|
||||
cancel_work_sync(&usbhid->reset_work);
|
||||
hid_cancel_delayed_stuff(usbhid);
|
||||
|
||||
if (hid->claimed & HID_CLAIMED_INPUT)
|
||||
hidinput_disconnect(hid);
|
||||
|
@ -935,12 +1067,28 @@ static void usbhid_stop(struct hid_device *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 = {
|
||||
.parse = usbhid_parse,
|
||||
.start = usbhid_start,
|
||||
.stop = usbhid_stop,
|
||||
.open = usbhid_open,
|
||||
.close = usbhid_close,
|
||||
.power = usbhid_power,
|
||||
.hidinput_input_event = usb_hidinput_input_event,
|
||||
};
|
||||
|
||||
|
@ -1049,19 +1197,126 @@ static void hid_disconnect(struct usb_interface *intf)
|
|||
kfree(usbhid);
|
||||
}
|
||||
|
||||
static int hid_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
static void hid_cancel_delayed_stuff(struct usbhid_device *usbhid)
|
||||
{
|
||||
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;
|
||||
|
||||
if (!test_bit(HID_STARTED, &usbhid->iofl))
|
||||
return 0;
|
||||
spin_lock_irq(&usbhid->lock);
|
||||
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 */
|
||||
set_bit(HID_SUSPENDED, &usbhid->iofl);
|
||||
spin_unlock_irq(&usbhid->inlock);
|
||||
del_timer_sync(&usbhid->io_retry);
|
||||
usb_kill_urb(usbhid->urbin);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Same routine used for post_reset and reset_resume */
|
||||
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");
|
||||
return 0;
|
||||
}
|
||||
|
@ -1075,32 +1330,33 @@ static int hid_resume(struct usb_interface *intf)
|
|||
if (!test_bit(HID_STARTED, &usbhid->iofl))
|
||||
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;
|
||||
status = hid_start_in(hid);
|
||||
dev_dbg(&intf->dev, "resume status %d\n", status);
|
||||
return status;
|
||||
}
|
||||
if (status < 0)
|
||||
hid_io_error(hid);
|
||||
usbhid_restart_queues(usbhid);
|
||||
|
||||
/* Treat USB reset pretty much the same as suspend/resume */
|
||||
static int hid_pre_reset(struct usb_interface *intf)
|
||||
{
|
||||
/* FIXME: What if the interface is already suspended? */
|
||||
hid_suspend(intf, PMSG_ON);
|
||||
dev_dbg(&intf->dev, "resume status %d\n", status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Same routine used for post_reset and reset_resume */
|
||||
static int hid_post_reset(struct usb_interface *intf)
|
||||
static int hid_reset_resume(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);
|
||||
/* FIXME: Any more reinitialization needed? */
|
||||
|
||||
return hid_resume(intf);
|
||||
clear_bit(HID_REPORTED_IDLE, &usbhid->iofl);
|
||||
return hid_post_reset(intf);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static struct usb_device_id hid_usb_ids [] = {
|
||||
{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
|
||||
.bInterfaceClass = USB_INTERFACE_CLASS_HID },
|
||||
|
@ -1113,9 +1369,11 @@ static struct usb_driver hid_driver = {
|
|||
.name = "usbhid",
|
||||
.probe = hid_probe,
|
||||
.disconnect = hid_disconnect,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = hid_suspend,
|
||||
.resume = hid_resume,
|
||||
.reset_resume = hid_post_reset,
|
||||
.reset_resume = hid_reset_resume,
|
||||
#endif
|
||||
.pre_reset = hid_pre_reset,
|
||||
.post_reset = hid_post_reset,
|
||||
.id_table = hid_usb_ids,
|
||||
|
@ -1134,7 +1392,11 @@ static struct hid_driver hid_usb_driver = {
|
|||
|
||||
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);
|
||||
if (retval)
|
||||
goto hid_register_fail;
|
||||
|
@ -1158,6 +1420,8 @@ hiddev_init_fail:
|
|||
usbhid_quirks_init_fail:
|
||||
hid_unregister_driver(&hid_usb_driver);
|
||||
hid_register_fail:
|
||||
destroy_workqueue(resumption_waker);
|
||||
no_queue:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
@ -1167,6 +1431,7 @@ static void __exit hid_exit(void)
|
|||
hiddev_exit();
|
||||
usbhid_quirks_exit();
|
||||
hid_unregister_driver(&hid_usb_driver);
|
||||
destroy_workqueue(resumption_waker);
|
||||
}
|
||||
|
||||
module_init(hid_init);
|
||||
|
|
|
@ -246,11 +246,13 @@ static int hiddev_release(struct inode * inode, struct file * file)
|
|||
spin_unlock_irqrestore(&list->hiddev->list_lock, flags);
|
||||
|
||||
if (!--list->hiddev->open) {
|
||||
if (list->hiddev->exist)
|
||||
if (list->hiddev->exist) {
|
||||
usbhid_close(list->hiddev->hid);
|
||||
else
|
||||
usbhid_put_power(list->hiddev->hid);
|
||||
} else {
|
||||
kfree(list->hiddev);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
bail:
|
||||
file->private_data = NULL;
|
||||
|
@ -875,16 +888,21 @@ int hiddev_connect(struct hid_device *hid, unsigned int force)
|
|||
hiddev->hid = hid;
|
||||
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);
|
||||
if (retval) {
|
||||
err_hid("Not able to get a minor for this device.");
|
||||
hid->hiddev = NULL;
|
||||
unlock_kernel();
|
||||
kfree(hiddev);
|
||||
return -1;
|
||||
} else {
|
||||
hid->minor = usbhid->intf->minor;
|
||||
hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev;
|
||||
}
|
||||
unlock_kernel();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -38,7 +38,10 @@ int usbhid_wait_io(struct hid_device* hid);
|
|||
void usbhid_close(struct hid_device *hid);
|
||||
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);
|
||||
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 */
|
||||
#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_DISCONNECTED 7
|
||||
#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
|
||||
|
@ -66,7 +72,6 @@ struct usbhid_device {
|
|||
struct urb *urbin; /* Input URB */
|
||||
char *inbuf; /* Input buffer */
|
||||
dma_addr_t inbuf_dma; /* Input buffer dma */
|
||||
spinlock_t inlock; /* Input fifo spinlock */
|
||||
|
||||
struct urb *urbctrl; /* Control URB */
|
||||
struct usb_ctrlrequest *cr; /* Control request struct */
|
||||
|
@ -75,21 +80,22 @@ struct usbhid_device {
|
|||
unsigned char ctrlhead, ctrltail; /* Control fifo head & tail */
|
||||
char *ctrlbuf; /* Control buffer */
|
||||
dma_addr_t ctrlbuf_dma; /* Control buffer dma */
|
||||
spinlock_t ctrllock; /* Control fifo spinlock */
|
||||
|
||||
struct urb *urbout; /* Output URB */
|
||||
struct hid_output_fifo out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */
|
||||
unsigned char outhead, outtail; /* Output pipe fifo head & tail */
|
||||
char *outbuf; /* Output buffer */
|
||||
dma_addr_t outbuf_dma; /* Output buffer dma */
|
||||
spinlock_t outlock; /* Output fifo spinlock */
|
||||
|
||||
spinlock_t lock; /* fifo spinlock */
|
||||
unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */
|
||||
struct timer_list io_retry; /* Retry timer */
|
||||
unsigned long stop_retry; /* Time to give up, in jiffies */
|
||||
unsigned int retry_delay; /* Delay length in ms */
|
||||
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 */
|
||||
int ledcount; /* counting the number of active leds */
|
||||
};
|
||||
|
||||
#define hid_to_usb_dev(hid_dev) \
|
||||
|
|
|
@ -270,6 +270,7 @@ struct hid_item {
|
|||
|
||||
#define HID_QUIRK_INVERT 0x00000001
|
||||
#define HID_QUIRK_NOTOUCH 0x00000002
|
||||
#define HID_QUIRK_IGNORE 0x00000004
|
||||
#define HID_QUIRK_NOGET 0x00000008
|
||||
#define HID_QUIRK_BADPAD 0x00000020
|
||||
#define HID_QUIRK_MULTI_INPUT 0x00000040
|
||||
|
@ -603,12 +604,17 @@ struct hid_ll_driver {
|
|||
int (*open)(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,
|
||||
unsigned int code, int value);
|
||||
|
||||
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 */
|
||||
/* 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))
|
||||
|
@ -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);
|
||||
struct hid_device *hid_allocate_device(void);
|
||||
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);
|
||||
|
||||
/**
|
||||
|
@ -791,21 +798,5 @@ dbg_hid(const char *fmt, ...)
|
|||
__FILE__ , ## arg)
|
||||
#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
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче