mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 11:11:45 -04:00
Merge branch 'for-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jikos/hid
* 'for-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jikos/hid: (21 commits) USB HID: don't warn on idVendor == 0 USB HID: add 'quirks' module parameter USB HID: add support for dynamically-created quirks USB HID: clarify static quirk handling as squirks USB HID: encapsulate quirk handling into hid-quirks.c USB HID: EMS USBII device needs HID_QUIRK_MULTI_INPUT HID: update copyright and authorship macro HID: introduce proper zeroing of unused bits in output reports USB HID: add support for WiseGroup MP-8800 Quad Joypad USB HID: add FF support for Logitech Force 3D Pro Joystick USB HID: numlock quirk for dell W7658 keyboard USB HID: Logitech MX3000 keyboard needs report descriptor quirk USB HID: extend quirk for Logitech S510 keyboard USB HID: usbkbd/usbmouse - handle errors when registering devices USB HID: add QUIRK_HIDDEV for Belkin Flip KVM HID: enable dead keys on a belkin wireless keyboard USB HID: Thustmaster firestorm dual power v1 support USB HID: specify explicit size for hid_blacklist.quirks USB HID: fix retry & reset logic USB HID: consolidate vendor/product ids ...
This commit is contained in:
commit
d6454706c3
22 changed files with 1004 additions and 615 deletions
|
|
@ -36,5 +36,7 @@ config HID_DEBUG
|
|||
|
||||
If unsure, say N
|
||||
|
||||
source "drivers/hid/usbhid/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
|
|
|
|||
|
|
@ -6,3 +6,7 @@ hid-objs := hid-core.o hid-input.o
|
|||
obj-$(CONFIG_HID) += hid.o
|
||||
hid-$(CONFIG_HID_DEBUG) += hid-debug.o
|
||||
|
||||
obj-$(CONFIG_USB_HID) += usbhid/
|
||||
obj-$(CONFIG_USB_MOUSE) += usbhid/
|
||||
obj-$(CONFIG_USB_KBD) += usbhid/
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
* Copyright (c) 1999 Andreas Gal
|
||||
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
|
||||
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
|
||||
* Copyright (c) 2006 Jiri Kosina
|
||||
* Copyright (c) 2006-2007 Jiri Kosina
|
||||
*/
|
||||
|
||||
/*
|
||||
|
|
@ -37,7 +37,7 @@
|
|||
*/
|
||||
|
||||
#define DRIVER_VERSION "v2.6"
|
||||
#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik"
|
||||
#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik, Jiri Kosina"
|
||||
#define DRIVER_DESC "HID core driver"
|
||||
#define DRIVER_LICENSE "GPL"
|
||||
|
||||
|
|
@ -872,8 +872,13 @@ static void hid_output_field(struct hid_field *field, __u8 *data)
|
|||
unsigned count = field->report_count;
|
||||
unsigned offset = field->report_offset;
|
||||
unsigned size = field->report_size;
|
||||
unsigned bitsused = offset + count * size;
|
||||
unsigned n;
|
||||
|
||||
/* make sure the unused bits in the last byte are zeros */
|
||||
if (count > 0 && size > 0 && (bitsused % 8) != 0)
|
||||
data[(bitsused-1)/8] &= (1 << (bitsused % 8)) - 1;
|
||||
|
||||
for (n = 0; n < count; n++) {
|
||||
if (field->logical_minimum < 0) /* signed values */
|
||||
implement(data, offset + n * size, size, s32ton(field->value[n], size));
|
||||
|
|
|
|||
|
|
@ -431,6 +431,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
|||
case 0x000: goto ignore;
|
||||
case 0x034: map_key_clear(KEY_SLEEP); break;
|
||||
case 0x036: map_key_clear(BTN_MISC); break;
|
||||
/*
|
||||
* The next three are reported by Belkin wireless
|
||||
* keyboard (1020:0006). These values are "reserved"
|
||||
* in HUT 1.12.
|
||||
*/
|
||||
case 0x03a: map_key_clear(KEY_SOUND); break;
|
||||
case 0x03b: map_key_clear(KEY_CAMERA); break;
|
||||
case 0x03c: map_key_clear(KEY_DOCUMENTS); break;
|
||||
|
||||
case 0x040: map_key_clear(KEY_MENU); break;
|
||||
case 0x045: map_key_clear(KEY_RADIO); break;
|
||||
|
||||
|
|
@ -531,10 +540,26 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
|||
case 0x302: map_key_clear(KEY_PROG2); break;
|
||||
case 0x303: map_key_clear(KEY_PROG3); break;
|
||||
|
||||
/* Reported on Logitech S510 wireless keyboard */
|
||||
/* Reported on certain Logitech wireless keyboards */
|
||||
case 0x1001: map_key_clear(KEY_MESSENGER); break;
|
||||
case 0x1003: map_key_clear(KEY_SOUND); break;
|
||||
case 0x1004: map_key_clear(KEY_VIDEO); break;
|
||||
case 0x1005: map_key_clear(KEY_AUDIO); break;
|
||||
case 0x100a: map_key_clear(KEY_DOCUMENTS); break;
|
||||
case 0x1011: map_key_clear(KEY_PREVIOUSSONG); break;
|
||||
case 0x1012: map_key_clear(KEY_NEXTSONG); break;
|
||||
case 0x1013: map_key_clear(KEY_CAMERA); break;
|
||||
case 0x1014: map_key_clear(KEY_MESSENGER); break;
|
||||
case 0x1015: map_key_clear(KEY_RECORD); break;
|
||||
case 0x1016: map_key_clear(KEY_PLAYER); break;
|
||||
case 0x1017: map_key_clear(KEY_EJECTCD); break;
|
||||
case 0x1019: map_key_clear(KEY_PROG1); break;
|
||||
case 0x101a: map_key_clear(KEY_PROG2); break;
|
||||
case 0x101b: map_key_clear(KEY_PROG3); break;
|
||||
case 0x101f: map_key_clear(KEY_ZOOMIN); break;
|
||||
case 0x1020: map_key_clear(KEY_ZOOMOUT); break;
|
||||
case 0x1021: map_key_clear(KEY_ZOOMRESET); break;
|
||||
case 0x1023: map_key_clear(KEY_CLOSE); break;
|
||||
/* this one is marked as 'Rotate' */
|
||||
case 0x1028: map_key_clear(KEY_ANGLE); break;
|
||||
case 0x1029: map_key_clear(KEY_SHUFFLE); break;
|
||||
|
|
|
|||
149
drivers/hid/usbhid/Kconfig
Normal file
149
drivers/hid/usbhid/Kconfig
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
comment "USB Input Devices"
|
||||
depends on USB
|
||||
|
||||
config USB_HID
|
||||
tristate "USB Human Interface Device (full HID) support"
|
||||
default y
|
||||
depends on USB && INPUT
|
||||
select HID
|
||||
---help---
|
||||
Say Y here if you want full HID support to connect USB keyboards,
|
||||
mice, joysticks, graphic tablets, or any other HID based devices
|
||||
to your computer via USB, as well as Uninterruptible Power Supply
|
||||
(UPS) and monitor control devices.
|
||||
|
||||
You can't use this driver and the HIDBP (Boot Protocol) keyboard
|
||||
and mouse drivers at the same time. More information is available:
|
||||
<file:Documentation/input/input.txt>.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called usbhid.
|
||||
|
||||
comment "Input core support is needed for USB HID input layer or HIDBP support"
|
||||
depends on USB_HID && INPUT=n
|
||||
|
||||
config USB_HIDINPUT_POWERBOOK
|
||||
bool "Enable support for iBook/PowerBook special keys"
|
||||
default n
|
||||
depends on USB_HID
|
||||
help
|
||||
Say Y here if you want support for the special keys (Fn, Numlock) on
|
||||
Apple iBooks and PowerBooks.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config HID_FF
|
||||
bool "Force feedback support (EXPERIMENTAL)"
|
||||
depends on USB_HID && EXPERIMENTAL
|
||||
help
|
||||
Say Y here is you want force feedback support for a few HID devices.
|
||||
See below for a list of supported devices.
|
||||
|
||||
See <file:Documentation/input/ff.txt> for a description of the force
|
||||
feedback API.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config HID_PID
|
||||
bool "PID device support"
|
||||
depends on HID_FF
|
||||
help
|
||||
Say Y here if you have a PID-compliant device and wish to enable force
|
||||
feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such
|
||||
devices.
|
||||
|
||||
config LOGITECH_FF
|
||||
bool "Logitech devices support"
|
||||
depends on HID_FF
|
||||
select INPUT_FF_MEMLESS if USB_HID
|
||||
help
|
||||
Say Y here if you have one of these devices:
|
||||
- Logitech WingMan Cordless RumblePad
|
||||
- Logitech WingMan Cordless RumblePad 2
|
||||
- Logitech WingMan Force 3D
|
||||
- Logitech Formula Force EX
|
||||
- Logitech MOMO Force wheel
|
||||
|
||||
and if you want to enable force feedback for them.
|
||||
Note: if you say N here, this device will still be supported, but without
|
||||
force feedback.
|
||||
|
||||
config PANTHERLORD_FF
|
||||
bool "PantherLord USB/PS2 2in1 Adapter support"
|
||||
depends on HID_FF
|
||||
select INPUT_FF_MEMLESS if USB_HID
|
||||
help
|
||||
Say Y here if you have a PantherLord USB/PS2 2in1 Adapter and want
|
||||
to enable force feedback support for it.
|
||||
|
||||
config THRUSTMASTER_FF
|
||||
bool "ThrustMaster FireStorm Dual Power 2 support (EXPERIMENTAL)"
|
||||
depends on HID_FF && EXPERIMENTAL
|
||||
select INPUT_FF_MEMLESS if USB_HID
|
||||
help
|
||||
Say Y here if you have a THRUSTMASTER FireStore Dual Power 2,
|
||||
and want to enable force feedback support for it.
|
||||
Note: if you say N here, this device will still be supported, but without
|
||||
force feedback.
|
||||
|
||||
config ZEROPLUS_FF
|
||||
bool "Zeroplus based game controller support"
|
||||
depends on HID_FF
|
||||
select INPUT_FF_MEMLESS if USB_HID
|
||||
help
|
||||
Say Y here if you have a Zeroplus based game controller and want to
|
||||
enable force feedback for it.
|
||||
|
||||
config USB_HIDDEV
|
||||
bool "/dev/hiddev raw HID device support"
|
||||
depends on USB_HID
|
||||
help
|
||||
Say Y here if you want to support HID devices (from the USB
|
||||
specification standpoint) that aren't strictly user interface
|
||||
devices, like monitor controls and Uninterruptable Power Supplies.
|
||||
|
||||
This module supports these devices separately using a separate
|
||||
event interface on /dev/usb/hiddevX (char 180:96 to 180:111).
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
menu "USB HID Boot Protocol drivers"
|
||||
depends on USB!=n && USB_HID!=y
|
||||
|
||||
config USB_KBD
|
||||
tristate "USB HIDBP Keyboard (simple Boot) support"
|
||||
depends on USB && INPUT
|
||||
---help---
|
||||
Say Y here only if you are absolutely sure that you don't want
|
||||
to use the generic HID driver for your USB keyboard and prefer
|
||||
to use the keyboard in its limited Boot Protocol mode instead.
|
||||
|
||||
This is almost certainly not what you want. This is mostly
|
||||
useful for embedded applications or simple keyboards.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called usbkbd.
|
||||
|
||||
If even remotely unsure, say N.
|
||||
|
||||
config USB_MOUSE
|
||||
tristate "USB HIDBP Mouse (simple Boot) support"
|
||||
depends on USB && INPUT
|
||||
---help---
|
||||
Say Y here only if you are absolutely sure that you don't want
|
||||
to use the generic HID driver for your USB mouse and prefer
|
||||
to use the mouse in its limited Boot Protocol mode instead.
|
||||
|
||||
This is almost certainly not what you want. This is mostly
|
||||
useful for embedded applications or simple mice.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called usbmouse.
|
||||
|
||||
If even remotely unsure, say N.
|
||||
|
||||
endmenu
|
||||
|
||||
|
||||
35
drivers/hid/usbhid/Makefile
Normal file
35
drivers/hid/usbhid/Makefile
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#
|
||||
# Makefile for the USB input drivers
|
||||
#
|
||||
|
||||
# Multipart objects.
|
||||
usbhid-objs := hid-core.o hid-quirks.o
|
||||
|
||||
# Optional parts of multipart objects.
|
||||
|
||||
ifeq ($(CONFIG_USB_HIDDEV),y)
|
||||
usbhid-objs += hiddev.o
|
||||
endif
|
||||
ifeq ($(CONFIG_HID_PID),y)
|
||||
usbhid-objs += hid-pidff.o
|
||||
endif
|
||||
ifeq ($(CONFIG_LOGITECH_FF),y)
|
||||
usbhid-objs += hid-lgff.o
|
||||
endif
|
||||
ifeq ($(CONFIG_PANTHERLORD_FF),y)
|
||||
usbhid-objs += hid-plff.o
|
||||
endif
|
||||
ifeq ($(CONFIG_THRUSTMASTER_FF),y)
|
||||
usbhid-objs += hid-tmff.o
|
||||
endif
|
||||
ifeq ($(CONFIG_ZEROPLUS_FF),y)
|
||||
usbhid-objs += hid-zpff.o
|
||||
endif
|
||||
ifeq ($(CONFIG_HID_FF),y)
|
||||
usbhid-objs += hid-ff.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_USB_HID) += usbhid.o
|
||||
obj-$(CONFIG_USB_KBD) += usbkbd.o
|
||||
obj-$(CONFIG_USB_MOUSE) += usbmouse.o
|
||||
|
||||
1114
drivers/hid/usbhid/hid-core.c
Normal file
1114
drivers/hid/usbhid/hid-core.c
Normal file
File diff suppressed because it is too large
Load diff
91
drivers/hid/usbhid/hid-ff.c
Normal file
91
drivers/hid/usbhid/hid-ff.c
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* $Id: hid-ff.c,v 1.2 2002/04/18 22:02:47 jdeneux Exp $
|
||||
*
|
||||
* Force feedback support for hid devices.
|
||||
* Not all hid devices use the same protocol. For example, some use PID,
|
||||
* other use their own proprietary procotol.
|
||||
*
|
||||
* Copyright (c) 2002-2004 Johann Deneux
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* Should you need to contact me, the author, you can do so by
|
||||
* e-mail - mail your message to <johann.deneux@it.uu.se>
|
||||
*/
|
||||
|
||||
#include <linux/input.h>
|
||||
|
||||
#undef DEBUG
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include <linux/hid.h>
|
||||
#include "usbhid.h"
|
||||
|
||||
/*
|
||||
* This table contains pointers to initializers. To add support for new
|
||||
* devices, you need to add the USB vendor and product ids here.
|
||||
*/
|
||||
struct hid_ff_initializer {
|
||||
u16 idVendor;
|
||||
u16 idProduct;
|
||||
int (*init)(struct hid_device*);
|
||||
};
|
||||
|
||||
/*
|
||||
* We try pidff when no other driver is found because PID is the
|
||||
* standards compliant way of implementing force feedback in HID.
|
||||
* pidff_init() will quickly abort if the device doesn't appear to
|
||||
* be a PID device
|
||||
*/
|
||||
static struct hid_ff_initializer inits[] = {
|
||||
#ifdef CONFIG_LOGITECH_FF
|
||||
{ 0x46d, 0xc211, hid_lgff_init }, /* Logitech Cordless rumble pad */
|
||||
{ 0x46d, 0xc219, hid_lgff_init }, /* Logitech Cordless rumble pad 2 */
|
||||
{ 0x46d, 0xc283, hid_lgff_init }, /* Logitech Wingman Force 3d */
|
||||
{ 0x46d, 0xc286, hid_lgff_init }, /* Logitech Force 3D Pro Joystick */
|
||||
{ 0x46d, 0xc294, hid_lgff_init }, /* Logitech Formula Force EX */
|
||||
{ 0x46d, 0xc295, hid_lgff_init }, /* Logitech MOMO force wheel */
|
||||
{ 0x46d, 0xca03, hid_lgff_init }, /* Logitech MOMO force wheel */
|
||||
#endif
|
||||
#ifdef CONFIG_PANTHERLORD_FF
|
||||
{ 0x810, 0x0001, hid_plff_init },
|
||||
#endif
|
||||
#ifdef CONFIG_THRUSTMASTER_FF
|
||||
{ 0x44f, 0xb300, hid_tmff_init },
|
||||
{ 0x44f, 0xb304, hid_tmff_init },
|
||||
#endif
|
||||
#ifdef CONFIG_ZEROPLUS_FF
|
||||
{ 0xc12, 0x0005, hid_zpff_init },
|
||||
{ 0xc12, 0x0030, hid_zpff_init },
|
||||
#endif
|
||||
{ 0, 0, hid_pidff_init} /* Matches anything */
|
||||
};
|
||||
|
||||
int hid_ff_init(struct hid_device* hid)
|
||||
{
|
||||
struct hid_ff_initializer *init;
|
||||
int vendor = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idVendor);
|
||||
int product = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idProduct);
|
||||
|
||||
for (init = inits; init->idVendor; init++)
|
||||
if (init->idVendor == vendor && init->idProduct == product)
|
||||
break;
|
||||
|
||||
return init->init(hid);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hid_ff_init);
|
||||
|
||||
151
drivers/hid/usbhid/hid-lgff.c
Normal file
151
drivers/hid/usbhid/hid-lgff.c
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Force feedback support for hid-compliant for some of the devices from
|
||||
* Logitech, namely:
|
||||
* - WingMan Cordless RumblePad
|
||||
* - WingMan Force 3D
|
||||
*
|
||||
* Copyright (c) 2002-2004 Johann Deneux
|
||||
* Copyright (c) 2006 Anssi Hannula <anssi.hannula@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
|
||||
*
|
||||
* Should you need to contact me, the author, you can do so by
|
||||
* e-mail - mail your message to <johann.deneux@it.uu.se>
|
||||
*/
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/hid.h>
|
||||
#include "usbhid.h"
|
||||
|
||||
struct dev_type {
|
||||
u16 idVendor;
|
||||
u16 idProduct;
|
||||
const signed short *ff;
|
||||
};
|
||||
|
||||
static const signed short ff_rumble[] = {
|
||||
FF_RUMBLE,
|
||||
-1
|
||||
};
|
||||
|
||||
static const signed short ff_joystick[] = {
|
||||
FF_CONSTANT,
|
||||
-1
|
||||
};
|
||||
|
||||
static const struct dev_type devices[] = {
|
||||
{ 0x046d, 0xc211, ff_rumble },
|
||||
{ 0x046d, 0xc219, ff_rumble },
|
||||
{ 0x046d, 0xc283, ff_joystick },
|
||||
{ 0x046d, 0xc286, ff_joystick },
|
||||
{ 0x046d, 0xc294, ff_joystick },
|
||||
{ 0x046d, 0xc295, ff_joystick },
|
||||
{ 0x046d, 0xca03, ff_joystick },
|
||||
};
|
||||
|
||||
static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
|
||||
int x, y;
|
||||
unsigned int left, right;
|
||||
|
||||
#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
|
||||
|
||||
switch (effect->type) {
|
||||
case FF_CONSTANT:
|
||||
x = effect->u.ramp.start_level + 0x7f; /* 0x7f is center */
|
||||
y = effect->u.ramp.end_level + 0x7f;
|
||||
CLAMP(x);
|
||||
CLAMP(y);
|
||||
report->field[0]->value[0] = 0x51;
|
||||
report->field[0]->value[1] = 0x08;
|
||||
report->field[0]->value[2] = x;
|
||||
report->field[0]->value[3] = y;
|
||||
dbg("(x, y)=(%04x, %04x)", x, y);
|
||||
usbhid_submit_report(hid, report, USB_DIR_OUT);
|
||||
break;
|
||||
|
||||
case FF_RUMBLE:
|
||||
right = effect->u.rumble.strong_magnitude;
|
||||
left = effect->u.rumble.weak_magnitude;
|
||||
right = right * 0xff / 0xffff;
|
||||
left = left * 0xff / 0xffff;
|
||||
CLAMP(left);
|
||||
CLAMP(right);
|
||||
report->field[0]->value[0] = 0x42;
|
||||
report->field[0]->value[1] = 0x00;
|
||||
report->field[0]->value[2] = left;
|
||||
report->field[0]->value[3] = right;
|
||||
dbg("(left, right)=(%04x, %04x)", left, right);
|
||||
usbhid_submit_report(hid, report, USB_DIR_OUT);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hid_lgff_init(struct hid_device* hid)
|
||||
{
|
||||
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||
struct input_dev *dev = hidinput->input;
|
||||
struct hid_report *report;
|
||||
struct hid_field *field;
|
||||
const signed short *ff_bits = ff_joystick;
|
||||
int error;
|
||||
int i;
|
||||
|
||||
/* Find the report to use */
|
||||
if (list_empty(report_list)) {
|
||||
err("No output report found");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check that the report looks ok */
|
||||
report = list_entry(report_list->next, struct hid_report, list);
|
||||
if (!report) {
|
||||
err("NULL output report");
|
||||
return -1;
|
||||
}
|
||||
|
||||
field = report->field[0];
|
||||
if (!field) {
|
||||
err("NULL field");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(devices); i++) {
|
||||
if (dev->id.vendor == devices[i].idVendor &&
|
||||
dev->id.product == devices[i].idProduct) {
|
||||
ff_bits = devices[i].ff;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; ff_bits[i] >= 0; i++)
|
||||
set_bit(ff_bits[i], dev->ffbit);
|
||||
|
||||
error = input_ff_create_memless(dev, NULL, hid_lgff_play);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux <[email protected]>\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
1331
drivers/hid/usbhid/hid-pidff.c
Normal file
1331
drivers/hid/usbhid/hid-pidff.c
Normal file
File diff suppressed because it is too large
Load diff
129
drivers/hid/usbhid/hid-plff.c
Normal file
129
drivers/hid/usbhid/hid-plff.c
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Force feedback support for PantherLord USB/PS2 2in1 Adapter devices
|
||||
*
|
||||
* Copyright (c) 2007 Anssi Hannula <anssi.hannula@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
|
||||
*/
|
||||
|
||||
|
||||
/* #define DEBUG */
|
||||
|
||||
#define debug(format, arg...) pr_debug("hid-plff: " format "\n" , ## arg)
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/hid.h>
|
||||
#include "usbhid.h"
|
||||
|
||||
struct plff_device {
|
||||
struct hid_report *report;
|
||||
};
|
||||
|
||||
static int hid_plff_play(struct input_dev *dev, void *data,
|
||||
struct ff_effect *effect)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
struct plff_device *plff = data;
|
||||
int left, right;
|
||||
|
||||
left = effect->u.rumble.strong_magnitude;
|
||||
right = effect->u.rumble.weak_magnitude;
|
||||
debug("called with 0x%04x 0x%04x", left, right);
|
||||
|
||||
left = left * 0x7f / 0xffff;
|
||||
right = right * 0x7f / 0xffff;
|
||||
|
||||
plff->report->field[0]->value[2] = left;
|
||||
plff->report->field[0]->value[3] = right;
|
||||
debug("running with 0x%02x 0x%02x", left, right);
|
||||
usbhid_submit_report(hid, plff->report, USB_DIR_OUT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hid_plff_init(struct hid_device *hid)
|
||||
{
|
||||
struct plff_device *plff;
|
||||
struct hid_report *report;
|
||||
struct hid_input *hidinput;
|
||||
struct list_head *report_list =
|
||||
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||
struct list_head *report_ptr = report_list;
|
||||
struct input_dev *dev;
|
||||
int error;
|
||||
|
||||
/* The device contains 2 output reports (one for each
|
||||
HID_QUIRK_MULTI_INPUT device), both containing 1 field, which
|
||||
contains 4 ff00.0002 usages and 4 16bit absolute values.
|
||||
|
||||
The 2 input reports also contain a field which contains
|
||||
8 ff00.0001 usages and 8 boolean values. Their meaning is
|
||||
currently unknown. */
|
||||
|
||||
if (list_empty(report_list)) {
|
||||
printk(KERN_ERR "hid-plff: no output reports found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
list_for_each_entry(hidinput, &hid->inputs, list) {
|
||||
|
||||
report_ptr = report_ptr->next;
|
||||
|
||||
if (report_ptr == report_list) {
|
||||
printk(KERN_ERR "hid-plff: required output report is missing\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
report = list_entry(report_ptr, struct hid_report, list);
|
||||
if (report->maxfield < 1) {
|
||||
printk(KERN_ERR "hid-plff: no fields in the report\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (report->field[0]->report_count < 4) {
|
||||
printk(KERN_ERR "hid-plff: not enough values in the field\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
plff = kzalloc(sizeof(struct plff_device), GFP_KERNEL);
|
||||
if (!plff)
|
||||
return -ENOMEM;
|
||||
|
||||
dev = hidinput->input;
|
||||
|
||||
set_bit(FF_RUMBLE, dev->ffbit);
|
||||
|
||||
error = input_ff_create_memless(dev, plff, hid_plff_play);
|
||||
if (error) {
|
||||
kfree(plff);
|
||||
return error;
|
||||
}
|
||||
|
||||
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;
|
||||
usbhid_submit_report(hid, plff->report, USB_DIR_OUT);
|
||||
}
|
||||
|
||||
printk(KERN_INFO "hid-plff: Force feedback for PantherLord USB/PS2 "
|
||||
"2in1 Adapters by Anssi Hannula <[email protected]>\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
681
drivers/hid/usbhid/hid-quirks.c
Normal file
681
drivers/hid/usbhid/hid-quirks.c
Normal file
|
|
@ -0,0 +1,681 @@
|
|||
/*
|
||||
* USB HID quirks support for Linux
|
||||
*
|
||||
* Copyright (c) 1999 Andreas Gal
|
||||
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
|
||||
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
|
||||
* Copyright (c) 2006-2007 Jiri Kosina
|
||||
* Copyright (c) 2007 Paul Walmsley
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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/hid.h>
|
||||
|
||||
#define USB_VENDOR_ID_A4TECH 0x09da
|
||||
#define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006
|
||||
|
||||
#define USB_VENDOR_ID_AASHIMA 0x06d6
|
||||
#define USB_DEVICE_ID_AASHIMA_GAMEPAD 0x0025
|
||||
#define USB_DEVICE_ID_AASHIMA_PREDATOR 0x0026
|
||||
|
||||
#define USB_VENDOR_ID_ACECAD 0x0460
|
||||
#define USB_DEVICE_ID_ACECAD_FLAIR 0x0004
|
||||
#define USB_DEVICE_ID_ACECAD_302 0x0008
|
||||
|
||||
#define USB_VENDOR_ID_AIPTEK 0x08ca
|
||||
#define USB_DEVICE_ID_AIPTEK_01 0x0001
|
||||
#define USB_DEVICE_ID_AIPTEK_10 0x0010
|
||||
#define USB_DEVICE_ID_AIPTEK_20 0x0020
|
||||
#define USB_DEVICE_ID_AIPTEK_21 0x0021
|
||||
#define USB_DEVICE_ID_AIPTEK_22 0x0022
|
||||
#define USB_DEVICE_ID_AIPTEK_23 0x0023
|
||||
#define USB_DEVICE_ID_AIPTEK_24 0x0024
|
||||
|
||||
#define USB_VENDOR_ID_AIRCABLE 0x16CA
|
||||
#define USB_DEVICE_ID_AIRCABLE1 0x1502
|
||||
|
||||
#define USB_VENDOR_ID_ALCOR 0x058f
|
||||
#define USB_DEVICE_ID_ALCOR_USBRS232 0x9720
|
||||
|
||||
#define USB_VENDOR_ID_ALPS 0x0433
|
||||
#define USB_DEVICE_ID_IBM_GAMEPAD 0x1101
|
||||
|
||||
#define USB_VENDOR_ID_APPLE 0x05ac
|
||||
#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304
|
||||
#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI 0x020e
|
||||
#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f
|
||||
#define USB_DEVICE_ID_APPLE_GEYSER_ANSI 0x0214
|
||||
#define USB_DEVICE_ID_APPLE_GEYSER_ISO 0x0215
|
||||
#define USB_DEVICE_ID_APPLE_GEYSER_JIS 0x0216
|
||||
#define USB_DEVICE_ID_APPLE_GEYSER3_ANSI 0x0217
|
||||
#define USB_DEVICE_ID_APPLE_GEYSER3_ISO 0x0218
|
||||
#define USB_DEVICE_ID_APPLE_GEYSER3_JIS 0x0219
|
||||
#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_FOUNTAIN_TP_ONLY 0x030a
|
||||
#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
|
||||
#define USB_DEVICE_ID_APPLE_IR 0x8240
|
||||
|
||||
#define USB_VENDOR_ID_ATEN 0x0557
|
||||
#define USB_DEVICE_ID_ATEN_UC100KM 0x2004
|
||||
#define USB_DEVICE_ID_ATEN_CS124U 0x2202
|
||||
#define USB_DEVICE_ID_ATEN_2PORTKVM 0x2204
|
||||
#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205
|
||||
#define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208
|
||||
|
||||
#define USB_VENDOR_ID_BELKIN 0x050d
|
||||
#define USB_DEVICE_ID_FLIP_KVM 0x3201
|
||||
|
||||
#define USB_VENDOR_ID_BERKSHIRE 0x0c98
|
||||
#define USB_DEVICE_ID_BERKSHIRE_PCWD 0x1140
|
||||
|
||||
#define USB_VENDOR_ID_CHERRY 0x046a
|
||||
#define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023
|
||||
|
||||
#define USB_VENDOR_ID_CHIC 0x05fe
|
||||
#define USB_DEVICE_ID_CHIC_GAMEPAD 0x0014
|
||||
|
||||
#define USB_VENDOR_ID_CIDC 0x1677
|
||||
|
||||
#define USB_VENDOR_ID_CODEMERCS 0x07c0
|
||||
#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500
|
||||
#define USB_DEVICE_ID_CODEMERCS_IOW_LAST 0x15ff
|
||||
|
||||
#define USB_VENDOR_ID_CYPRESS 0x04b4
|
||||
#define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001
|
||||
#define USB_DEVICE_ID_CYPRESS_HIDCOM 0x5500
|
||||
#define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE 0x7417
|
||||
|
||||
#define USB_VENDOR_ID_DELL 0x413c
|
||||
#define USB_DEVICE_ID_DELL_W7658 0x2005
|
||||
|
||||
#define USB_VENDOR_ID_DELORME 0x1163
|
||||
#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100
|
||||
#define USB_DEVICE_ID_DELORME_EM_LT20 0x0200
|
||||
|
||||
#define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f
|
||||
#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
|
||||
|
||||
#define USB_VENDOR_ID_GLAB 0x06c2
|
||||
#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038
|
||||
#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039
|
||||
#define USB_DEVICE_ID_0_0_4_IF_KIT 0x0040
|
||||
#define USB_DEVICE_ID_0_16_16_IF_KIT 0x0044
|
||||
#define USB_DEVICE_ID_8_8_8_IF_KIT 0x0045
|
||||
#define USB_DEVICE_ID_0_8_7_IF_KIT 0x0051
|
||||
#define USB_DEVICE_ID_0_8_8_IF_KIT 0x0053
|
||||
#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL 0x0058
|
||||
|
||||
#define USB_VENDOR_ID_GRIFFIN 0x077d
|
||||
#define USB_DEVICE_ID_POWERMATE 0x0410
|
||||
#define USB_DEVICE_ID_SOUNDKNOB 0x04AA
|
||||
|
||||
#define USB_VENDOR_ID_GTCO 0x078c
|
||||
#define USB_DEVICE_ID_GTCO_90 0x0090
|
||||
#define USB_DEVICE_ID_GTCO_100 0x0100
|
||||
#define USB_DEVICE_ID_GTCO_101 0x0101
|
||||
#define USB_DEVICE_ID_GTCO_103 0x0103
|
||||
#define USB_DEVICE_ID_GTCO_104 0x0104
|
||||
#define USB_DEVICE_ID_GTCO_105 0x0105
|
||||
#define USB_DEVICE_ID_GTCO_106 0x0106
|
||||
#define USB_DEVICE_ID_GTCO_107 0x0107
|
||||
#define USB_DEVICE_ID_GTCO_108 0x0108
|
||||
#define USB_DEVICE_ID_GTCO_200 0x0200
|
||||
#define USB_DEVICE_ID_GTCO_201 0x0201
|
||||
#define USB_DEVICE_ID_GTCO_202 0x0202
|
||||
#define USB_DEVICE_ID_GTCO_203 0x0203
|
||||
#define USB_DEVICE_ID_GTCO_204 0x0204
|
||||
#define USB_DEVICE_ID_GTCO_205 0x0205
|
||||
#define USB_DEVICE_ID_GTCO_206 0x0206
|
||||
#define USB_DEVICE_ID_GTCO_207 0x0207
|
||||
#define USB_DEVICE_ID_GTCO_300 0x0300
|
||||
#define USB_DEVICE_ID_GTCO_301 0x0301
|
||||
#define USB_DEVICE_ID_GTCO_302 0x0302
|
||||
#define USB_DEVICE_ID_GTCO_303 0x0303
|
||||
#define USB_DEVICE_ID_GTCO_304 0x0304
|
||||
#define USB_DEVICE_ID_GTCO_305 0x0305
|
||||
#define USB_DEVICE_ID_GTCO_306 0x0306
|
||||
#define USB_DEVICE_ID_GTCO_307 0x0307
|
||||
#define USB_DEVICE_ID_GTCO_308 0x0308
|
||||
#define USB_DEVICE_ID_GTCO_309 0x0309
|
||||
#define USB_DEVICE_ID_GTCO_400 0x0400
|
||||
#define USB_DEVICE_ID_GTCO_401 0x0401
|
||||
#define USB_DEVICE_ID_GTCO_402 0x0402
|
||||
#define USB_DEVICE_ID_GTCO_403 0x0403
|
||||
#define USB_DEVICE_ID_GTCO_404 0x0404
|
||||
#define USB_DEVICE_ID_GTCO_405 0x0405
|
||||
#define USB_DEVICE_ID_GTCO_500 0x0500
|
||||
#define USB_DEVICE_ID_GTCO_501 0x0501
|
||||
#define USB_DEVICE_ID_GTCO_502 0x0502
|
||||
#define USB_DEVICE_ID_GTCO_503 0x0503
|
||||
#define USB_DEVICE_ID_GTCO_504 0x0504
|
||||
#define USB_DEVICE_ID_GTCO_1000 0x1000
|
||||
#define USB_DEVICE_ID_GTCO_1001 0x1001
|
||||
#define USB_DEVICE_ID_GTCO_1002 0x1002
|
||||
#define USB_DEVICE_ID_GTCO_1003 0x1003
|
||||
#define USB_DEVICE_ID_GTCO_1004 0x1004
|
||||
#define USB_DEVICE_ID_GTCO_1005 0x1005
|
||||
#define USB_DEVICE_ID_GTCO_1006 0x1006
|
||||
|
||||
#define USB_VENDOR_ID_HAPP 0x078b
|
||||
#define USB_DEVICE_ID_UGCI_DRIVING 0x0010
|
||||
#define USB_DEVICE_ID_UGCI_FLYING 0x0020
|
||||
#define USB_DEVICE_ID_UGCI_FIGHTING 0x0030
|
||||
|
||||
#define USB_VENDOR_ID_IMATION 0x0718
|
||||
#define USB_DEVICE_ID_DISC_STAKKA 0xd000
|
||||
|
||||
#define USB_VENDOR_ID_KBGEAR 0x084e
|
||||
#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001
|
||||
|
||||
#define USB_VENDOR_ID_LD 0x0f11
|
||||
#define USB_DEVICE_ID_LD_CASSY 0x1000
|
||||
#define USB_DEVICE_ID_LD_POCKETCASSY 0x1010
|
||||
#define USB_DEVICE_ID_LD_MOBILECASSY 0x1020
|
||||
#define USB_DEVICE_ID_LD_JWM 0x1080
|
||||
#define USB_DEVICE_ID_LD_DMMP 0x1081
|
||||
#define USB_DEVICE_ID_LD_UMIP 0x1090
|
||||
#define USB_DEVICE_ID_LD_XRAY1 0x1100
|
||||
#define USB_DEVICE_ID_LD_XRAY2 0x1101
|
||||
#define USB_DEVICE_ID_LD_VIDEOCOM 0x1200
|
||||
#define USB_DEVICE_ID_LD_COM3LAB 0x2000
|
||||
#define USB_DEVICE_ID_LD_TELEPORT 0x2010
|
||||
#define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020
|
||||
#define USB_DEVICE_ID_LD_POWERCONTROL 0x2030
|
||||
#define USB_DEVICE_ID_LD_MACHINETEST 0x2040
|
||||
|
||||
#define USB_VENDOR_ID_LOGITECH 0x046d
|
||||
#define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101
|
||||
#define USB_DEVICE_ID_S510_RECEIVER 0xc50c
|
||||
#define USB_DEVICE_ID_S510_RECEIVER_2 0xc517
|
||||
#define USB_DEVICE_ID_MX3000_RECEIVER 0xc513
|
||||
#define USB_DEVICE_ID_DINOVO_EDGE 0xc714
|
||||
|
||||
#define USB_VENDOR_ID_MCC 0x09db
|
||||
#define USB_DEVICE_ID_MCC_PMD1024LS 0x0076
|
||||
#define USB_DEVICE_ID_MCC_PMD1208LS 0x007a
|
||||
|
||||
#define USB_VENDOR_ID_MGE 0x0463
|
||||
#define USB_DEVICE_ID_MGE_UPS 0xffff
|
||||
#define USB_DEVICE_ID_MGE_UPS1 0x0001
|
||||
|
||||
#define USB_VENDOR_ID_NEC 0x073e
|
||||
#define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301
|
||||
|
||||
#define USB_VENDOR_ID_ONTRAK 0x0a07
|
||||
#define USB_DEVICE_ID_ONTRAK_ADU100 0x0064
|
||||
|
||||
#define USB_VENDOR_ID_PANJIT 0x134c
|
||||
|
||||
#define USB_VENDOR_ID_PANTHERLORD 0x0810
|
||||
#define USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK 0x0001
|
||||
|
||||
#define USB_VENDOR_ID_PLAYDOTCOM 0x0b43
|
||||
#define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII 0x0003
|
||||
|
||||
#define USB_VENDOR_ID_SAITEK 0x06a3
|
||||
#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17
|
||||
|
||||
#define USB_VENDOR_ID_SONY 0x054c
|
||||
#define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268
|
||||
|
||||
#define USB_VENDOR_ID_SUN 0x0430
|
||||
#define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab
|
||||
|
||||
#define USB_VENDOR_ID_TOPMAX 0x0663
|
||||
#define USB_DEVICE_ID_TOPMAX_COBRAPAD 0x0103
|
||||
|
||||
#define USB_VENDOR_ID_TURBOX 0x062a
|
||||
#define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201
|
||||
|
||||
#define USB_VENDOR_ID_VERNIER 0x08f7
|
||||
#define USB_DEVICE_ID_VERNIER_LABPRO 0x0001
|
||||
#define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002
|
||||
#define USB_DEVICE_ID_VERNIER_SKIP 0x0003
|
||||
#define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004
|
||||
|
||||
#define USB_VENDOR_ID_WACOM 0x056a
|
||||
|
||||
#define USB_VENDOR_ID_WISEGROUP 0x0925
|
||||
#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101
|
||||
#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104
|
||||
#define USB_DEVICE_ID_8_8_4_IF_KIT 0x8201
|
||||
#define USB_DEVICE_ID_QUAD_USB_JOYPAD 0x8800
|
||||
#define USB_DEVICE_ID_DUAL_USB_JOYPAD 0x8866
|
||||
|
||||
#define USB_VENDOR_ID_WISEGROUP_LTD 0x6677
|
||||
#define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802
|
||||
|
||||
#define USB_VENDOR_ID_YEALINK 0x6993
|
||||
#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001
|
||||
|
||||
/*
|
||||
* Alphabetically sorted blacklist by quirk type.
|
||||
*/
|
||||
|
||||
static const struct hid_blacklist {
|
||||
__u16 idVendor;
|
||||
__u16 idProduct;
|
||||
__u32 quirks;
|
||||
} hid_blacklist[] = {
|
||||
|
||||
{ USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU, HID_QUIRK_2WHEEL_MOUSE_HACK_7 },
|
||||
{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE, HID_QUIRK_2WHEEL_MOUSE_HACK_5 },
|
||||
|
||||
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER, HID_QUIRK_BAD_RELATIVE_KEYS },
|
||||
|
||||
{ USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD, HID_QUIRK_BADPAD },
|
||||
{ USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD },
|
||||
{ USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD },
|
||||
{ USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD },
|
||||
{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_NEC, USB_DEVICE_ID_NEC_USB_GAME_PAD, HID_QUIRK_BADPAD },
|
||||
{ USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
|
||||
{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
|
||||
|
||||
{ USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_CYMOTION },
|
||||
|
||||
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE, HID_QUIRK_DUPLICATE_USAGES },
|
||||
|
||||
{ USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM, HID_QUIRK_HIDDEV },
|
||||
|
||||
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_21, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IR, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_CIDC, 0x0103, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_100, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_101, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_103, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_104, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_105, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_106, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_107, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_108, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_200, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_201, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_202, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_203, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_204, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_205, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_206, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_207, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_300, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_301, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_302, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_303, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_304, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_305, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_306, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_307, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_308, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_309, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_400, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_401, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_402, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_403, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_404, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_405, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_500, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_501, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_502, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_503, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_504, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1000, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1001, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1002, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1003, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1004, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1005, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 30, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 108, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 118, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_PANJIT, 0x0001, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_PANJIT, 0x0002, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_PANJIT, 0x0003, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_PANJIT, 0x0004, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K, HID_QUIRK_IGNORE },
|
||||
|
||||
{ USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302, HID_QUIRK_IGNORE },
|
||||
|
||||
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER, HID_QUIRK_LOGITECH_DESCRIPTOR },
|
||||
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER, HID_QUIRK_LOGITECH_DESCRIPTOR },
|
||||
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2, HID_QUIRK_LOGITECH_DESCRIPTOR },
|
||||
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL },
|
||||
|
||||
{ USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },
|
||||
{ USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT },
|
||||
|
||||
{ USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER, HID_QUIRK_SONY_PS3_CONTROLLER },
|
||||
|
||||
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
|
||||
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
|
||||
|
||||
{ USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
|
||||
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD},
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD},
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD},
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
|
||||
|
||||
{ USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658, HID_QUIRK_RESET_LEDS },
|
||||
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
/* Dynamic HID quirks list - specified at runtime */
|
||||
struct quirks_list_struct {
|
||||
struct hid_blacklist hid_bl_item;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
static LIST_HEAD(dquirks_list);
|
||||
static DECLARE_RWSEM(dquirks_rwsem);
|
||||
|
||||
/* Runtime ("dynamic") quirks manipulation functions */
|
||||
|
||||
/**
|
||||
* usbhid_exists_dquirk: find any dynamic quirks for a USB HID device
|
||||
* @idVendor: the 16-bit USB vendor ID, in native byteorder
|
||||
* @idProduct: the 16-bit USB product ID, in native byteorder
|
||||
*
|
||||
* Description:
|
||||
* Scans dquirks_list for a matching dynamic quirk and returns
|
||||
* the pointer to the relevant struct hid_blacklist if found.
|
||||
* Must be called with a read lock held on dquirks_rwsem.
|
||||
*
|
||||
* Returns: NULL if no quirk found, struct hid_blacklist * if found.
|
||||
*/
|
||||
static struct hid_blacklist *usbhid_exists_dquirk(const u16 idVendor,
|
||||
const u16 idProduct)
|
||||
{
|
||||
struct quirks_list_struct *q;
|
||||
struct hid_blacklist *bl_entry = NULL;
|
||||
|
||||
list_for_each_entry(q, &dquirks_list, node) {
|
||||
if (q->hid_bl_item.idVendor == idVendor &&
|
||||
q->hid_bl_item.idProduct == idProduct) {
|
||||
bl_entry = &q->hid_bl_item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bl_entry != NULL)
|
||||
dbg("Found dynamic quirk 0x%x for USB HID vendor 0x%hx prod 0x%hx\n",
|
||||
bl_entry->quirks, bl_entry->idVendor,
|
||||
bl_entry->idProduct);
|
||||
|
||||
return bl_entry;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* usbhid_modify_dquirk: add/replace a HID quirk
|
||||
* @idVendor: the 16-bit USB vendor ID, in native byteorder
|
||||
* @idProduct: the 16-bit USB product ID, in native byteorder
|
||||
* @quirks: the u32 quirks value to add/replace
|
||||
*
|
||||
* Description:
|
||||
* If an dynamic quirk exists in memory for this (idVendor,
|
||||
* idProduct) pair, replace its quirks value with what was
|
||||
* provided. Otherwise, add the quirk to the dynamic quirks list.
|
||||
*
|
||||
* Returns: 0 OK, -error on failure.
|
||||
*/
|
||||
int usbhid_modify_dquirk(const u16 idVendor, const u16 idProduct,
|
||||
const u32 quirks)
|
||||
{
|
||||
struct quirks_list_struct *q_new, *q;
|
||||
int list_edited = 0;
|
||||
|
||||
if (!idVendor) {
|
||||
dbg("Cannot add a quirk with idVendor = 0");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
q_new = kmalloc(sizeof(struct quirks_list_struct), GFP_KERNEL);
|
||||
if (!q_new) {
|
||||
dbg("Could not allocate quirks_list_struct");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
q_new->hid_bl_item.idVendor = idVendor;
|
||||
q_new->hid_bl_item.idProduct = idProduct;
|
||||
q_new->hid_bl_item.quirks = quirks;
|
||||
|
||||
down_write(&dquirks_rwsem);
|
||||
|
||||
list_for_each_entry(q, &dquirks_list, node) {
|
||||
|
||||
if (q->hid_bl_item.idVendor == idVendor &&
|
||||
q->hid_bl_item.idProduct == idProduct) {
|
||||
|
||||
list_replace(&q->node, &q_new->node);
|
||||
kfree(q);
|
||||
list_edited = 1;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!list_edited)
|
||||
list_add_tail(&q_new->node, &dquirks_list);
|
||||
|
||||
up_write(&dquirks_rwsem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* usbhid_remove_all_dquirks: remove all runtime HID quirks from memory
|
||||
*
|
||||
* Description:
|
||||
* Free all memory associated with dynamic quirks - called before
|
||||
* module unload.
|
||||
*
|
||||
*/
|
||||
static void usbhid_remove_all_dquirks(void)
|
||||
{
|
||||
struct quirks_list_struct *q, *temp;
|
||||
|
||||
down_write(&dquirks_rwsem);
|
||||
list_for_each_entry_safe(q, temp, &dquirks_list, node) {
|
||||
list_del(&q->node);
|
||||
kfree(q);
|
||||
}
|
||||
up_write(&dquirks_rwsem);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* usbhid_quirks_init: apply USB HID quirks specified at module load time
|
||||
*/
|
||||
int usbhid_quirks_init(char **quirks_param)
|
||||
{
|
||||
u16 idVendor, idProduct;
|
||||
u32 quirks;
|
||||
int n = 0, m;
|
||||
|
||||
for (; quirks_param[n] && n < MAX_USBHID_BOOT_QUIRKS; n++) {
|
||||
|
||||
m = sscanf(quirks_param[n], "0x%hx:0x%hx:0x%x",
|
||||
&idVendor, &idProduct, &quirks);
|
||||
|
||||
if (m != 3 ||
|
||||
usbhid_modify_dquirk(idVendor, idProduct, quirks) != 0) {
|
||||
printk(KERN_WARNING
|
||||
"Could not parse HID quirk module param %s\n",
|
||||
quirks_param[n]);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* usbhid_quirks_exit: release memory associated with dynamic_quirks
|
||||
*
|
||||
* Description:
|
||||
* Release all memory associated with dynamic quirks. Called upon
|
||||
* module unload.
|
||||
*
|
||||
* Returns: nothing
|
||||
*/
|
||||
void usbhid_quirks_exit(void)
|
||||
{
|
||||
usbhid_remove_all_dquirks();
|
||||
}
|
||||
|
||||
/**
|
||||
* usbhid_exists_squirk: return any static quirks for a USB HID device
|
||||
* @idVendor: the 16-bit USB vendor ID, in native byteorder
|
||||
* @idProduct: the 16-bit USB product ID, in native byteorder
|
||||
*
|
||||
* Description:
|
||||
* Given a USB vendor ID and product ID, return a pointer to
|
||||
* the hid_blacklist entry associated with that device.
|
||||
*
|
||||
* Returns: pointer if quirk found, or NULL if no quirks found.
|
||||
*/
|
||||
static const struct hid_blacklist *usbhid_exists_squirk(const u16 idVendor,
|
||||
const u16 idProduct)
|
||||
{
|
||||
const struct hid_blacklist *bl_entry = NULL;
|
||||
int n = 0;
|
||||
|
||||
for (; hid_blacklist[n].idVendor; n++)
|
||||
if (hid_blacklist[n].idVendor == idVendor &&
|
||||
hid_blacklist[n].idProduct == idProduct)
|
||||
bl_entry = &hid_blacklist[n];
|
||||
|
||||
if (bl_entry != NULL)
|
||||
dbg("Found squirk 0x%x for USB HID vendor 0x%hx prod 0x%hx\n",
|
||||
bl_entry->quirks, bl_entry->idVendor,
|
||||
bl_entry->idProduct);
|
||||
return bl_entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* usbhid_lookup_quirk: return any quirks associated with a USB HID device
|
||||
* @idVendor: the 16-bit USB vendor ID, in native byteorder
|
||||
* @idProduct: the 16-bit USB product ID, in native byteorder
|
||||
*
|
||||
* Description:
|
||||
* Given a USB vendor ID and product ID, return any quirks associated
|
||||
* with that device.
|
||||
*
|
||||
* Returns: a u32 quirks value.
|
||||
*/
|
||||
u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct)
|
||||
{
|
||||
u32 quirks = 0;
|
||||
const struct hid_blacklist *bl_entry = NULL;
|
||||
|
||||
/* Ignore all Wacom devices */
|
||||
if (idVendor == USB_VENDOR_ID_WACOM)
|
||||
return HID_QUIRK_IGNORE;
|
||||
|
||||
/* ignore all Code Mercenaries IOWarrior devices */
|
||||
if (idVendor == USB_VENDOR_ID_CODEMERCS)
|
||||
if (idProduct >= USB_DEVICE_ID_CODEMERCS_IOW_FIRST &&
|
||||
idProduct <= USB_DEVICE_ID_CODEMERCS_IOW_LAST)
|
||||
return HID_QUIRK_IGNORE;
|
||||
|
||||
down_read(&dquirks_rwsem);
|
||||
bl_entry = usbhid_exists_dquirk(idVendor, idProduct);
|
||||
if (!bl_entry)
|
||||
bl_entry = usbhid_exists_squirk(idVendor, idProduct);
|
||||
if (bl_entry)
|
||||
quirks = bl_entry->quirks;
|
||||
up_read(&dquirks_rwsem);
|
||||
|
||||
return quirks;
|
||||
}
|
||||
|
||||
147
drivers/hid/usbhid/hid-tmff.c
Normal file
147
drivers/hid/usbhid/hid-tmff.c
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Force feedback support for various HID compliant devices by ThrustMaster:
|
||||
* ThrustMaster FireStorm Dual Power 2
|
||||
* and possibly others whose device ids haven't been added.
|
||||
*
|
||||
* Modified to support ThrustMaster devices by Zinx Verituse
|
||||
* on 2003-01-25 from the Logitech force feedback driver,
|
||||
* which is by Johann Deneux.
|
||||
*
|
||||
* Copyright (c) 2003 Zinx Verituse <zinx@epicsol.org>
|
||||
* Copyright (c) 2002 Johann Deneux
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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>
|
||||
|
||||
#undef DEBUG
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include <linux/hid.h>
|
||||
#include "usbhid.h"
|
||||
|
||||
/* Usages for thrustmaster devices I know about */
|
||||
#define THRUSTMASTER_USAGE_RUMBLE_LR (HID_UP_GENDESK | 0xbb)
|
||||
|
||||
|
||||
struct tmff_device {
|
||||
struct hid_report *report;
|
||||
struct hid_field *rumble;
|
||||
};
|
||||
|
||||
/* Changes values from 0 to 0xffff into values from minimum to maximum */
|
||||
static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = (in * (maximum - minimum) / 0xffff) + minimum;
|
||||
if (ret < minimum)
|
||||
return minimum;
|
||||
if (ret > maximum)
|
||||
return maximum;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
struct tmff_device *tmff = data;
|
||||
int left, right; /* Rumbling */
|
||||
|
||||
left = hid_tmff_scale(effect->u.rumble.weak_magnitude,
|
||||
tmff->rumble->logical_minimum, tmff->rumble->logical_maximum);
|
||||
right = hid_tmff_scale(effect->u.rumble.strong_magnitude,
|
||||
tmff->rumble->logical_minimum, tmff->rumble->logical_maximum);
|
||||
|
||||
tmff->rumble->value[0] = left;
|
||||
tmff->rumble->value[1] = right;
|
||||
dbg("(left,right)=(%08x, %08x)", left, right);
|
||||
usbhid_submit_report(hid, tmff->report, USB_DIR_OUT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hid_tmff_init(struct hid_device *hid)
|
||||
{
|
||||
struct tmff_device *tmff;
|
||||
struct list_head *pos;
|
||||
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||
struct input_dev *input_dev = hidinput->input;
|
||||
int error;
|
||||
|
||||
tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL);
|
||||
if (!tmff)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Find the report to use */
|
||||
__list_for_each(pos, &hid->report_enum[HID_OUTPUT_REPORT].report_list) {
|
||||
struct hid_report *report = (struct hid_report *)pos;
|
||||
int fieldnum;
|
||||
|
||||
for (fieldnum = 0; fieldnum < report->maxfield; ++fieldnum) {
|
||||
struct hid_field *field = report->field[fieldnum];
|
||||
|
||||
if (field->maxusage <= 0)
|
||||
continue;
|
||||
|
||||
switch (field->usage[0].hid) {
|
||||
case THRUSTMASTER_USAGE_RUMBLE_LR:
|
||||
if (field->report_count < 2) {
|
||||
warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR with report_count < 2");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (field->logical_maximum == field->logical_minimum) {
|
||||
warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR with logical_maximum == logical_minimum");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tmff->report && tmff->report != report) {
|
||||
warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR in other report");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tmff->rumble && tmff->rumble != field) {
|
||||
warn("ignoring duplicate THRUSTMASTER_USAGE_RUMBLE_LR");
|
||||
continue;
|
||||
}
|
||||
|
||||
tmff->report = report;
|
||||
tmff->rumble = field;
|
||||
|
||||
set_bit(FF_RUMBLE, input_dev->ffbit);
|
||||
break;
|
||||
|
||||
default:
|
||||
warn("ignoring unknown output usage %08x", field->usage[0].hid);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
error = input_ff_create_memless(input_dev, tmff, hid_tmff_play);
|
||||
if (error) {
|
||||
kfree(tmff);
|
||||
return error;
|
||||
}
|
||||
|
||||
info("Force feedback for ThrustMaster rumble pad devices by Zinx Verituse <[email protected]>");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
111
drivers/hid/usbhid/hid-zpff.c
Normal file
111
drivers/hid/usbhid/hid-zpff.c
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Force feedback support for Zeroplus based devices
|
||||
*
|
||||
* Copyright (c) 2005, 2006 Anssi Hannula <anssi.hannula@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
|
||||
*/
|
||||
|
||||
|
||||
/* #define DEBUG */
|
||||
|
||||
#define debug(format, arg...) pr_debug("hid-zpff: " format "\n" , ## arg)
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/hid.h>
|
||||
#include "usbhid.h"
|
||||
|
||||
struct zpff_device {
|
||||
struct hid_report *report;
|
||||
};
|
||||
|
||||
static int hid_zpff_play(struct input_dev *dev, void *data,
|
||||
struct ff_effect *effect)
|
||||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
struct zpff_device *zpff = data;
|
||||
int left, right;
|
||||
|
||||
/*
|
||||
* The following is specified the other way around in the Zeroplus
|
||||
* datasheet but the order below is correct for the XFX Executioner;
|
||||
* however it is possible that the XFX Executioner is an exception
|
||||
*/
|
||||
|
||||
left = effect->u.rumble.strong_magnitude;
|
||||
right = effect->u.rumble.weak_magnitude;
|
||||
debug("called with 0x%04x 0x%04x", left, right);
|
||||
|
||||
left = left * 0x7f / 0xffff;
|
||||
right = right * 0x7f / 0xffff;
|
||||
|
||||
zpff->report->field[2]->value[0] = left;
|
||||
zpff->report->field[3]->value[0] = right;
|
||||
debug("running with 0x%02x 0x%02x", left, right);
|
||||
usbhid_submit_report(hid, zpff->report, USB_DIR_OUT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hid_zpff_init(struct hid_device *hid)
|
||||
{
|
||||
struct zpff_device *zpff;
|
||||
struct hid_report *report;
|
||||
struct hid_input *hidinput = list_entry(hid->inputs.next,
|
||||
struct hid_input, list);
|
||||
struct list_head *report_list =
|
||||
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||
struct input_dev *dev = hidinput->input;
|
||||
int error;
|
||||
|
||||
if (list_empty(report_list)) {
|
||||
printk(KERN_ERR "hid-zpff: no output report found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
report = list_entry(report_list->next, struct hid_report, list);
|
||||
|
||||
if (report->maxfield < 4) {
|
||||
printk(KERN_ERR "hid-zpff: not enough fields in report\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
zpff = kzalloc(sizeof(struct zpff_device), GFP_KERNEL);
|
||||
if (!zpff)
|
||||
return -ENOMEM;
|
||||
|
||||
set_bit(FF_RUMBLE, dev->ffbit);
|
||||
|
||||
error = input_ff_create_memless(dev, zpff, hid_zpff_play);
|
||||
if (error) {
|
||||
kfree(zpff);
|
||||
return error;
|
||||
}
|
||||
|
||||
zpff->report = report;
|
||||
zpff->report->field[0]->value[0] = 0x00;
|
||||
zpff->report->field[1]->value[0] = 0x02;
|
||||
zpff->report->field[2]->value[0] = 0x00;
|
||||
zpff->report->field[3]->value[0] = 0x00;
|
||||
usbhid_submit_report(hid, zpff->report, USB_DIR_OUT);
|
||||
|
||||
printk(KERN_INFO "Force feedback for Zeroplus based devices by "
|
||||
"Anssi Hannula <[email protected]>\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
847
drivers/hid/usbhid/hiddev.c
Normal file
847
drivers/hid/usbhid/hiddev.c
Normal file
|
|
@ -0,0 +1,847 @@
|
|||
/*
|
||||
* Copyright (c) 2001 Paul Stewart
|
||||
* Copyright (c) 2001 Vojtech Pavlik
|
||||
*
|
||||
* HID char devices, giving access to raw HID device events.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* Should you need to contact me, the author, you can do so either by
|
||||
* e-mail - mail your message to Paul Stewart <stewart@wetlogic.net>
|
||||
*/
|
||||
|
||||
#include <linux/poll.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/hiddev.h>
|
||||
#include "usbhid.h"
|
||||
|
||||
#ifdef CONFIG_USB_DYNAMIC_MINORS
|
||||
#define HIDDEV_MINOR_BASE 0
|
||||
#define HIDDEV_MINORS 256
|
||||
#else
|
||||
#define HIDDEV_MINOR_BASE 96
|
||||
#define HIDDEV_MINORS 16
|
||||
#endif
|
||||
#define HIDDEV_BUFFER_SIZE 64
|
||||
|
||||
struct hiddev {
|
||||
int exist;
|
||||
int open;
|
||||
wait_queue_head_t wait;
|
||||
struct hid_device *hid;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct hiddev_list {
|
||||
struct hiddev_usage_ref buffer[HIDDEV_BUFFER_SIZE];
|
||||
int head;
|
||||
int tail;
|
||||
unsigned flags;
|
||||
struct fasync_struct *fasync;
|
||||
struct hiddev *hiddev;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
static struct hiddev *hiddev_table[HIDDEV_MINORS];
|
||||
|
||||
/*
|
||||
* Find a report, given the report's type and ID. The ID can be specified
|
||||
* indirectly by REPORT_ID_FIRST (which returns the first report of the given
|
||||
* type) or by (REPORT_ID_NEXT | old_id), which returns the next report of the
|
||||
* given type which follows old_id.
|
||||
*/
|
||||
static struct hid_report *
|
||||
hiddev_lookup_report(struct hid_device *hid, struct hiddev_report_info *rinfo)
|
||||
{
|
||||
unsigned int flags = rinfo->report_id & ~HID_REPORT_ID_MASK;
|
||||
unsigned int rid = rinfo->report_id & HID_REPORT_ID_MASK;
|
||||
struct hid_report_enum *report_enum;
|
||||
struct hid_report *report;
|
||||
struct list_head *list;
|
||||
|
||||
if (rinfo->report_type < HID_REPORT_TYPE_MIN ||
|
||||
rinfo->report_type > HID_REPORT_TYPE_MAX)
|
||||
return NULL;
|
||||
|
||||
report_enum = hid->report_enum +
|
||||
(rinfo->report_type - HID_REPORT_TYPE_MIN);
|
||||
|
||||
switch (flags) {
|
||||
case 0: /* Nothing to do -- report_id is already set correctly */
|
||||
break;
|
||||
|
||||
case HID_REPORT_ID_FIRST:
|
||||
if (list_empty(&report_enum->report_list))
|
||||
return NULL;
|
||||
|
||||
list = report_enum->report_list.next;
|
||||
report = list_entry(list, struct hid_report, list);
|
||||
rinfo->report_id = report->id;
|
||||
break;
|
||||
|
||||
case HID_REPORT_ID_NEXT:
|
||||
report = report_enum->report_id_hash[rid];
|
||||
if (!report)
|
||||
return NULL;
|
||||
|
||||
list = report->list.next;
|
||||
if (list == &report_enum->report_list)
|
||||
return NULL;
|
||||
|
||||
report = list_entry(list, struct hid_report, list);
|
||||
rinfo->report_id = report->id;
|
||||
break;
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return report_enum->report_id_hash[rinfo->report_id];
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform an exhaustive search of the report table for a usage, given its
|
||||
* type and usage id.
|
||||
*/
|
||||
static struct hid_field *
|
||||
hiddev_lookup_usage(struct hid_device *hid, struct hiddev_usage_ref *uref)
|
||||
{
|
||||
int i, j;
|
||||
struct hid_report *report;
|
||||
struct hid_report_enum *report_enum;
|
||||
struct hid_field *field;
|
||||
|
||||
if (uref->report_type < HID_REPORT_TYPE_MIN ||
|
||||
uref->report_type > HID_REPORT_TYPE_MAX)
|
||||
return NULL;
|
||||
|
||||
report_enum = hid->report_enum +
|
||||
(uref->report_type - HID_REPORT_TYPE_MIN);
|
||||
|
||||
list_for_each_entry(report, &report_enum->report_list, list) {
|
||||
for (i = 0; i < report->maxfield; i++) {
|
||||
field = report->field[i];
|
||||
for (j = 0; j < field->maxusage; j++) {
|
||||
if (field->usage[j].hid == uref->usage_code) {
|
||||
uref->report_id = report->id;
|
||||
uref->field_index = i;
|
||||
uref->usage_index = j;
|
||||
return field;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void hiddev_send_event(struct hid_device *hid,
|
||||
struct hiddev_usage_ref *uref)
|
||||
{
|
||||
struct hiddev *hiddev = hid->hiddev;
|
||||
struct hiddev_list *list;
|
||||
|
||||
list_for_each_entry(list, &hiddev->list, node) {
|
||||
if (uref->field_index != HID_FIELD_INDEX_NONE ||
|
||||
(list->flags & HIDDEV_FLAG_REPORT) != 0) {
|
||||
list->buffer[list->head] = *uref;
|
||||
list->head = (list->head + 1) &
|
||||
(HIDDEV_BUFFER_SIZE - 1);
|
||||
kill_fasync(&list->fasync, SIGIO, POLL_IN);
|
||||
}
|
||||
}
|
||||
|
||||
wake_up_interruptible(&hiddev->wait);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is where hid.c calls into hiddev to pass an event that occurred over
|
||||
* the interrupt pipe
|
||||
*/
|
||||
void hiddev_hid_event(struct hid_device *hid, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
unsigned type = field->report_type;
|
||||
struct hiddev_usage_ref uref;
|
||||
|
||||
uref.report_type =
|
||||
(type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT :
|
||||
((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT :
|
||||
((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0));
|
||||
uref.report_id = field->report->id;
|
||||
uref.field_index = field->index;
|
||||
uref.usage_index = (usage - field->usage);
|
||||
uref.usage_code = usage->hid;
|
||||
uref.value = value;
|
||||
|
||||
hiddev_send_event(hid, &uref);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hiddev_hid_event);
|
||||
|
||||
void hiddev_report_event(struct hid_device *hid, struct hid_report *report)
|
||||
{
|
||||
unsigned type = report->type;
|
||||
struct hiddev_usage_ref uref;
|
||||
|
||||
memset(&uref, 0, sizeof(uref));
|
||||
uref.report_type =
|
||||
(type == HID_INPUT_REPORT) ? HID_REPORT_TYPE_INPUT :
|
||||
((type == HID_OUTPUT_REPORT) ? HID_REPORT_TYPE_OUTPUT :
|
||||
((type == HID_FEATURE_REPORT) ? HID_REPORT_TYPE_FEATURE : 0));
|
||||
uref.report_id = report->id;
|
||||
uref.field_index = HID_FIELD_INDEX_NONE;
|
||||
|
||||
hiddev_send_event(hid, &uref);
|
||||
}
|
||||
|
||||
/*
|
||||
* fasync file op
|
||||
*/
|
||||
static int hiddev_fasync(int fd, struct file *file, int on)
|
||||
{
|
||||
int retval;
|
||||
struct hiddev_list *list = file->private_data;
|
||||
|
||||
retval = fasync_helper(fd, file, on, &list->fasync);
|
||||
|
||||
return retval < 0 ? retval : 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* release file op
|
||||
*/
|
||||
static int hiddev_release(struct inode * inode, struct file * file)
|
||||
{
|
||||
struct hiddev_list *list = file->private_data;
|
||||
|
||||
hiddev_fasync(-1, file, 0);
|
||||
list_del(&list->node);
|
||||
|
||||
if (!--list->hiddev->open) {
|
||||
if (list->hiddev->exist)
|
||||
usbhid_close(list->hiddev->hid);
|
||||
else
|
||||
kfree(list->hiddev);
|
||||
}
|
||||
|
||||
kfree(list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* open file op
|
||||
*/
|
||||
static int hiddev_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct hiddev_list *list;
|
||||
|
||||
int i = iminor(inode) - HIDDEV_MINOR_BASE;
|
||||
|
||||
if (i >= HIDDEV_MINORS || !hiddev_table[i])
|
||||
return -ENODEV;
|
||||
|
||||
if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
|
||||
list->hiddev = hiddev_table[i];
|
||||
list_add_tail(&list->node, &hiddev_table[i]->list);
|
||||
file->private_data = list;
|
||||
|
||||
if (!list->hiddev->open++)
|
||||
if (list->hiddev->exist)
|
||||
usbhid_open(hiddev_table[i]->hid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* "write" file op
|
||||
*/
|
||||
static ssize_t hiddev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* "read" file op
|
||||
*/
|
||||
static ssize_t hiddev_read(struct file * file, char __user * buffer, size_t count, loff_t *ppos)
|
||||
{
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
struct hiddev_list *list = file->private_data;
|
||||
int event_size;
|
||||
int retval = 0;
|
||||
|
||||
event_size = ((list->flags & HIDDEV_FLAG_UREF) != 0) ?
|
||||
sizeof(struct hiddev_usage_ref) : sizeof(struct hiddev_event);
|
||||
|
||||
if (count < event_size)
|
||||
return 0;
|
||||
|
||||
while (retval == 0) {
|
||||
if (list->head == list->tail) {
|
||||
add_wait_queue(&list->hiddev->wait, &wait);
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
while (list->head == list->tail) {
|
||||
if (file->f_flags & O_NONBLOCK) {
|
||||
retval = -EAGAIN;
|
||||
break;
|
||||
}
|
||||
if (signal_pending(current)) {
|
||||
retval = -ERESTARTSYS;
|
||||
break;
|
||||
}
|
||||
if (!list->hiddev->exist) {
|
||||
retval = -EIO;
|
||||
break;
|
||||
}
|
||||
|
||||
schedule();
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
}
|
||||
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&list->hiddev->wait, &wait);
|
||||
}
|
||||
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
|
||||
while (list->head != list->tail &&
|
||||
retval + event_size <= count) {
|
||||
if ((list->flags & HIDDEV_FLAG_UREF) == 0) {
|
||||
if (list->buffer[list->tail].field_index !=
|
||||
HID_FIELD_INDEX_NONE) {
|
||||
struct hiddev_event event;
|
||||
event.hid = list->buffer[list->tail].usage_code;
|
||||
event.value = list->buffer[list->tail].value;
|
||||
if (copy_to_user(buffer + retval, &event, sizeof(struct hiddev_event)))
|
||||
return -EFAULT;
|
||||
retval += sizeof(struct hiddev_event);
|
||||
}
|
||||
} else {
|
||||
if (list->buffer[list->tail].field_index != HID_FIELD_INDEX_NONE ||
|
||||
(list->flags & HIDDEV_FLAG_REPORT) != 0) {
|
||||
if (copy_to_user(buffer + retval, list->buffer + list->tail, sizeof(struct hiddev_usage_ref)))
|
||||
return -EFAULT;
|
||||
retval += sizeof(struct hiddev_usage_ref);
|
||||
}
|
||||
}
|
||||
list->tail = (list->tail + 1) & (HIDDEV_BUFFER_SIZE - 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* "poll" file op
|
||||
* No kernel lock - fine
|
||||
*/
|
||||
static unsigned int hiddev_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct hiddev_list *list = file->private_data;
|
||||
|
||||
poll_wait(file, &list->hiddev->wait, wait);
|
||||
if (list->head != list->tail)
|
||||
return POLLIN | POLLRDNORM;
|
||||
if (!list->hiddev->exist)
|
||||
return POLLERR | POLLHUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* "ioctl" file op
|
||||
*/
|
||||
static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct hiddev_list *list = file->private_data;
|
||||
struct hiddev *hiddev = list->hiddev;
|
||||
struct hid_device *hid = hiddev->hid;
|
||||
struct usb_device *dev = hid_to_usb_dev(hid);
|
||||
struct hiddev_collection_info cinfo;
|
||||
struct hiddev_report_info rinfo;
|
||||
struct hiddev_field_info finfo;
|
||||
struct hiddev_usage_ref_multi *uref_multi = NULL;
|
||||
struct hiddev_usage_ref *uref;
|
||||
struct hiddev_devinfo dinfo;
|
||||
struct hid_report *report;
|
||||
struct hid_field *field;
|
||||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
void __user *user_arg = (void __user *)arg;
|
||||
int i;
|
||||
|
||||
if (!hiddev->exist)
|
||||
return -EIO;
|
||||
|
||||
switch (cmd) {
|
||||
|
||||
case HIDIOCGVERSION:
|
||||
return put_user(HID_VERSION, (int __user *)arg);
|
||||
|
||||
case HIDIOCAPPLICATION:
|
||||
if (arg < 0 || arg >= hid->maxapplication)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < hid->maxcollection; i++)
|
||||
if (hid->collection[i].type ==
|
||||
HID_COLLECTION_APPLICATION && arg-- == 0)
|
||||
break;
|
||||
|
||||
if (i == hid->maxcollection)
|
||||
return -EINVAL;
|
||||
|
||||
return hid->collection[i].usage;
|
||||
|
||||
case HIDIOCGDEVINFO:
|
||||
dinfo.bustype = BUS_USB;
|
||||
dinfo.busnum = dev->bus->busnum;
|
||||
dinfo.devnum = dev->devnum;
|
||||
dinfo.ifnum = usbhid->ifnum;
|
||||
dinfo.vendor = le16_to_cpu(dev->descriptor.idVendor);
|
||||
dinfo.product = le16_to_cpu(dev->descriptor.idProduct);
|
||||
dinfo.version = le16_to_cpu(dev->descriptor.bcdDevice);
|
||||
dinfo.num_applications = hid->maxapplication;
|
||||
if (copy_to_user(user_arg, &dinfo, sizeof(dinfo)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
|
||||
case HIDIOCGFLAG:
|
||||
if (put_user(list->flags, (int __user *)arg))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
|
||||
case HIDIOCSFLAG:
|
||||
{
|
||||
int newflags;
|
||||
if (get_user(newflags, (int __user *)arg))
|
||||
return -EFAULT;
|
||||
|
||||
if ((newflags & ~HIDDEV_FLAGS) != 0 ||
|
||||
((newflags & HIDDEV_FLAG_REPORT) != 0 &&
|
||||
(newflags & HIDDEV_FLAG_UREF) == 0))
|
||||
return -EINVAL;
|
||||
|
||||
list->flags = newflags;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
case HIDIOCGSTRING:
|
||||
{
|
||||
int idx, len;
|
||||
char *buf;
|
||||
|
||||
if (get_user(idx, (int __user *)arg))
|
||||
return -EFAULT;
|
||||
|
||||
if ((buf = kmalloc(HID_STRING_SIZE, GFP_KERNEL)) == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if ((len = usb_string(dev, idx, buf, HID_STRING_SIZE-1)) < 0) {
|
||||
kfree(buf);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (copy_to_user(user_arg+sizeof(int), buf, len+1)) {
|
||||
kfree(buf);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
kfree(buf);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
case HIDIOCINITREPORT:
|
||||
usbhid_init_reports(hid);
|
||||
|
||||
return 0;
|
||||
|
||||
case HIDIOCGREPORT:
|
||||
if (copy_from_user(&rinfo, user_arg, sizeof(rinfo)))
|
||||
return -EFAULT;
|
||||
|
||||
if (rinfo.report_type == HID_REPORT_TYPE_OUTPUT)
|
||||
return -EINVAL;
|
||||
|
||||
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
usbhid_submit_report(hid, report, USB_DIR_IN);
|
||||
usbhid_wait_io(hid);
|
||||
|
||||
return 0;
|
||||
|
||||
case HIDIOCSREPORT:
|
||||
if (copy_from_user(&rinfo, user_arg, sizeof(rinfo)))
|
||||
return -EFAULT;
|
||||
|
||||
if (rinfo.report_type == HID_REPORT_TYPE_INPUT)
|
||||
return -EINVAL;
|
||||
|
||||
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
usbhid_submit_report(hid, report, USB_DIR_OUT);
|
||||
usbhid_wait_io(hid);
|
||||
|
||||
return 0;
|
||||
|
||||
case HIDIOCGREPORTINFO:
|
||||
if (copy_from_user(&rinfo, user_arg, sizeof(rinfo)))
|
||||
return -EFAULT;
|
||||
|
||||
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
rinfo.num_fields = report->maxfield;
|
||||
|
||||
if (copy_to_user(user_arg, &rinfo, sizeof(rinfo)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
|
||||
case HIDIOCGFIELDINFO:
|
||||
if (copy_from_user(&finfo, user_arg, sizeof(finfo)))
|
||||
return -EFAULT;
|
||||
rinfo.report_type = finfo.report_type;
|
||||
rinfo.report_id = finfo.report_id;
|
||||
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (finfo.field_index >= report->maxfield)
|
||||
return -EINVAL;
|
||||
|
||||
field = report->field[finfo.field_index];
|
||||
memset(&finfo, 0, sizeof(finfo));
|
||||
finfo.report_type = rinfo.report_type;
|
||||
finfo.report_id = rinfo.report_id;
|
||||
finfo.field_index = field->report_count - 1;
|
||||
finfo.maxusage = field->maxusage;
|
||||
finfo.flags = field->flags;
|
||||
finfo.physical = field->physical;
|
||||
finfo.logical = field->logical;
|
||||
finfo.application = field->application;
|
||||
finfo.logical_minimum = field->logical_minimum;
|
||||
finfo.logical_maximum = field->logical_maximum;
|
||||
finfo.physical_minimum = field->physical_minimum;
|
||||
finfo.physical_maximum = field->physical_maximum;
|
||||
finfo.unit_exponent = field->unit_exponent;
|
||||
finfo.unit = field->unit;
|
||||
|
||||
if (copy_to_user(user_arg, &finfo, sizeof(finfo)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
|
||||
case HIDIOCGUCODE:
|
||||
uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL);
|
||||
if (!uref_multi)
|
||||
return -ENOMEM;
|
||||
uref = &uref_multi->uref;
|
||||
if (copy_from_user(uref, user_arg, sizeof(*uref)))
|
||||
goto fault;
|
||||
|
||||
rinfo.report_type = uref->report_type;
|
||||
rinfo.report_id = uref->report_id;
|
||||
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
|
||||
goto inval;
|
||||
|
||||
if (uref->field_index >= report->maxfield)
|
||||
goto inval;
|
||||
|
||||
field = report->field[uref->field_index];
|
||||
if (uref->usage_index >= field->maxusage)
|
||||
goto inval;
|
||||
|
||||
uref->usage_code = field->usage[uref->usage_index].hid;
|
||||
|
||||
if (copy_to_user(user_arg, uref, sizeof(*uref)))
|
||||
goto fault;
|
||||
|
||||
kfree(uref_multi);
|
||||
return 0;
|
||||
|
||||
case HIDIOCGUSAGE:
|
||||
case HIDIOCSUSAGE:
|
||||
case HIDIOCGUSAGES:
|
||||
case HIDIOCSUSAGES:
|
||||
case HIDIOCGCOLLECTIONINDEX:
|
||||
uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL);
|
||||
if (!uref_multi)
|
||||
return -ENOMEM;
|
||||
uref = &uref_multi->uref;
|
||||
if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) {
|
||||
if (copy_from_user(uref_multi, user_arg,
|
||||
sizeof(*uref_multi)))
|
||||
goto fault;
|
||||
} else {
|
||||
if (copy_from_user(uref, user_arg, sizeof(*uref)))
|
||||
goto fault;
|
||||
}
|
||||
|
||||
if (cmd != HIDIOCGUSAGE &&
|
||||
cmd != HIDIOCGUSAGES &&
|
||||
uref->report_type == HID_REPORT_TYPE_INPUT)
|
||||
goto inval;
|
||||
|
||||
if (uref->report_id == HID_REPORT_ID_UNKNOWN) {
|
||||
field = hiddev_lookup_usage(hid, uref);
|
||||
if (field == NULL)
|
||||
goto inval;
|
||||
} else {
|
||||
rinfo.report_type = uref->report_type;
|
||||
rinfo.report_id = uref->report_id;
|
||||
if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
|
||||
goto inval;
|
||||
|
||||
if (uref->field_index >= report->maxfield)
|
||||
goto inval;
|
||||
|
||||
field = report->field[uref->field_index];
|
||||
|
||||
if (cmd == HIDIOCGCOLLECTIONINDEX) {
|
||||
if (uref->usage_index >= field->maxusage)
|
||||
goto inval;
|
||||
} else if (uref->usage_index >= field->report_count)
|
||||
goto inval;
|
||||
|
||||
else if ((cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) &&
|
||||
(uref_multi->num_values > HID_MAX_MULTI_USAGES ||
|
||||
uref->usage_index + uref_multi->num_values > field->report_count))
|
||||
goto inval;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case HIDIOCGUSAGE:
|
||||
uref->value = field->value[uref->usage_index];
|
||||
if (copy_to_user(user_arg, uref, sizeof(*uref)))
|
||||
goto fault;
|
||||
goto goodreturn;
|
||||
|
||||
case HIDIOCSUSAGE:
|
||||
field->value[uref->usage_index] = uref->value;
|
||||
goto goodreturn;
|
||||
|
||||
case HIDIOCGCOLLECTIONINDEX:
|
||||
kfree(uref_multi);
|
||||
return field->usage[uref->usage_index].collection_index;
|
||||
case HIDIOCGUSAGES:
|
||||
for (i = 0; i < uref_multi->num_values; i++)
|
||||
uref_multi->values[i] =
|
||||
field->value[uref->usage_index + i];
|
||||
if (copy_to_user(user_arg, uref_multi,
|
||||
sizeof(*uref_multi)))
|
||||
goto fault;
|
||||
goto goodreturn;
|
||||
case HIDIOCSUSAGES:
|
||||
for (i = 0; i < uref_multi->num_values; i++)
|
||||
field->value[uref->usage_index + i] =
|
||||
uref_multi->values[i];
|
||||
goto goodreturn;
|
||||
}
|
||||
|
||||
goodreturn:
|
||||
kfree(uref_multi);
|
||||
return 0;
|
||||
fault:
|
||||
kfree(uref_multi);
|
||||
return -EFAULT;
|
||||
inval:
|
||||
kfree(uref_multi);
|
||||
return -EINVAL;
|
||||
|
||||
case HIDIOCGCOLLECTIONINFO:
|
||||
if (copy_from_user(&cinfo, user_arg, sizeof(cinfo)))
|
||||
return -EFAULT;
|
||||
|
||||
if (cinfo.index >= hid->maxcollection)
|
||||
return -EINVAL;
|
||||
|
||||
cinfo.type = hid->collection[cinfo.index].type;
|
||||
cinfo.usage = hid->collection[cinfo.index].usage;
|
||||
cinfo.level = hid->collection[cinfo.index].level;
|
||||
|
||||
if (copy_to_user(user_arg, &cinfo, sizeof(cinfo)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
default:
|
||||
|
||||
if (_IOC_TYPE(cmd) != 'H' || _IOC_DIR(cmd) != _IOC_READ)
|
||||
return -EINVAL;
|
||||
|
||||
if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGNAME(0))) {
|
||||
int len;
|
||||
if (!hid->name)
|
||||
return 0;
|
||||
len = strlen(hid->name) + 1;
|
||||
if (len > _IOC_SIZE(cmd))
|
||||
len = _IOC_SIZE(cmd);
|
||||
return copy_to_user(user_arg, hid->name, len) ?
|
||||
-EFAULT : len;
|
||||
}
|
||||
|
||||
if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGPHYS(0))) {
|
||||
int len;
|
||||
if (!hid->phys)
|
||||
return 0;
|
||||
len = strlen(hid->phys) + 1;
|
||||
if (len > _IOC_SIZE(cmd))
|
||||
len = _IOC_SIZE(cmd);
|
||||
return copy_to_user(user_arg, hid->phys, len) ?
|
||||
-EFAULT : len;
|
||||
}
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct file_operations hiddev_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = hiddev_read,
|
||||
.write = hiddev_write,
|
||||
.poll = hiddev_poll,
|
||||
.open = hiddev_open,
|
||||
.release = hiddev_release,
|
||||
.ioctl = hiddev_ioctl,
|
||||
.fasync = hiddev_fasync,
|
||||
};
|
||||
|
||||
static struct usb_class_driver hiddev_class = {
|
||||
.name = "hiddev%d",
|
||||
.fops = &hiddev_fops,
|
||||
.minor_base = HIDDEV_MINOR_BASE,
|
||||
};
|
||||
|
||||
/*
|
||||
* This is where hid.c calls us to connect a hid device to the hiddev driver
|
||||
*/
|
||||
int hiddev_connect(struct hid_device *hid)
|
||||
{
|
||||
struct hiddev *hiddev;
|
||||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
int i;
|
||||
int retval;
|
||||
|
||||
for (i = 0; i < hid->maxcollection; i++)
|
||||
if (hid->collection[i].type ==
|
||||
HID_COLLECTION_APPLICATION &&
|
||||
!IS_INPUT_APPLICATION(hid->collection[i].usage))
|
||||
break;
|
||||
|
||||
if (i == hid->maxcollection && (hid->quirks & HID_QUIRK_HIDDEV) == 0)
|
||||
return -1;
|
||||
|
||||
if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL)))
|
||||
return -1;
|
||||
|
||||
retval = usb_register_dev(usbhid->intf, &hiddev_class);
|
||||
if (retval) {
|
||||
err("Not able to get a minor for this device.");
|
||||
kfree(hiddev);
|
||||
return -1;
|
||||
}
|
||||
|
||||
init_waitqueue_head(&hiddev->wait);
|
||||
INIT_LIST_HEAD(&hiddev->list);
|
||||
hiddev->hid = hid;
|
||||
hiddev->exist = 1;
|
||||
|
||||
hid->minor = usbhid->intf->minor;
|
||||
hid->hiddev = hiddev;
|
||||
|
||||
hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is where hid.c calls us to disconnect a hiddev device from the
|
||||
* corresponding hid device (usually because the usb device has disconnected)
|
||||
*/
|
||||
static struct usb_class_driver hiddev_class;
|
||||
void hiddev_disconnect(struct hid_device *hid)
|
||||
{
|
||||
struct hiddev *hiddev = hid->hiddev;
|
||||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
|
||||
hiddev->exist = 0;
|
||||
|
||||
hiddev_table[hiddev->hid->minor - HIDDEV_MINOR_BASE] = NULL;
|
||||
usb_deregister_dev(usbhid->intf, &hiddev_class);
|
||||
|
||||
if (hiddev->open) {
|
||||
usbhid_close(hiddev->hid);
|
||||
wake_up_interruptible(&hiddev->wait);
|
||||
} else {
|
||||
kfree(hiddev);
|
||||
}
|
||||
}
|
||||
|
||||
/* Currently this driver is a USB driver. It's not a conventional one in
|
||||
* the sense that it doesn't probe at the USB level. Instead it waits to
|
||||
* be connected by HID through the hiddev_connect / hiddev_disconnect
|
||||
* routines. The reason to register as a USB device is to gain part of the
|
||||
* minor number space from the USB major.
|
||||
*
|
||||
* In theory, should the HID code be generalized to more than one physical
|
||||
* medium (say, IEEE 1384), this driver will probably need to register its
|
||||
* own major number, and in doing so, no longer need to register with USB.
|
||||
* At that point the probe routine and hiddev_driver struct below will no
|
||||
* longer be useful.
|
||||
*/
|
||||
|
||||
|
||||
/* We never attach in this manner, and rely on HID to connect us. This
|
||||
* is why there is no disconnect routine defined in the usb_driver either.
|
||||
*/
|
||||
static int hiddev_usbd_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *hiddev_info)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
||||
static /* const */ struct usb_driver hiddev_driver = {
|
||||
.name = "hiddev",
|
||||
.probe = hiddev_usbd_probe,
|
||||
};
|
||||
|
||||
int __init hiddev_init(void)
|
||||
{
|
||||
return usb_register(&hiddev_driver);
|
||||
}
|
||||
|
||||
void hiddev_exit(void)
|
||||
{
|
||||
usb_deregister(&hiddev_driver);
|
||||
}
|
||||
87
drivers/hid/usbhid/usbhid.h
Normal file
87
drivers/hid/usbhid/usbhid.h
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
#ifndef __USBHID_H
|
||||
#define __USBHID_H
|
||||
|
||||
/*
|
||||
* Copyright (c) 1999 Andreas Gal
|
||||
* Copyright (c) 2000-2001 Vojtech Pavlik
|
||||
* Copyright (c) 2006 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.
|
||||
*
|
||||
* 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/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
/* API provided by hid-core.c for USB HID drivers */
|
||||
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);
|
||||
|
||||
/*
|
||||
* USB-specific HID struct, to be pointed to
|
||||
* from struct hid_device->driver_data
|
||||
*/
|
||||
|
||||
struct usbhid_device {
|
||||
struct hid_device *hid; /* pointer to corresponding HID dev */
|
||||
|
||||
struct usb_interface *intf; /* USB interface */
|
||||
int ifnum; /* USB interface number */
|
||||
|
||||
unsigned int bufsize; /* URB buffer size */
|
||||
|
||||
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 */
|
||||
dma_addr_t cr_dma; /* Control request struct dma */
|
||||
struct hid_control_fifo ctrl[HID_CONTROL_FIFO_SIZE]; /* Control fifo */
|
||||
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_report *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 */
|
||||
|
||||
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 */
|
||||
|
||||
};
|
||||
|
||||
#define hid_to_usb_dev(hid_dev) \
|
||||
container_of(hid_dev->dev->parent, struct usb_device, dev)
|
||||
|
||||
#endif
|
||||
|
||||
367
drivers/hid/usbhid/usbkbd.c
Normal file
367
drivers/hid/usbhid/usbkbd.c
Normal file
|
|
@ -0,0 +1,367 @@
|
|||
/*
|
||||
* $Id: usbkbd.c,v 1.27 2001/12/27 10:37:41 vojtech Exp $
|
||||
*
|
||||
* Copyright (c) 1999-2001 Vojtech Pavlik
|
||||
*
|
||||
* USB HIDBP Keyboard support
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* Should you need to contact me, the author, you can do so either by
|
||||
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
|
||||
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb/input.h>
|
||||
#include <linux/hid.h>
|
||||
|
||||
/*
|
||||
* Version Information
|
||||
*/
|
||||
#define DRIVER_VERSION ""
|
||||
#define DRIVER_AUTHOR "Vojtech Pavlik <[email protected]>"
|
||||
#define DRIVER_DESC "USB HID Boot Protocol keyboard driver"
|
||||
#define DRIVER_LICENSE "GPL"
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE(DRIVER_LICENSE);
|
||||
|
||||
static unsigned char usb_kbd_keycode[256] = {
|
||||
0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
|
||||
50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
|
||||
4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
|
||||
27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
|
||||
65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
|
||||
105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
|
||||
72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
|
||||
191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
|
||||
115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,
|
||||
122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
|
||||
150,158,159,128,136,177,178,176,142,152,173,140
|
||||
};
|
||||
|
||||
struct usb_kbd {
|
||||
struct input_dev *dev;
|
||||
struct usb_device *usbdev;
|
||||
unsigned char old[8];
|
||||
struct urb *irq, *led;
|
||||
unsigned char newleds;
|
||||
char name[128];
|
||||
char phys[64];
|
||||
|
||||
unsigned char *new;
|
||||
struct usb_ctrlrequest *cr;
|
||||
unsigned char *leds;
|
||||
dma_addr_t cr_dma;
|
||||
dma_addr_t new_dma;
|
||||
dma_addr_t leds_dma;
|
||||
};
|
||||
|
||||
static void usb_kbd_irq(struct urb *urb)
|
||||
{
|
||||
struct usb_kbd *kbd = urb->context;
|
||||
int i;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0: /* success */
|
||||
break;
|
||||
case -ECONNRESET: /* unlink */
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
return;
|
||||
/* -EPIPE: should clear the halt */
|
||||
default: /* error */
|
||||
goto resubmit;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);
|
||||
|
||||
for (i = 2; i < 8; i++) {
|
||||
|
||||
if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {
|
||||
if (usb_kbd_keycode[kbd->old[i]])
|
||||
input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0);
|
||||
else
|
||||
info("Unknown key (scancode %#x) released.", kbd->old[i]);
|
||||
}
|
||||
|
||||
if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {
|
||||
if (usb_kbd_keycode[kbd->new[i]])
|
||||
input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);
|
||||
else
|
||||
info("Unknown key (scancode %#x) pressed.", kbd->new[i]);
|
||||
}
|
||||
}
|
||||
|
||||
input_sync(kbd->dev);
|
||||
|
||||
memcpy(kbd->old, kbd->new, 8);
|
||||
|
||||
resubmit:
|
||||
i = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
if (i)
|
||||
err ("can't resubmit intr, %s-%s/input0, status %d",
|
||||
kbd->usbdev->bus->bus_name,
|
||||
kbd->usbdev->devpath, i);
|
||||
}
|
||||
|
||||
static int usb_kbd_event(struct input_dev *dev, unsigned int type,
|
||||
unsigned int code, int value)
|
||||
{
|
||||
struct usb_kbd *kbd = dev->private;
|
||||
|
||||
if (type != EV_LED)
|
||||
return -1;
|
||||
|
||||
|
||||
kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |
|
||||
(!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) |
|
||||
(!!test_bit(LED_NUML, dev->led));
|
||||
|
||||
if (kbd->led->status == -EINPROGRESS)
|
||||
return 0;
|
||||
|
||||
if (*(kbd->leds) == kbd->newleds)
|
||||
return 0;
|
||||
|
||||
*(kbd->leds) = kbd->newleds;
|
||||
kbd->led->dev = kbd->usbdev;
|
||||
if (usb_submit_urb(kbd->led, GFP_ATOMIC))
|
||||
err("usb_submit_urb(leds) failed");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_kbd_led(struct urb *urb)
|
||||
{
|
||||
struct usb_kbd *kbd = urb->context;
|
||||
|
||||
if (urb->status)
|
||||
warn("led urb status %d received", urb->status);
|
||||
|
||||
if (*(kbd->leds) == kbd->newleds)
|
||||
return;
|
||||
|
||||
*(kbd->leds) = kbd->newleds;
|
||||
kbd->led->dev = kbd->usbdev;
|
||||
if (usb_submit_urb(kbd->led, GFP_ATOMIC))
|
||||
err("usb_submit_urb(leds) failed");
|
||||
}
|
||||
|
||||
static int usb_kbd_open(struct input_dev *dev)
|
||||
{
|
||||
struct usb_kbd *kbd = dev->private;
|
||||
|
||||
kbd->irq->dev = kbd->usbdev;
|
||||
if (usb_submit_urb(kbd->irq, GFP_KERNEL))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_kbd_close(struct input_dev *dev)
|
||||
{
|
||||
struct usb_kbd *kbd = dev->private;
|
||||
|
||||
usb_kill_urb(kbd->irq);
|
||||
}
|
||||
|
||||
static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)
|
||||
{
|
||||
if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL)))
|
||||
return -1;
|
||||
if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL)))
|
||||
return -1;
|
||||
if (!(kbd->new = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &kbd->new_dma)))
|
||||
return -1;
|
||||
if (!(kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), GFP_ATOMIC, &kbd->cr_dma)))
|
||||
return -1;
|
||||
if (!(kbd->leds = usb_buffer_alloc(dev, 1, GFP_ATOMIC, &kbd->leds_dma)))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd)
|
||||
{
|
||||
usb_free_urb(kbd->irq);
|
||||
usb_free_urb(kbd->led);
|
||||
if (kbd->new)
|
||||
usb_buffer_free(dev, 8, kbd->new, kbd->new_dma);
|
||||
if (kbd->cr)
|
||||
usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma);
|
||||
if (kbd->leds)
|
||||
usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma);
|
||||
}
|
||||
|
||||
static int usb_kbd_probe(struct usb_interface *iface,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *dev = interface_to_usbdev(iface);
|
||||
struct usb_host_interface *interface;
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct usb_kbd *kbd;
|
||||
struct input_dev *input_dev;
|
||||
int i, pipe, maxp;
|
||||
int error = -ENOMEM;
|
||||
|
||||
interface = iface->cur_altsetting;
|
||||
|
||||
if (interface->desc.bNumEndpoints != 1)
|
||||
return -ENODEV;
|
||||
|
||||
endpoint = &interface->endpoint[0].desc;
|
||||
if (!usb_endpoint_is_int_in(endpoint))
|
||||
return -ENODEV;
|
||||
|
||||
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
|
||||
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
|
||||
|
||||
kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!kbd || !input_dev)
|
||||
goto fail1;
|
||||
|
||||
if (usb_kbd_alloc_mem(dev, kbd))
|
||||
goto fail2;
|
||||
|
||||
kbd->usbdev = dev;
|
||||
kbd->dev = input_dev;
|
||||
|
||||
if (dev->manufacturer)
|
||||
strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));
|
||||
|
||||
if (dev->product) {
|
||||
if (dev->manufacturer)
|
||||
strlcat(kbd->name, " ", sizeof(kbd->name));
|
||||
strlcat(kbd->name, dev->product, sizeof(kbd->name));
|
||||
}
|
||||
|
||||
if (!strlen(kbd->name))
|
||||
snprintf(kbd->name, sizeof(kbd->name),
|
||||
"USB HIDBP Keyboard %04x:%04x",
|
||||
le16_to_cpu(dev->descriptor.idVendor),
|
||||
le16_to_cpu(dev->descriptor.idProduct));
|
||||
|
||||
usb_make_path(dev, kbd->phys, sizeof(kbd->phys));
|
||||
strlcpy(kbd->phys, "/input0", sizeof(kbd->phys));
|
||||
|
||||
input_dev->name = kbd->name;
|
||||
input_dev->phys = kbd->phys;
|
||||
usb_to_input_id(dev, &input_dev->id);
|
||||
input_dev->cdev.dev = &iface->dev;
|
||||
input_dev->private = kbd;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);
|
||||
input_dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA);
|
||||
|
||||
for (i = 0; i < 255; i++)
|
||||
set_bit(usb_kbd_keycode[i], input_dev->keybit);
|
||||
clear_bit(0, input_dev->keybit);
|
||||
|
||||
input_dev->event = usb_kbd_event;
|
||||
input_dev->open = usb_kbd_open;
|
||||
input_dev->close = usb_kbd_close;
|
||||
|
||||
usb_fill_int_urb(kbd->irq, dev, pipe,
|
||||
kbd->new, (maxp > 8 ? 8 : maxp),
|
||||
usb_kbd_irq, kbd, endpoint->bInterval);
|
||||
kbd->irq->transfer_dma = kbd->new_dma;
|
||||
kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
|
||||
kbd->cr->bRequest = 0x09;
|
||||
kbd->cr->wValue = cpu_to_le16(0x200);
|
||||
kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
|
||||
kbd->cr->wLength = cpu_to_le16(1);
|
||||
|
||||
usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),
|
||||
(void *) kbd->cr, kbd->leds, 1,
|
||||
usb_kbd_led, kbd);
|
||||
kbd->led->setup_dma = kbd->cr_dma;
|
||||
kbd->led->transfer_dma = kbd->leds_dma;
|
||||
kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
|
||||
|
||||
error = input_register_device(kbd->dev);
|
||||
if (error)
|
||||
goto fail2;
|
||||
|
||||
usb_set_intfdata(iface, kbd);
|
||||
return 0;
|
||||
|
||||
fail2:
|
||||
usb_kbd_free_mem(dev, kbd);
|
||||
fail1:
|
||||
input_free_device(input_dev);
|
||||
kfree(kbd);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void usb_kbd_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_kbd *kbd = usb_get_intfdata (intf);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
if (kbd) {
|
||||
usb_kill_urb(kbd->irq);
|
||||
input_unregister_device(kbd->dev);
|
||||
usb_kbd_free_mem(interface_to_usbdev(intf), kbd);
|
||||
kfree(kbd);
|
||||
}
|
||||
}
|
||||
|
||||
static struct usb_device_id usb_kbd_id_table [] = {
|
||||
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
|
||||
USB_INTERFACE_PROTOCOL_KEYBOARD) },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);
|
||||
|
||||
static struct usb_driver usb_kbd_driver = {
|
||||
.name = "usbkbd",
|
||||
.probe = usb_kbd_probe,
|
||||
.disconnect = usb_kbd_disconnect,
|
||||
.id_table = usb_kbd_id_table,
|
||||
};
|
||||
|
||||
static int __init usb_kbd_init(void)
|
||||
{
|
||||
int result = usb_register(&usb_kbd_driver);
|
||||
if (result == 0)
|
||||
info(DRIVER_VERSION ":" DRIVER_DESC);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void __exit usb_kbd_exit(void)
|
||||
{
|
||||
usb_deregister(&usb_kbd_driver);
|
||||
}
|
||||
|
||||
module_init(usb_kbd_init);
|
||||
module_exit(usb_kbd_exit);
|
||||
252
drivers/hid/usbhid/usbmouse.c
Normal file
252
drivers/hid/usbhid/usbmouse.c
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
* $Id: usbmouse.c,v 1.15 2001/12/27 10:37:41 vojtech Exp $
|
||||
*
|
||||
* Copyright (c) 1999-2001 Vojtech Pavlik
|
||||
*
|
||||
* USB HIDBP Mouse support
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* Should you need to contact me, the author, you can do so either by
|
||||
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
|
||||
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb/input.h>
|
||||
#include <linux/hid.h>
|
||||
|
||||
/*
|
||||
* Version Information
|
||||
*/
|
||||
#define DRIVER_VERSION "v1.6"
|
||||
#define DRIVER_AUTHOR "Vojtech Pavlik <[email protected]>"
|
||||
#define DRIVER_DESC "USB HID Boot Protocol mouse driver"
|
||||
#define DRIVER_LICENSE "GPL"
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE(DRIVER_LICENSE);
|
||||
|
||||
struct usb_mouse {
|
||||
char name[128];
|
||||
char phys[64];
|
||||
struct usb_device *usbdev;
|
||||
struct input_dev *dev;
|
||||
struct urb *irq;
|
||||
|
||||
signed char *data;
|
||||
dma_addr_t data_dma;
|
||||
};
|
||||
|
||||
static void usb_mouse_irq(struct urb *urb)
|
||||
{
|
||||
struct usb_mouse *mouse = urb->context;
|
||||
signed char *data = mouse->data;
|
||||
struct input_dev *dev = mouse->dev;
|
||||
int status;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0: /* success */
|
||||
break;
|
||||
case -ECONNRESET: /* unlink */
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
return;
|
||||
/* -EPIPE: should clear the halt */
|
||||
default: /* error */
|
||||
goto resubmit;
|
||||
}
|
||||
|
||||
input_report_key(dev, BTN_LEFT, data[0] & 0x01);
|
||||
input_report_key(dev, BTN_RIGHT, data[0] & 0x02);
|
||||
input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);
|
||||
input_report_key(dev, BTN_SIDE, data[0] & 0x08);
|
||||
input_report_key(dev, BTN_EXTRA, data[0] & 0x10);
|
||||
|
||||
input_report_rel(dev, REL_X, data[1]);
|
||||
input_report_rel(dev, REL_Y, data[2]);
|
||||
input_report_rel(dev, REL_WHEEL, data[3]);
|
||||
|
||||
input_sync(dev);
|
||||
resubmit:
|
||||
status = usb_submit_urb (urb, GFP_ATOMIC);
|
||||
if (status)
|
||||
err ("can't resubmit intr, %s-%s/input0, status %d",
|
||||
mouse->usbdev->bus->bus_name,
|
||||
mouse->usbdev->devpath, status);
|
||||
}
|
||||
|
||||
static int usb_mouse_open(struct input_dev *dev)
|
||||
{
|
||||
struct usb_mouse *mouse = dev->private;
|
||||
|
||||
mouse->irq->dev = mouse->usbdev;
|
||||
if (usb_submit_urb(mouse->irq, GFP_KERNEL))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usb_mouse_close(struct input_dev *dev)
|
||||
{
|
||||
struct usb_mouse *mouse = dev->private;
|
||||
|
||||
usb_kill_urb(mouse->irq);
|
||||
}
|
||||
|
||||
static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
struct usb_host_interface *interface;
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct usb_mouse *mouse;
|
||||
struct input_dev *input_dev;
|
||||
int pipe, maxp;
|
||||
int error = -ENOMEM;
|
||||
|
||||
interface = intf->cur_altsetting;
|
||||
|
||||
if (interface->desc.bNumEndpoints != 1)
|
||||
return -ENODEV;
|
||||
|
||||
endpoint = &interface->endpoint[0].desc;
|
||||
if (!usb_endpoint_is_int_in(endpoint))
|
||||
return -ENODEV;
|
||||
|
||||
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
|
||||
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
|
||||
|
||||
mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!mouse || !input_dev)
|
||||
goto fail1;
|
||||
|
||||
mouse->data = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &mouse->data_dma);
|
||||
if (!mouse->data)
|
||||
goto fail1;
|
||||
|
||||
mouse->irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!mouse->irq)
|
||||
goto fail2;
|
||||
|
||||
mouse->usbdev = dev;
|
||||
mouse->dev = input_dev;
|
||||
|
||||
if (dev->manufacturer)
|
||||
strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));
|
||||
|
||||
if (dev->product) {
|
||||
if (dev->manufacturer)
|
||||
strlcat(mouse->name, " ", sizeof(mouse->name));
|
||||
strlcat(mouse->name, dev->product, sizeof(mouse->name));
|
||||
}
|
||||
|
||||
if (!strlen(mouse->name))
|
||||
snprintf(mouse->name, sizeof(mouse->name),
|
||||
"USB HIDBP Mouse %04x:%04x",
|
||||
le16_to_cpu(dev->descriptor.idVendor),
|
||||
le16_to_cpu(dev->descriptor.idProduct));
|
||||
|
||||
usb_make_path(dev, mouse->phys, sizeof(mouse->phys));
|
||||
strlcat(mouse->phys, "/input0", sizeof(mouse->phys));
|
||||
|
||||
input_dev->name = mouse->name;
|
||||
input_dev->phys = mouse->phys;
|
||||
usb_to_input_id(dev, &input_dev->id);
|
||||
input_dev->cdev.dev = &intf->dev;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
|
||||
input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
|
||||
input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
|
||||
input_dev->keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA);
|
||||
input_dev->relbit[0] |= BIT(REL_WHEEL);
|
||||
|
||||
input_dev->private = mouse;
|
||||
input_dev->open = usb_mouse_open;
|
||||
input_dev->close = usb_mouse_close;
|
||||
|
||||
usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,
|
||||
(maxp > 8 ? 8 : maxp),
|
||||
usb_mouse_irq, mouse, endpoint->bInterval);
|
||||
mouse->irq->transfer_dma = mouse->data_dma;
|
||||
mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
error = input_register_device(mouse->dev);
|
||||
if (error)
|
||||
goto fail3;
|
||||
|
||||
usb_set_intfdata(intf, mouse);
|
||||
return 0;
|
||||
|
||||
fail3:
|
||||
usb_free_urb(mouse->irq);
|
||||
fail2:
|
||||
usb_buffer_free(dev, 8, mouse->data, mouse->data_dma);
|
||||
fail1:
|
||||
input_free_device(input_dev);
|
||||
kfree(mouse);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void usb_mouse_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct usb_mouse *mouse = usb_get_intfdata (intf);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
if (mouse) {
|
||||
usb_kill_urb(mouse->irq);
|
||||
input_unregister_device(mouse->dev);
|
||||
usb_free_urb(mouse->irq);
|
||||
usb_buffer_free(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma);
|
||||
kfree(mouse);
|
||||
}
|
||||
}
|
||||
|
||||
static struct usb_device_id usb_mouse_id_table [] = {
|
||||
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
|
||||
USB_INTERFACE_PROTOCOL_MOUSE) },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);
|
||||
|
||||
static struct usb_driver usb_mouse_driver = {
|
||||
.name = "usbmouse",
|
||||
.probe = usb_mouse_probe,
|
||||
.disconnect = usb_mouse_disconnect,
|
||||
.id_table = usb_mouse_id_table,
|
||||
};
|
||||
|
||||
static int __init usb_mouse_init(void)
|
||||
{
|
||||
int retval = usb_register(&usb_mouse_driver);
|
||||
if (retval == 0)
|
||||
info(DRIVER_VERSION ":" DRIVER_DESC);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void __exit usb_mouse_exit(void)
|
||||
{
|
||||
usb_deregister(&usb_mouse_driver);
|
||||
}
|
||||
|
||||
module_init(usb_mouse_init);
|
||||
module_exit(usb_mouse_exit);
|
||||
Loading…
Reference in a new issue