Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (21 commits) HID: hidraw_connect() memleak fix HID: add hidraw interface USB HID: provide hook for hidraw write() HID: hiddev: Add 32bit ioctl compatibilty HID: Add GeneralTouch touchscreen to the blacklist HID: add support for Microsoft Wireless Laser Keyboard 6000 Input: add KEY_LOGOFF USBHID: report descriptor fix for MacBook JIS keyboard HID: trivial fixes in hid-debug HID: fix input mapping for Microsoft Ergonomic Keyboard HID: use hid-plff driver for GreenAsia 0e8f:0003 devices USBHID: Add HID_QUIRK_NOGET for ELO Touch Screen 2700 display HID: enable hiddev for the SantaRosa MacBookPro IR receiver USBHID: add CM109 device to blacklist HID: Report usage codes of keys as EV_MSC scancode events HID: ignore all non-LED usages in output fields in hid-input HID: fix whitespace damage HID: add support for Thrustmaster FGT Force Feedback wheel HID: minimal autosuspend support for USB HID devices HID: add support for Microsoft Natural Ergonomic Keyboard 4000 ...
This commit is contained in:
Коммит
52d4e661ac
|
@ -46,6 +46,25 @@ config HID_DEBUG
|
|||
|
||||
If unsure, say N
|
||||
|
||||
config HIDRAW
|
||||
bool "/dev/hidraw raw HID device support"
|
||||
depends on 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/hidraw.
|
||||
|
||||
There is also a /dev/hiddev configuration option in the USB HID
|
||||
configuration menu. In comparison to hiddev, this device does not process
|
||||
the hid events at all (no parsing, no lookups). This lets applications
|
||||
to work on raw hid events when they want to, and avoid using transport-specific
|
||||
userspace libhid/libusb libraries.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
source "drivers/hid/usbhid/Kconfig"
|
||||
|
||||
endif # HID_SUPPORT
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
hid-objs := hid-core.o hid-input.o
|
||||
|
||||
obj-$(CONFIG_HID) += hid.o
|
||||
|
||||
hid-$(CONFIG_HID_DEBUG) += hid-debug.o
|
||||
hid-$(CONFIG_HIDRAW) += hidraw.o
|
||||
|
||||
obj-$(CONFIG_USB_HID) += usbhid/
|
||||
obj-$(CONFIG_USB_MOUSE) += usbhid/
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <linux/hid.h>
|
||||
#include <linux/hiddev.h>
|
||||
#include <linux/hid-debug.h>
|
||||
#include <linux/hidraw.h>
|
||||
|
||||
/*
|
||||
* Version Information
|
||||
|
@ -979,6 +980,8 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i
|
|||
|
||||
if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event)
|
||||
hid->hiddev_report_event(hid, report);
|
||||
if (hid->claimed & HID_CLAIMED_HIDRAW)
|
||||
hidraw_report_event(hid, data, size);
|
||||
|
||||
for (n = 0; n < report->maxfield; n++)
|
||||
hid_input_field(hid, report->field[n], data, interrupt);
|
||||
|
@ -990,5 +993,18 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(hid_input_report);
|
||||
|
||||
static int __init hid_init(void)
|
||||
{
|
||||
return hidraw_init();
|
||||
}
|
||||
|
||||
static void __exit hid_exit(void)
|
||||
{
|
||||
hidraw_exit();
|
||||
}
|
||||
|
||||
module_init(hid_init);
|
||||
module_exit(hid_exit);
|
||||
|
||||
MODULE_LICENSE(DRIVER_LICENSE);
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
struct hid_usage_entry {
|
||||
unsigned page;
|
||||
unsigned usage;
|
||||
char *description;
|
||||
const char *description;
|
||||
};
|
||||
|
||||
static const struct hid_usage_entry hid_usage_table[] = {
|
||||
|
@ -365,8 +365,8 @@ void hid_resolv_usage(unsigned usage) {
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(hid_resolv_usage);
|
||||
|
||||
__inline__ static void tab(int n) {
|
||||
while (n--) printk(" ");
|
||||
static void tab(int n) {
|
||||
printk(KERN_DEBUG "%*s", n, "");
|
||||
}
|
||||
|
||||
void hid_dump_field(struct hid_field *field, int n) {
|
||||
|
@ -401,8 +401,8 @@ void hid_dump_field(struct hid_field *field, int n) {
|
|||
tab(n); printk("Unit Exponent(%d)\n", field->unit_exponent);
|
||||
}
|
||||
if (field->unit) {
|
||||
char *systems[5] = { "None", "SI Linear", "SI Rotation", "English Linear", "English Rotation" };
|
||||
char *units[5][8] = {
|
||||
static const char *systems[5] = { "None", "SI Linear", "SI Rotation", "English Linear", "English Rotation" };
|
||||
static const char *units[5][8] = {
|
||||
{ "None", "None", "None", "None", "None", "None", "None", "None" },
|
||||
{ "None", "Centimeter", "Gram", "Seconds", "Kelvin", "Ampere", "Candela", "None" },
|
||||
{ "None", "Radians", "Gram", "Seconds", "Kelvin", "Ampere", "Candela", "None" },
|
||||
|
@ -457,7 +457,7 @@ void hid_dump_field(struct hid_field *field, int n) {
|
|||
printk("%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute ");
|
||||
printk("%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : "");
|
||||
printk("%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : "");
|
||||
printk("%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPrefferedState " : "");
|
||||
printk("%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPreferredState " : "");
|
||||
printk("%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : "");
|
||||
printk("%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : "");
|
||||
printk("%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : "");
|
||||
|
@ -470,7 +470,7 @@ void hid_dump_device(struct hid_device *device) {
|
|||
struct hid_report *report;
|
||||
struct list_head *list;
|
||||
unsigned i,k;
|
||||
static char *table[] = {"INPUT", "OUTPUT", "FEATURE"};
|
||||
static const char *table[] = {"INPUT", "OUTPUT", "FEATURE"};
|
||||
|
||||
if (!hid_debug)
|
||||
return;
|
||||
|
@ -501,13 +501,13 @@ void hid_dump_input(struct hid_usage *usage, __s32 value) {
|
|||
if (!hid_debug)
|
||||
return;
|
||||
|
||||
printk("hid-debug: input ");
|
||||
printk(KERN_DEBUG "hid-debug: input ");
|
||||
hid_resolv_usage(usage->hid);
|
||||
printk(" = %d\n", value);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hid_dump_input);
|
||||
|
||||
static char *events[EV_MAX + 1] = {
|
||||
static const char *events[EV_MAX + 1] = {
|
||||
[EV_SYN] = "Sync", [EV_KEY] = "Key",
|
||||
[EV_REL] = "Relative", [EV_ABS] = "Absolute",
|
||||
[EV_MSC] = "Misc", [EV_LED] = "LED",
|
||||
|
@ -516,10 +516,10 @@ static char *events[EV_MAX + 1] = {
|
|||
[EV_FF_STATUS] = "ForceFeedbackStatus",
|
||||
};
|
||||
|
||||
static char *syncs[2] = {
|
||||
static const char *syncs[2] = {
|
||||
[SYN_REPORT] = "Report", [SYN_CONFIG] = "Config",
|
||||
};
|
||||
static char *keys[KEY_MAX + 1] = {
|
||||
static const char *keys[KEY_MAX + 1] = {
|
||||
[KEY_RESERVED] = "Reserved", [KEY_ESC] = "Esc",
|
||||
[KEY_1] = "1", [KEY_2] = "2",
|
||||
[KEY_3] = "3", [KEY_4] = "4",
|
||||
|
@ -697,7 +697,8 @@ static char *keys[KEY_MAX + 1] = {
|
|||
[KEY_DEL_LINE] = "DeleteLine",
|
||||
[KEY_SEND] = "Send", [KEY_REPLY] = "Reply",
|
||||
[KEY_FORWARDMAIL] = "ForwardMail", [KEY_SAVE] = "Save",
|
||||
[KEY_DOCUMENTS] = "Documents",
|
||||
[KEY_DOCUMENTS] = "Documents", [KEY_SPELLCHECK] = "SpellCheck",
|
||||
[KEY_LOGOFF] = "Logoff",
|
||||
[KEY_FN] = "Fn", [KEY_FN_ESC] = "Fn+ESC",
|
||||
[KEY_FN_1] = "Fn+1", [KEY_FN_2] = "Fn+2",
|
||||
[KEY_FN_B] = "Fn+B", [KEY_FN_D] = "Fn+D",
|
||||
|
@ -715,7 +716,7 @@ static char *keys[KEY_MAX + 1] = {
|
|||
[KEY_SWITCHVIDEOMODE] = "SwitchVideoMode",
|
||||
};
|
||||
|
||||
static char *relatives[REL_MAX + 1] = {
|
||||
static const char *relatives[REL_MAX + 1] = {
|
||||
[REL_X] = "X", [REL_Y] = "Y",
|
||||
[REL_Z] = "Z", [REL_RX] = "Rx",
|
||||
[REL_RY] = "Ry", [REL_RZ] = "Rz",
|
||||
|
@ -723,7 +724,7 @@ static char *relatives[REL_MAX + 1] = {
|
|||
[REL_WHEEL] = "Wheel", [REL_MISC] = "Misc",
|
||||
};
|
||||
|
||||
static char *absolutes[ABS_MAX + 1] = {
|
||||
static const char *absolutes[ABS_MAX + 1] = {
|
||||
[ABS_X] = "X", [ABS_Y] = "Y",
|
||||
[ABS_Z] = "Z", [ABS_RX] = "Rx",
|
||||
[ABS_RY] = "Ry", [ABS_RZ] = "Rz",
|
||||
|
@ -739,12 +740,12 @@ static char *absolutes[ABS_MAX + 1] = {
|
|||
[ABS_VOLUME] = "Volume", [ABS_MISC] = "Misc",
|
||||
};
|
||||
|
||||
static char *misc[MSC_MAX + 1] = {
|
||||
static const char *misc[MSC_MAX + 1] = {
|
||||
[MSC_SERIAL] = "Serial", [MSC_PULSELED] = "Pulseled",
|
||||
[MSC_GESTURE] = "Gesture", [MSC_RAW] = "RawData"
|
||||
};
|
||||
|
||||
static char *leds[LED_MAX + 1] = {
|
||||
static const char *leds[LED_MAX + 1] = {
|
||||
[LED_NUML] = "NumLock", [LED_CAPSL] = "CapsLock",
|
||||
[LED_SCROLLL] = "ScrollLock", [LED_COMPOSE] = "Compose",
|
||||
[LED_KANA] = "Kana", [LED_SLEEP] = "Sleep",
|
||||
|
@ -752,16 +753,16 @@ static char *leds[LED_MAX + 1] = {
|
|||
[LED_MISC] = "Misc",
|
||||
};
|
||||
|
||||
static char *repeats[REP_MAX + 1] = {
|
||||
static const char *repeats[REP_MAX + 1] = {
|
||||
[REP_DELAY] = "Delay", [REP_PERIOD] = "Period"
|
||||
};
|
||||
|
||||
static char *sounds[SND_MAX + 1] = {
|
||||
static const char *sounds[SND_MAX + 1] = {
|
||||
[SND_CLICK] = "Click", [SND_BELL] = "Bell",
|
||||
[SND_TONE] = "Tone"
|
||||
};
|
||||
|
||||
static char **names[EV_MAX + 1] = {
|
||||
static const char **names[EV_MAX + 1] = {
|
||||
[EV_SYN] = syncs, [EV_KEY] = keys,
|
||||
[EV_REL] = relatives, [EV_ABS] = absolutes,
|
||||
[EV_MSC] = misc, [EV_LED] = leds,
|
||||
|
@ -777,4 +778,3 @@ void hid_resolv_event(__u8 type, __u16 code) {
|
|||
names[type] ? (names[type][code] ? names[type][code] : "?") : "?");
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hid_resolv_event);
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ static const unsigned char hid_keyboard[256] = {
|
|||
115,114,unk,unk,unk,121,unk, 89, 93,124, 92, 94, 95,unk,unk,unk,
|
||||
122,123, 90, 91, 85,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
|
||||
unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
|
||||
unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
|
||||
unk,unk,unk,unk,unk,unk,179,180,unk,unk,unk,unk,unk,unk,unk,unk,
|
||||
unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
|
||||
unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,
|
||||
29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
|
||||
|
@ -86,6 +86,10 @@ static const struct {
|
|||
#define map_abs_clear(c) do { map_abs(c); clear_bit(c, bit); } while (0)
|
||||
#define map_key_clear(c) do { map_key(c); clear_bit(c, bit); } while (0)
|
||||
|
||||
/* hardware needing special handling due to colliding MSVENDOR page usages */
|
||||
#define IS_CHICONY_TACTICAL_PAD(x) (x->vendor == 0x04f2 && device->product == 0x0418)
|
||||
#define IS_MS_KB(x) (x->vendor == 0x045e && (x->product == 0x00db || x->product == 0x00f9))
|
||||
|
||||
#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
|
||||
|
||||
struct hidinput_key_translation {
|
||||
|
@ -295,7 +299,7 @@ static int hidinput_getkeycode(struct input_dev *dev, int scancode,
|
|||
{
|
||||
struct hid_device *hid = dev->private;
|
||||
struct hid_usage *usage;
|
||||
|
||||
|
||||
usage = hidinput_find_key(hid, scancode, 0);
|
||||
if (usage) {
|
||||
*keycode = usage->code;
|
||||
|
@ -310,15 +314,15 @@ static int hidinput_setkeycode(struct input_dev *dev, int scancode,
|
|||
struct hid_device *hid = dev->private;
|
||||
struct hid_usage *usage;
|
||||
int old_keycode;
|
||||
|
||||
|
||||
if (keycode < 0 || keycode > KEY_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
usage = hidinput_find_key(hid, scancode, 0);
|
||||
if (usage) {
|
||||
old_keycode = usage->code;
|
||||
usage->code = keycode;
|
||||
|
||||
|
||||
clear_bit(old_keycode, dev->keybit);
|
||||
set_bit(usage->code, dev->keybit);
|
||||
dbg_hid(KERN_DEBUG "Assigned keycode %d to HID usage code %x\n", keycode, scancode);
|
||||
|
@ -326,10 +330,10 @@ static int hidinput_setkeycode(struct input_dev *dev, int scancode,
|
|||
* by another key */
|
||||
if (hidinput_find_key (hid, 0, old_keycode))
|
||||
set_bit(old_keycode, dev->keybit);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -351,6 +355,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
|||
if (field->flags & HID_MAIN_ITEM_CONSTANT)
|
||||
goto ignore;
|
||||
|
||||
/* only LED usages are supported in output fields */
|
||||
if (field->report_type == HID_OUTPUT_REPORT &&
|
||||
(usage->hid & HID_USAGE_PAGE) != HID_UP_LED) {
|
||||
dbg_hid_line(" [non-LED output field] ");
|
||||
goto ignore;
|
||||
}
|
||||
|
||||
switch (usage->hid & HID_USAGE_PAGE) {
|
||||
|
||||
case HID_UP_UNDEFINED:
|
||||
|
@ -595,6 +606,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
|||
case 0x0f6: map_key_clear(KEY_NEXT); break;
|
||||
case 0x0fa: map_key_clear(KEY_BACK); break;
|
||||
|
||||
case 0x182: map_key_clear(KEY_BOOKMARKS); break;
|
||||
case 0x183: map_key_clear(KEY_CONFIG); break;
|
||||
case 0x184: map_key_clear(KEY_WORDPROCESSOR); break;
|
||||
case 0x185: map_key_clear(KEY_EDITOR); break;
|
||||
|
@ -611,9 +623,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
|||
case 0x192: map_key_clear(KEY_CALC); break;
|
||||
case 0x194: map_key_clear(KEY_FILE); break;
|
||||
case 0x196: map_key_clear(KEY_WWW); break;
|
||||
case 0x19c: map_key_clear(KEY_LOGOFF); break;
|
||||
case 0x19e: map_key_clear(KEY_COFFEE); break;
|
||||
case 0x1a6: map_key_clear(KEY_HELP); break;
|
||||
case 0x1a7: map_key_clear(KEY_DOCUMENTS); break;
|
||||
case 0x1ab: map_key_clear(KEY_SPELLCHECK); break;
|
||||
case 0x1b6: map_key_clear(KEY_MEDIA); break;
|
||||
case 0x1b7: map_key_clear(KEY_SOUND); break;
|
||||
case 0x1bc: map_key_clear(KEY_MESSENGER); break;
|
||||
case 0x1bd: map_key_clear(KEY_INFO); break;
|
||||
case 0x201: map_key_clear(KEY_NEW); break;
|
||||
|
@ -720,8 +736,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
|||
|
||||
case HID_UP_MSVENDOR:
|
||||
|
||||
/* special case - Chicony Chicony KU-0418 tactical pad */
|
||||
if (device->vendor == 0x04f2 && device->product == 0x0418) {
|
||||
/* Unfortunately, there are multiple devices which
|
||||
* emit usages from MSVENDOR page that require different
|
||||
* handling. If this list grows too much in the future,
|
||||
* more general handling will have to be introduced here
|
||||
* (i.e. another blacklist).
|
||||
*/
|
||||
|
||||
/* Chicony Chicony KU-0418 tactical pad */
|
||||
if (IS_CHICONY_TACTICAL_PAD(device)) {
|
||||
set_bit(EV_REP, input->evbit);
|
||||
switch(usage->hid & HID_USAGE) {
|
||||
case 0xff01: map_key_clear(BTN_1); break;
|
||||
|
@ -737,6 +760,26 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
|||
case 0xff0b: map_key_clear(BTN_B); break;
|
||||
default: goto ignore;
|
||||
}
|
||||
|
||||
/* Microsoft Natural Ergonomic Keyboard 4000 */
|
||||
} else if (IS_MS_KB(device)) {
|
||||
switch(usage->hid & HID_USAGE) {
|
||||
case 0xfd06:
|
||||
map_key_clear(KEY_CHAT);
|
||||
break;
|
||||
case 0xfd07:
|
||||
map_key_clear(KEY_PHONE);
|
||||
break;
|
||||
case 0xff05:
|
||||
set_bit(EV_REP, input->evbit);
|
||||
map_key_clear(KEY_F13);
|
||||
set_bit(KEY_F14, input->keybit);
|
||||
set_bit(KEY_F15, input->keybit);
|
||||
set_bit(KEY_F16, input->keybit);
|
||||
set_bit(KEY_F17, input->keybit);
|
||||
set_bit(KEY_F18, input->keybit);
|
||||
default: goto ignore;
|
||||
}
|
||||
} else {
|
||||
goto ignore;
|
||||
}
|
||||
|
@ -888,6 +931,11 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
|||
set_bit(KEY_VOLUMEDOWN, input->keybit);
|
||||
}
|
||||
|
||||
if (usage->type == EV_KEY) {
|
||||
set_bit(EV_MSC, input->evbit);
|
||||
set_bit(MSC_SCAN, input->mscbit);
|
||||
}
|
||||
|
||||
hid_resolv_event(usage->type, usage->code);
|
||||
|
||||
dbg_hid_line("\n");
|
||||
|
@ -991,6 +1039,29 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
|
|||
return;
|
||||
}
|
||||
|
||||
/* Handling MS keyboards special buttons */
|
||||
if (IS_MS_KB(hid) && usage->hid == (HID_UP_MSVENDOR | 0xff05)) {
|
||||
int key = 0;
|
||||
static int last_key = 0;
|
||||
switch (value) {
|
||||
case 0x01: key = KEY_F14; break;
|
||||
case 0x02: key = KEY_F15; break;
|
||||
case 0x04: key = KEY_F16; break;
|
||||
case 0x08: key = KEY_F17; break;
|
||||
case 0x10: key = KEY_F18; break;
|
||||
default: break;
|
||||
}
|
||||
if (key) {
|
||||
input_event(input, usage->type, key, 1);
|
||||
last_key = key;
|
||||
} else {
|
||||
input_event(input, usage->type, last_key, 0);
|
||||
}
|
||||
}
|
||||
/* report the usage code as scancode if the key status has changed */
|
||||
if (usage->type == EV_KEY && !!test_bit(usage->code, input->key) != value)
|
||||
input_event(input, EV_MSC, MSC_SCAN, usage->hid);
|
||||
|
||||
input_event(input, usage->type, usage->code, value);
|
||||
|
||||
if ((field->flags & HID_MAIN_ITEM_RELATIVE) && (usage->type == EV_KEY))
|
||||
|
@ -1051,6 +1122,9 @@ int hidinput_connect(struct hid_device *hid)
|
|||
int i, j, k;
|
||||
int max_report_type = HID_OUTPUT_REPORT;
|
||||
|
||||
if (hid->quirks & HID_QUIRK_IGNORE_HIDINPUT)
|
||||
return -1;
|
||||
|
||||
INIT_LIST_HEAD(&hid->inputs);
|
||||
|
||||
for (i = 0; i < hid->maxcollection; i++)
|
||||
|
|
|
@ -0,0 +1,401 @@
|
|||
/*
|
||||
* HID raw devices, giving access to raw HID events.
|
||||
*
|
||||
* In comparison to hiddev, this device does not process the
|
||||
* hid events at all (no parsing, no lookups). This lets applications
|
||||
* to work on raw hid events as they want to, and avoids a need to
|
||||
* use a transport-specific userspace libhid/libusb libraries.
|
||||
*
|
||||
* Copyright (c) 2007 Jiri Kosina
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include <linux/hidraw.h>
|
||||
|
||||
static int hidraw_major;
|
||||
static struct cdev hidraw_cdev;
|
||||
static struct class *hidraw_class;
|
||||
static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES];
|
||||
static DEFINE_SPINLOCK(minors_lock);
|
||||
|
||||
static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hidraw_list *list = file->private_data;
|
||||
int ret = 0, len;
|
||||
char *report;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
|
||||
while (ret == 0) {
|
||||
|
||||
mutex_lock(&list->read_mutex);
|
||||
|
||||
if (list->head == list->tail) {
|
||||
add_wait_queue(&list->hidraw->wait, &wait);
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
while (list->head == list->tail) {
|
||||
if (file->f_flags & O_NONBLOCK) {
|
||||
ret = -EAGAIN;
|
||||
break;
|
||||
}
|
||||
if (signal_pending(current)) {
|
||||
ret = -ERESTARTSYS;
|
||||
break;
|
||||
}
|
||||
if (!list->hidraw->exist) {
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
|
||||
/* allow O_NONBLOCK to work well from other threads */
|
||||
mutex_unlock(&list->read_mutex);
|
||||
schedule();
|
||||
mutex_lock(&list->read_mutex);
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
}
|
||||
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&list->hidraw->wait, &wait);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
report = list->buffer[list->tail].value;
|
||||
len = list->buffer[list->tail].len > count ?
|
||||
count : list->buffer[list->tail].len;
|
||||
|
||||
if (copy_to_user(buffer, list->buffer[list->tail].value, len)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
ret += len;
|
||||
|
||||
kfree(list->buffer[list->tail].value);
|
||||
list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1);
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&list->read_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* the first byte is expected to be a report number */
|
||||
static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
|
||||
{
|
||||
unsigned int minor = iminor(file->f_path.dentry->d_inode);
|
||||
struct hid_device *dev = hidraw_table[minor]->hid;
|
||||
__u8 *buf;
|
||||
int ret = 0;
|
||||
|
||||
if (!dev->hid_output_raw_report)
|
||||
return -ENODEV;
|
||||
|
||||
if (count > HID_MIN_BUFFER_SIZE) {
|
||||
printk(KERN_WARNING "hidraw: pid %d passed too large report\n",
|
||||
current->pid);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (count < 2) {
|
||||
printk(KERN_WARNING "hidraw: pid %d passed too short report\n",
|
||||
current->pid);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
buf = kmalloc(count * sizeof(__u8), GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(buf, buffer, count)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = dev->hid_output_raw_report(dev, buf, count);
|
||||
out:
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int hidraw_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct hidraw_list *list = file->private_data;
|
||||
|
||||
poll_wait(file, &list->hidraw->wait, wait);
|
||||
if (list->head != list->tail)
|
||||
return POLLIN | POLLRDNORM;
|
||||
if (!list->hidraw->exist)
|
||||
return POLLERR | POLLHUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hidraw_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
unsigned int minor = iminor(inode);
|
||||
struct hidraw *dev;
|
||||
struct hidraw_list *list;
|
||||
int err = 0;
|
||||
|
||||
if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
spin_lock(&minors_lock);
|
||||
if (!hidraw_table[minor]) {
|
||||
printk(KERN_EMERG "hidraw device with minor %d doesn't exist\n",
|
||||
minor);
|
||||
kfree(list);
|
||||
err = -ENODEV;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
list->hidraw = hidraw_table[minor];
|
||||
mutex_init(&list->read_mutex);
|
||||
list_add_tail(&list->node, &hidraw_table[minor]->list);
|
||||
file->private_data = list;
|
||||
|
||||
dev = hidraw_table[minor];
|
||||
if (!dev->open++)
|
||||
dev->hid->hid_open(dev->hid);
|
||||
|
||||
out_unlock:
|
||||
spin_unlock(&minors_lock);
|
||||
out:
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
static int hidraw_release(struct inode * inode, struct file * file)
|
||||
{
|
||||
unsigned int minor = iminor(inode);
|
||||
struct hidraw *dev;
|
||||
struct hidraw_list *list = file->private_data;
|
||||
|
||||
if (!hidraw_table[minor]) {
|
||||
printk(KERN_EMERG "hidraw device with minor %d doesn't exist\n",
|
||||
minor);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
list_del(&list->node);
|
||||
dev = hidraw_table[minor];
|
||||
if (!dev->open--) {
|
||||
if (list->hidraw->exist)
|
||||
dev->hid->hid_close(dev->hid);
|
||||
else
|
||||
kfree(list->hidraw);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hidraw_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
unsigned int minor = iminor(inode);
|
||||
struct hidraw *dev = hidraw_table[minor];
|
||||
void __user *user_arg = (void __user*) arg;
|
||||
|
||||
switch (cmd) {
|
||||
case HIDIOCGRDESCSIZE:
|
||||
if (put_user(dev->hid->rsize, (int __user *)arg))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
case HIDIOCGRDESC:
|
||||
{
|
||||
__u32 len;
|
||||
|
||||
if (get_user(len, (int __user *)arg))
|
||||
return -EFAULT;
|
||||
if (copy_to_user(*((__u8 **)(user_arg +
|
||||
sizeof(__u32))),
|
||||
dev->hid->rdesc, len))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
case HIDIOCGRAWINFO:
|
||||
{
|
||||
struct hidraw_devinfo dinfo;
|
||||
|
||||
dinfo.bustype = dev->hid->bus;
|
||||
dinfo.vendor = dev->hid->vendor;
|
||||
dinfo.product = dev->hid->product;
|
||||
if (copy_to_user(user_arg, &dinfo, sizeof(dinfo)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
printk(KERN_EMERG "hidraw: unsupported ioctl() %x\n",
|
||||
cmd);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct file_operations hidraw_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = hidraw_read,
|
||||
.write = hidraw_write,
|
||||
.poll = hidraw_poll,
|
||||
.open = hidraw_open,
|
||||
.release = hidraw_release,
|
||||
.ioctl = hidraw_ioctl,
|
||||
};
|
||||
|
||||
void hidraw_report_event(struct hid_device *hid, u8 *data, int len)
|
||||
{
|
||||
struct hidraw *dev = hid->hidraw;
|
||||
struct hidraw_list *list;
|
||||
|
||||
list_for_each_entry(list, &dev->list, node) {
|
||||
list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC);
|
||||
list->buffer[list->head].len = len;
|
||||
list->head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1);
|
||||
kill_fasync(&list->fasync, SIGIO, POLL_IN);
|
||||
}
|
||||
|
||||
wake_up_interruptible(&dev->wait);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hidraw_report_event);
|
||||
|
||||
int hidraw_connect(struct hid_device *hid)
|
||||
{
|
||||
int minor, result;
|
||||
struct hidraw *dev;
|
||||
|
||||
/* TODO currently we accept any HID device. This should later
|
||||
* probably be fixed to accept only those devices which provide
|
||||
* non-input applications
|
||||
*/
|
||||
|
||||
dev = kzalloc(sizeof(struct hidraw), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
result = -EINVAL;
|
||||
|
||||
spin_lock(&minors_lock);
|
||||
|
||||
for (minor = 0; minor < HIDRAW_MAX_DEVICES; minor++) {
|
||||
if (hidraw_table[minor])
|
||||
continue;
|
||||
hidraw_table[minor] = dev;
|
||||
result = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock(&minors_lock);
|
||||
|
||||
if (result) {
|
||||
kfree(dev);
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev->dev = device_create(hidraw_class, NULL, MKDEV(hidraw_major, minor),
|
||||
"%s%d", "hidraw", minor);
|
||||
|
||||
if (IS_ERR(dev->dev)) {
|
||||
spin_lock(&minors_lock);
|
||||
hidraw_table[minor] = NULL;
|
||||
spin_unlock(&minors_lock);
|
||||
result = PTR_ERR(dev->dev);
|
||||
kfree(dev);
|
||||
goto out;
|
||||
}
|
||||
|
||||
init_waitqueue_head(&dev->wait);
|
||||
INIT_LIST_HEAD(&dev->list);
|
||||
|
||||
dev->hid = hid;
|
||||
dev->minor = minor;
|
||||
|
||||
dev->exist = 1;
|
||||
hid->hidraw = dev;
|
||||
|
||||
out:
|
||||
return result;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hidraw_connect);
|
||||
|
||||
void hidraw_disconnect(struct hid_device *hid)
|
||||
{
|
||||
struct hidraw *hidraw = hid->hidraw;
|
||||
|
||||
hidraw->exist = 0;
|
||||
|
||||
spin_lock(&minors_lock);
|
||||
hidraw_table[hidraw->minor] = NULL;
|
||||
spin_unlock(&minors_lock);
|
||||
|
||||
device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
|
||||
|
||||
if (hidraw->open) {
|
||||
hid->hid_close(hid);
|
||||
wake_up_interruptible(&hidraw->wait);
|
||||
} else {
|
||||
kfree(hidraw);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hidraw_disconnect);
|
||||
|
||||
int __init hidraw_init(void)
|
||||
{
|
||||
int result;
|
||||
dev_t dev_id;
|
||||
|
||||
result = alloc_chrdev_region(&dev_id, HIDRAW_FIRST_MINOR,
|
||||
HIDRAW_MAX_DEVICES, "hidraw");
|
||||
|
||||
hidraw_major = MAJOR(dev_id);
|
||||
|
||||
if (result < 0) {
|
||||
printk(KERN_WARNING "hidraw: can't get major number\n");
|
||||
result = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
hidraw_class = class_create(THIS_MODULE, "hidraw");
|
||||
if (IS_ERR(hidraw_class)) {
|
||||
result = PTR_ERR(hidraw_class);
|
||||
unregister_chrdev(hidraw_major, "hidraw");
|
||||
goto out;
|
||||
}
|
||||
|
||||
cdev_init(&hidraw_cdev, &hidraw_ops);
|
||||
cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES);
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
void __exit hidraw_exit(void)
|
||||
{
|
||||
dev_t dev_id = MKDEV(hidraw_major, 0);
|
||||
|
||||
cdev_del(&hidraw_cdev);
|
||||
class_destroy(hidraw_class);
|
||||
unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);
|
||||
|
||||
}
|
|
@ -71,19 +71,20 @@ config LOGITECH_FF
|
|||
force feedback.
|
||||
|
||||
config PANTHERLORD_FF
|
||||
bool "PantherLord USB/PS2 2in1 Adapter support"
|
||||
bool "PantherLord/GreenAsia based device 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.
|
||||
Say Y here if you have a PantherLord/GreenAsia based game controller
|
||||
or adapter and want to enable force feedback support for it.
|
||||
|
||||
config THRUSTMASTER_FF
|
||||
bool "ThrustMaster FireStorm Dual Power 2 support (EXPERIMENTAL)"
|
||||
bool "ThrustMaster devices 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,
|
||||
Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or
|
||||
a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel,
|
||||
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.
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <linux/hid.h>
|
||||
#include <linux/hiddev.h>
|
||||
#include <linux/hid-debug.h>
|
||||
#include <linux/hidraw.h>
|
||||
#include "usbhid.h"
|
||||
|
||||
/*
|
||||
|
@ -512,7 +513,16 @@ static int hid_get_class_descriptor(struct usb_device *dev, int ifnum,
|
|||
|
||||
int usbhid_open(struct hid_device *hid)
|
||||
{
|
||||
++hid->open;
|
||||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
int res;
|
||||
|
||||
if (!hid->open++) {
|
||||
res = usb_autopm_get_interface(usbhid->intf);
|
||||
if (res < 0) {
|
||||
hid->open--;
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
if (hid_start_in(hid))
|
||||
hid_io_error(hid);
|
||||
return 0;
|
||||
|
@ -522,8 +532,10 @@ void usbhid_close(struct hid_device *hid)
|
|||
{
|
||||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
|
||||
if (!--hid->open)
|
||||
if (!--hid->open) {
|
||||
usb_kill_urb(usbhid->urbin);
|
||||
usb_autopm_put_interface(usbhid->intf);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -628,6 +640,28 @@ static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count)
|
||||
{
|
||||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
struct usb_device *dev = hid_to_usb_dev(hid);
|
||||
struct usb_interface *intf = usbhid->intf;
|
||||
struct usb_host_interface *interface = intf->cur_altsetting;
|
||||
int ret;
|
||||
|
||||
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
||||
HID_REQ_SET_REPORT,
|
||||
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
|
||||
cpu_to_le16(((HID_OUTPUT_REPORT + 1) << 8) | *buf),
|
||||
interface->desc.bInterfaceNumber, buf + 1, count - 1,
|
||||
USB_CTRL_SET_TIMEOUT);
|
||||
|
||||
/* count also the report id */
|
||||
if (ret > 0)
|
||||
ret++;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid)
|
||||
{
|
||||
struct usbhid_device *usbhid = hid->driver_data;
|
||||
|
@ -871,6 +905,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
|
|||
hid->hiddev_hid_event = hiddev_hid_event;
|
||||
hid->hiddev_report_event = hiddev_report_event;
|
||||
#endif
|
||||
hid->hid_output_raw_report = usbhid_output_raw_report;
|
||||
return hid;
|
||||
|
||||
fail:
|
||||
|
@ -909,6 +944,8 @@ static void hid_disconnect(struct usb_interface *intf)
|
|||
hidinput_disconnect(hid);
|
||||
if (hid->claimed & HID_CLAIMED_HIDDEV)
|
||||
hiddev_disconnect(hid);
|
||||
if (hid->claimed & HID_CLAIMED_HIDRAW)
|
||||
hidraw_disconnect(hid);
|
||||
|
||||
usb_free_urb(usbhid->urbin);
|
||||
usb_free_urb(usbhid->urbctrl);
|
||||
|
@ -941,11 +978,13 @@ static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
|||
hid->claimed |= HID_CLAIMED_INPUT;
|
||||
if (!hiddev_connect(hid))
|
||||
hid->claimed |= HID_CLAIMED_HIDDEV;
|
||||
if (!hidraw_connect(hid))
|
||||
hid->claimed |= HID_CLAIMED_HIDRAW;
|
||||
|
||||
usb_set_intfdata(intf, hid);
|
||||
|
||||
if (!hid->claimed) {
|
||||
printk ("HID device not claimed by input or hiddev\n");
|
||||
printk ("HID device claimed by neither input, hiddev nor hidraw\n");
|
||||
hid_disconnect(intf);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -961,10 +1000,16 @@ static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
|||
|
||||
if (hid->claimed & HID_CLAIMED_INPUT)
|
||||
printk("input");
|
||||
if (hid->claimed == (HID_CLAIMED_INPUT | HID_CLAIMED_HIDDEV))
|
||||
if ((hid->claimed & HID_CLAIMED_INPUT) && ((hid->claimed & HID_CLAIMED_HIDDEV) ||
|
||||
hid->claimed & HID_CLAIMED_HIDRAW))
|
||||
printk(",");
|
||||
if (hid->claimed & HID_CLAIMED_HIDDEV)
|
||||
printk("hiddev%d", hid->minor);
|
||||
if ((hid->claimed & HID_CLAIMED_INPUT) && (hid->claimed & HID_CLAIMED_HIDDEV) &&
|
||||
(hid->claimed & HID_CLAIMED_HIDRAW))
|
||||
printk(",");
|
||||
if (hid->claimed & HID_CLAIMED_HIDRAW)
|
||||
printk("hidraw%d", ((struct hidraw*)hid->hidraw)->minor);
|
||||
|
||||
c = "Device";
|
||||
for (i = 0; i < hid->maxcollection; i++) {
|
||||
|
@ -1048,6 +1093,7 @@ static struct usb_driver hid_driver = {
|
|||
.pre_reset = hid_pre_reset,
|
||||
.post_reset = hid_post_reset,
|
||||
.id_table = hid_usb_ids,
|
||||
.supports_autosuspend = 1,
|
||||
};
|
||||
|
||||
static int __init hid_init(void)
|
||||
|
|
|
@ -62,11 +62,14 @@ static struct hid_ff_initializer inits[] = {
|
|||
{ 0x46d, 0xca03, hid_lgff_init }, /* Logitech MOMO force wheel */
|
||||
#endif
|
||||
#ifdef CONFIG_PANTHERLORD_FF
|
||||
{ 0x810, 0x0001, hid_plff_init },
|
||||
{ 0x810, 0x0001, hid_plff_init }, /* "Twin USB Joystick" */
|
||||
{ 0xe8f, 0x0003, hid_plff_init }, /* "GreenAsia Inc. USB Joystick " */
|
||||
#endif
|
||||
#ifdef CONFIG_THRUSTMASTER_FF
|
||||
{ 0x44f, 0xb300, hid_tmff_init },
|
||||
{ 0x44f, 0xb304, hid_tmff_init },
|
||||
{ 0x44f, 0xb651, hid_tmff_init }, /* FGT Rumble Force Wheel */
|
||||
{ 0x44f, 0xb654, hid_tmff_init }, /* FGT Force Feedback Wheel */
|
||||
#endif
|
||||
#ifdef CONFIG_ZEROPLUS_FF
|
||||
{ 0xc12, 0x0005, hid_zpff_init },
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
/*
|
||||
* Force feedback support for PantherLord USB/PS2 2in1 Adapter devices
|
||||
* Force feedback support for PantherLord/GreenAsia based devices
|
||||
*
|
||||
* The devices are distributed under various names and the same USB device ID
|
||||
* can be used in both adapters and actual game controllers.
|
||||
*
|
||||
* 0810:0001 "Twin USB Joystick"
|
||||
* - tested with PantherLord USB/PS2 2in1 Adapter
|
||||
* - contains two reports, one for each port (HID_QUIRK_MULTI_INPUT)
|
||||
*
|
||||
* 0e8f:0003 "GreenAsia Inc. USB Joystick "
|
||||
* - tested with Köng Gaming gamepad
|
||||
*
|
||||
* Copyright (c) 2007 Anssi Hannula <anssi.hannula@gmail.com>
|
||||
*/
|
||||
|
@ -67,11 +77,11 @@ int hid_plff_init(struct hid_device *hid)
|
|||
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 device contains one output report per physical device, all
|
||||
containing 1 field, which contains 4 ff00.0002 usages and 4 16bit
|
||||
absolute values.
|
||||
|
||||
The 2 input reports also contain a field which contains
|
||||
The input reports also contain a field which contains
|
||||
8 ff00.0001 usages and 8 boolean values. Their meaning is
|
||||
currently unknown. */
|
||||
|
||||
|
@ -122,8 +132,8 @@ int hid_plff_init(struct hid_device *hid)
|
|||
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 <anssi.hannula@gmail.com>\n");
|
||||
printk(KERN_INFO "hid-plff: Force feedback for PantherLord/GreenAsia "
|
||||
"devices by Anssi Hannula <anssi.hannula@gmail.com>\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -61,6 +61,7 @@
|
|||
#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_IRCONTROL4 0x8242
|
||||
|
||||
#define USB_VENDOR_ID_ASUS 0x0b05
|
||||
#define USB_DEVICE_ID_ASUS_LCM 0x1726
|
||||
|
@ -86,6 +87,9 @@
|
|||
|
||||
#define USB_VENDOR_ID_CIDC 0x1677
|
||||
|
||||
#define USB_VENDOR_ID_CMEDIA 0x0d8c
|
||||
#define USB_DEVICE_ID_CM109 0x000e
|
||||
|
||||
#define USB_VENDOR_ID_CODEMERCS 0x07c0
|
||||
#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500
|
||||
#define USB_DEVICE_ID_CODEMERCS_IOW_LAST 0x15ff
|
||||
|
@ -104,12 +108,17 @@
|
|||
#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100
|
||||
#define USB_DEVICE_ID_DELORME_EM_LT20 0x0200
|
||||
|
||||
#define USB_VENDOR_ID_ELO 0x04E7
|
||||
#define USB_DEVICE_ID_ELO_TS2700 0x0020
|
||||
|
||||
#define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f
|
||||
#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
|
||||
|
||||
#define USB_VENDOR_ID_GAMERON 0x0810
|
||||
#define USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR 0x0001
|
||||
|
||||
#define USB_VENDOR_ID_GENERAL_TOUCH 0x0dfc
|
||||
|
||||
#define USB_VENDOR_ID_GLAB 0x06c2
|
||||
#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038
|
||||
#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039
|
||||
|
@ -373,6 +382,7 @@ static const struct hid_blacklist {
|
|||
{ 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_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4, HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT },
|
||||
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV, HID_QUIRK_HIDINPUT },
|
||||
|
||||
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01, HID_QUIRK_IGNORE },
|
||||
|
@ -387,11 +397,16 @@ static const struct hid_blacklist {
|
|||
{ USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_LCM, 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_CMEDIA, USB_DEVICE_ID_CM109, 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_GENERAL_TOUCH, 0x0001, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GENERAL_TOUCH, 0x0002, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GENERAL_TOUCH, 0x0003, HID_QUIRK_IGNORE },
|
||||
{ USB_VENDOR_ID_GENERAL_TOUCH, 0x0004, 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 },
|
||||
|
@ -507,6 +522,7 @@ static const struct hid_blacklist {
|
|||
{ 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_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_NOGET },
|
||||
{ USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
|
||||
|
@ -614,6 +630,8 @@ static const struct hid_rdesc_blacklist {
|
|||
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER, HID_QUIRK_RDESC_LOGITECH },
|
||||
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2, HID_QUIRK_RDESC_LOGITECH },
|
||||
|
||||
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS, HID_QUIRK_RDESC_MACBOOK_JIS },
|
||||
|
||||
{ USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, HID_QUIRK_RDESC_PETALYNX },
|
||||
|
||||
{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1, HID_QUIRK_RDESC_SWAPPED_MIN_MAX },
|
||||
|
@ -927,6 +945,18 @@ static void usbhid_fixup_cypress_descriptor(unsigned char *rdesc, int rsize)
|
|||
printk(KERN_INFO "Fixing up Cypress report descriptor\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* MacBook JIS keyboard has wrong logical maximum
|
||||
*/
|
||||
static void usbhid_fixup_macbook_descriptor(unsigned char *rdesc, int rsize)
|
||||
{
|
||||
if (rsize >= 60 && rdesc[53] == 0x65
|
||||
&& rdesc[59] == 0x65) {
|
||||
printk(KERN_INFO "Fixing up MacBook JIS keyboard report descriptor\n");
|
||||
rdesc[53] = rdesc[59] = 0xe7;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned rsize)
|
||||
{
|
||||
|
@ -941,6 +971,9 @@ static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned
|
|||
|
||||
if (quirks & HID_QUIRK_RDESC_PETALYNX)
|
||||
usbhid_fixup_petalynx_descriptor(rdesc, rsize);
|
||||
|
||||
if (quirks & HID_QUIRK_RDESC_MACBOOK_JIS)
|
||||
usbhid_fixup_macbook_descriptor(rdesc, rsize);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -36,16 +36,39 @@
|
|||
#include "usbhid.h"
|
||||
|
||||
/* Usages for thrustmaster devices I know about */
|
||||
#define THRUSTMASTER_USAGE_RUMBLE_LR (HID_UP_GENDESK | 0xbb)
|
||||
#define THRUSTMASTER_USAGE_FF (HID_UP_GENDESK | 0xbb)
|
||||
|
||||
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[] = {
|
||||
{ 0x44f, 0xb300, ff_rumble },
|
||||
{ 0x44f, 0xb304, ff_rumble },
|
||||
{ 0x44f, 0xb651, ff_rumble }, /* FGT Rumble Force Wheel */
|
||||
{ 0x44f, 0xb654, ff_joystick }, /* FGT Force Feedback Wheel */
|
||||
};
|
||||
|
||||
struct tmff_device {
|
||||
struct hid_report *report;
|
||||
struct hid_field *rumble;
|
||||
struct hid_field *ff_field;
|
||||
};
|
||||
|
||||
/* 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)
|
||||
static inline int hid_tmff_scale_u16(unsigned int in,
|
||||
int minimum, int maximum)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -57,22 +80,57 @@ static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Changes values from -0x80 to 0x7f into values from minimum to maximum */
|
||||
static inline int hid_tmff_scale_s8(int in,
|
||||
int minimum, int maximum)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = (((in + 0x80) * (maximum - minimum)) / 0xff) + 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 = input_get_drvdata(dev);
|
||||
struct tmff_device *tmff = data;
|
||||
struct hid_field *ff_field = tmff->ff_field;
|
||||
int x, y;
|
||||
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);
|
||||
switch (effect->type) {
|
||||
case FF_CONSTANT:
|
||||
x = hid_tmff_scale_s8(effect->u.ramp.start_level,
|
||||
ff_field->logical_minimum,
|
||||
ff_field->logical_maximum);
|
||||
y = hid_tmff_scale_s8(effect->u.ramp.end_level,
|
||||
ff_field->logical_minimum,
|
||||
ff_field->logical_maximum);
|
||||
|
||||
tmff->rumble->value[0] = left;
|
||||
tmff->rumble->value[1] = right;
|
||||
dbg_hid("(left,right)=(%08x, %08x)\n", left, right);
|
||||
usbhid_submit_report(hid, tmff->report, USB_DIR_OUT);
|
||||
dbg_hid("(x, y)=(%04x, %04x)\n", x, y);
|
||||
ff_field->value[0] = x;
|
||||
ff_field->value[1] = y;
|
||||
usbhid_submit_report(hid, tmff->report, USB_DIR_OUT);
|
||||
break;
|
||||
|
||||
case FF_RUMBLE:
|
||||
left = hid_tmff_scale_u16(effect->u.rumble.weak_magnitude,
|
||||
ff_field->logical_minimum,
|
||||
ff_field->logical_maximum);
|
||||
right = hid_tmff_scale_u16(effect->u.rumble.strong_magnitude,
|
||||
ff_field->logical_minimum,
|
||||
ff_field->logical_maximum);
|
||||
|
||||
dbg_hid("(left,right)=(%08x, %08x)\n", left, right);
|
||||
ff_field->value[0] = left;
|
||||
ff_field->value[1] = right;
|
||||
usbhid_submit_report(hid, tmff->report, USB_DIR_OUT);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -82,14 +140,16 @@ int hid_tmff_init(struct hid_device *hid)
|
|||
struct list_head *pos;
|
||||
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||
struct input_dev *input_dev = hidinput->input;
|
||||
const signed short *ff_bits = ff_joystick;
|
||||
int error;
|
||||
int i;
|
||||
|
||||
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) {
|
||||
list_for_each(pos, &hid->report_enum[HID_OUTPUT_REPORT].report_list) {
|
||||
struct hid_report *report = (struct hid_report *)pos;
|
||||
int fieldnum;
|
||||
|
||||
|
@ -100,48 +160,65 @@ int hid_tmff_init(struct hid_device *hid)
|
|||
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);
|
||||
case THRUSTMASTER_USAGE_FF:
|
||||
if (field->report_count < 2) {
|
||||
warn("ignoring FF field with report_count < 2");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (field->logical_maximum == field->logical_minimum) {
|
||||
warn("ignoring FF field with logical_maximum == logical_minimum");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tmff->report && tmff->report != report) {
|
||||
warn("ignoring FF field in other report");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tmff->ff_field && tmff->ff_field != field) {
|
||||
warn("ignoring duplicate FF field");
|
||||
continue;
|
||||
}
|
||||
|
||||
tmff->report = report;
|
||||
tmff->ff_field = field;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(devices); i++) {
|
||||
if (input_dev->id.vendor == devices[i].idVendor &&
|
||||
input_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], 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;
|
||||
if (!tmff->report) {
|
||||
err("cant find FF field in output reports\n");
|
||||
error = -ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
info("Force feedback for ThrustMaster rumble pad devices by Zinx Verituse <zinx@epicsol.org>");
|
||||
error = input_ff_create_memless(input_dev, tmff, hid_tmff_play);
|
||||
if (error)
|
||||
goto fail;
|
||||
|
||||
info("Force feedback for ThrustMaster devices by Zinx Verituse <zinx@epicsol.org>");
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
kfree(tmff);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <linux/usb.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/hiddev.h>
|
||||
#include <linux/compat.h>
|
||||
#include "usbhid.h"
|
||||
|
||||
#ifdef CONFIG_USB_DYNAMIC_MINORS
|
||||
|
@ -738,6 +739,14 @@ inval:
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static long hiddev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file->f_path.dentry->d_inode;
|
||||
return hiddev_ioctl(inode, file, cmd, compat_ptr(arg));
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct file_operations hiddev_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = hiddev_read,
|
||||
|
@ -747,6 +756,9 @@ static const struct file_operations hiddev_fops = {
|
|||
.release = hiddev_release,
|
||||
.ioctl = hiddev_ioctl,
|
||||
.fasync = hiddev_fasync,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = hiddev_compat_ioctl,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct usb_class_driver hiddev_class = {
|
||||
|
|
|
@ -276,6 +276,7 @@ struct hid_item {
|
|||
#define HID_QUIRK_HIDINPUT 0x00200000
|
||||
#define HID_QUIRK_LOGITECH_IGNORE_DOUBLED_WHEEL 0x00400000
|
||||
#define HID_QUIRK_LOGITECH_EXPANDED_KEYMAP 0x00800000
|
||||
#define HID_QUIRK_IGNORE_HIDINPUT 0x01000000
|
||||
|
||||
/*
|
||||
* Separate quirks for runtime report descriptor fixup
|
||||
|
@ -285,6 +286,7 @@ struct hid_item {
|
|||
#define HID_QUIRK_RDESC_LOGITECH 0x00000002
|
||||
#define HID_QUIRK_RDESC_SWAPPED_MIN_MAX 0x00000004
|
||||
#define HID_QUIRK_RDESC_PETALYNX 0x00000008
|
||||
#define HID_QUIRK_RDESC_MACBOOK_JIS 0x00000010
|
||||
|
||||
/*
|
||||
* This is the global environment of the parser. This information is
|
||||
|
@ -403,6 +405,7 @@ struct hid_control_fifo {
|
|||
|
||||
#define HID_CLAIMED_INPUT 1
|
||||
#define HID_CLAIMED_HIDDEV 2
|
||||
#define HID_CLAIMED_HIDRAW 4
|
||||
|
||||
#define HID_CTRL_RUNNING 1
|
||||
#define HID_OUT_RUNNING 2
|
||||
|
@ -438,6 +441,7 @@ struct hid_device { /* device report descriptor */
|
|||
|
||||
struct list_head inputs; /* The list of inputs */
|
||||
void *hiddev; /* The hiddev structure */
|
||||
void *hidraw;
|
||||
int minor; /* Hiddev minor number */
|
||||
|
||||
wait_queue_head_t wait; /* For sleeping */
|
||||
|
@ -458,6 +462,9 @@ struct hid_device { /* device report descriptor */
|
|||
void (*hiddev_hid_event) (struct hid_device *, struct hid_field *field,
|
||||
struct hid_usage *, __s32);
|
||||
void (*hiddev_report_event) (struct hid_device *, struct hid_report *);
|
||||
|
||||
/* handler for raw output data, used by hidraw */
|
||||
int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t);
|
||||
#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
|
||||
unsigned long pb_pressed_fn[NBITS(KEY_MAX)];
|
||||
unsigned long pb_pressed_numlock[NBITS(KEY_MAX)];
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
#ifndef _HIDRAW_H
|
||||
#define _HIDRAW_H
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Jiri Kosina
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
struct hidraw_report_descriptor {
|
||||
__u32 size;
|
||||
__u8 *value;
|
||||
};
|
||||
|
||||
struct hidraw_devinfo {
|
||||
__u32 bustype;
|
||||
__s16 vendor;
|
||||
__s16 product;
|
||||
};
|
||||
|
||||
/* ioctl interface */
|
||||
#define HIDIOCGRDESCSIZE _IOR('H', 0x01, int)
|
||||
#define HIDIOCGRDESC _IOR('H', 0x02, struct hidraw_report_descriptor)
|
||||
#define HIDIOCGRAWINFO _IOR('H', 0x03, struct hidraw_devinfo)
|
||||
|
||||
#define HIDRAW_FIRST_MINOR 0
|
||||
#define HIDRAW_MAX_DEVICES 64
|
||||
/* number of reports to buffer */
|
||||
#define HIDRAW_BUFFER_SIZE 64
|
||||
|
||||
|
||||
/* kernel-only API declarations */
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/hid.h>
|
||||
|
||||
struct hidraw {
|
||||
unsigned int minor;
|
||||
int exist;
|
||||
int open;
|
||||
wait_queue_head_t wait;
|
||||
struct hid_device *hid;
|
||||
struct device *dev;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct hidraw_report {
|
||||
__u8 *value;
|
||||
int len;
|
||||
};
|
||||
|
||||
struct hidraw_list {
|
||||
struct hidraw_report buffer[HIDRAW_BUFFER_SIZE];
|
||||
int head;
|
||||
int tail;
|
||||
struct fasync_struct *fasync;
|
||||
struct hidraw *hidraw;
|
||||
struct list_head node;
|
||||
struct mutex read_mutex;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_HIDRAW
|
||||
int hidraw_init(void);
|
||||
void hidraw_exit(void);
|
||||
void hidraw_report_event(struct hid_device *, u8 *, int);
|
||||
int hidraw_connect(struct hid_device *);
|
||||
void hidraw_disconnect(struct hid_device *);
|
||||
#else
|
||||
static inline int hidraw_init(void) { return 0; }
|
||||
static inline void hidraw_exit(void) { }
|
||||
static inline void hidraw_report_event(struct hid_device *hid, u8 *data, int len) { }
|
||||
static inline int hidraw_connect(struct hid_device *hid) { return -1; }
|
||||
static inline void hidraw_disconnect(struct hid_device *hid) { }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -523,6 +523,8 @@ struct input_absinfo {
|
|||
#define KEY_ADDRESSBOOK 0x1ad /* AL Contacts/Address Book */
|
||||
#define KEY_MESSENGER 0x1ae /* AL Instant Messaging */
|
||||
#define KEY_DISPLAYTOGGLE 0x1af /* Turn display (LCD) on and off */
|
||||
#define KEY_SPELLCHECK 0x1b0 /* AL Spell Check */
|
||||
#define KEY_LOGOFF 0x1b1 /* AL Logoff */
|
||||
|
||||
#define KEY_DEL_EOL 0x1c0
|
||||
#define KEY_DEL_EOS 0x1c1
|
||||
|
|
Загрузка…
Ссылка в новой задаче