Commit 0d07a15b authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid:
  HID: Suppress hidinput for Samsung IR control
  HID: remove 60x GTCO devices from blacklist
  HID: export headers properly
  HID: WiseGroup 866 Dual Joypad needs output reports quirk
  HID: ThrustMaster FF driver is no longer experimental
  HID: Logitech diNovo Mini pad support
  HID: fix race between open() and disconnect() in usbhid
  HID: make hid_input_field and usbhid_modify_dquirk static
  HID: pass numbered reports properly to hidraw
  HID: fix misplaced rdesc quirk
  HID: force feedback driver for Logitech Rumblepad 2
  HID: move wait from hid to usbhid
  HID: make function from dbg_hid
  HID: fix sparse warnings
  HID: only dump report traffic with debug level 2
  HID: patch to add NOGET for DMI/Acomdata
  HID: Sunplus Wireless Desktop needs report descriptor fixup
  HID: quirk for MS Wireless Desktop Receiver (model 1028)
  HID: fixup fullspeed interval on highspeed Afatech DVB-T IR kbd
  HID: fix build failure in hiddev_ioctl with gcc 3.2
parents 62429f43 efa0f16b
...@@ -44,8 +44,8 @@ ...@@ -44,8 +44,8 @@
#ifdef CONFIG_HID_DEBUG #ifdef CONFIG_HID_DEBUG
int hid_debug = 0; int hid_debug = 0;
module_param_named(debug, hid_debug, bool, 0600); module_param_named(debug, hid_debug, int, 0600);
MODULE_PARM_DESC(debug, "Turn HID debugging mode on and off"); MODULE_PARM_DESC(debug, "HID debugging (0=off, 1=probing info, 2=continuous data dumping)");
EXPORT_SYMBOL_GPL(hid_debug); EXPORT_SYMBOL_GPL(hid_debug);
#endif #endif
...@@ -97,7 +97,7 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned ...@@ -97,7 +97,7 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned
field->index = report->maxfield++; field->index = report->maxfield++;
report->field[field->index] = field; report->field[field->index] = field;
field->usage = (struct hid_usage *)(field + 1); field->usage = (struct hid_usage *)(field + 1);
field->value = (unsigned *)(field->usage + usages); field->value = (s32 *)(field->usage + usages);
field->report = report; field->report = report;
return field; return field;
...@@ -830,7 +830,8 @@ static void hid_process_event(struct hid_device *hid, struct hid_field *field, s ...@@ -830,7 +830,8 @@ static void hid_process_event(struct hid_device *hid, struct hid_field *field, s
* reporting to the layer). * reporting to the layer).
*/ */
void hid_input_field(struct hid_device *hid, struct hid_field *field, __u8 *data, int interrupt) static void hid_input_field(struct hid_device *hid, struct hid_field *field,
__u8 *data, int interrupt)
{ {
unsigned n; unsigned n;
unsigned count = field->report_count; unsigned count = field->report_count;
...@@ -876,7 +877,6 @@ void hid_input_field(struct hid_device *hid, struct hid_field *field, __u8 *data ...@@ -876,7 +877,6 @@ void hid_input_field(struct hid_device *hid, struct hid_field *field, __u8 *data
exit: exit:
kfree(value); kfree(value);
} }
EXPORT_SYMBOL_GPL(hid_input_field);
/* /*
* Output the field into the report. * Output the field into the report.
...@@ -988,8 +988,13 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i ...@@ -988,8 +988,13 @@ 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) if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event)
hid->hiddev_report_event(hid, report); hid->hiddev_report_event(hid, report);
if (hid->claimed & HID_CLAIMED_HIDRAW) if (hid->claimed & HID_CLAIMED_HIDRAW) {
/* numbered reports need to be passed with the report num */
if (report_enum->numbered)
hidraw_report_event(hid, data - 1, size + 1);
else
hidraw_report_event(hid, data, size); hidraw_report_event(hid, data, size);
}
for (n = 0; n < report->maxfield; n++) for (n = 0; n < report->maxfield; n++)
hid_input_field(hid, report->field[n], data, interrupt); hid_input_field(hid, report->field[n], data, interrupt);
......
...@@ -498,7 +498,7 @@ void hid_dump_device(struct hid_device *device) { ...@@ -498,7 +498,7 @@ void hid_dump_device(struct hid_device *device) {
EXPORT_SYMBOL_GPL(hid_dump_device); EXPORT_SYMBOL_GPL(hid_dump_device);
void hid_dump_input(struct hid_usage *usage, __s32 value) { void hid_dump_input(struct hid_usage *usage, __s32 value) {
if (!hid_debug) if (hid_debug < 2)
return; return;
printk(KERN_DEBUG "hid-debug: input "); printk(KERN_DEBUG "hid-debug: input ");
......
...@@ -276,6 +276,21 @@ static int quirk_btc_8193(struct hid_usage *usage, struct input_dev *input, ...@@ -276,6 +276,21 @@ static int quirk_btc_8193(struct hid_usage *usage, struct input_dev *input,
return 1; return 1;
} }
static int quirk_sunplus_wdesktop(struct hid_usage *usage, struct input_dev *input,
unsigned long **bit, int *max)
{
if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
return 0;
switch (usage->hid & HID_USAGE) {
case 0x2003: map_key_clear(KEY_ZOOMIN); break;
case 0x2103: map_key_clear(KEY_ZOOMOUT); break;
default:
return 0;
}
return 1;
}
#define VENDOR_ID_BELKIN 0x1020 #define VENDOR_ID_BELKIN 0x1020
#define DEVICE_ID_BELKIN_WIRELESS_KEYBOARD 0x0006 #define DEVICE_ID_BELKIN_WIRELESS_KEYBOARD 0x0006
...@@ -306,6 +321,9 @@ static int quirk_btc_8193(struct hid_usage *usage, struct input_dev *input, ...@@ -306,6 +321,9 @@ static int quirk_btc_8193(struct hid_usage *usage, struct input_dev *input,
#define VENDOR_ID_PETALYNX 0x18b1 #define VENDOR_ID_PETALYNX 0x18b1
#define DEVICE_ID_PETALYNX_MAXTER_REMOTE 0x0037 #define DEVICE_ID_PETALYNX_MAXTER_REMOTE 0x0037
#define VENDOR_ID_SUNPLUS 0x04fc
#define DEVICE_ID_SUNPLUS_WDESKTOP 0x05d8
static const struct hid_input_blacklist { static const struct hid_input_blacklist {
__u16 idVendor; __u16 idVendor;
__u16 idProduct; __u16 idProduct;
...@@ -333,7 +351,9 @@ static const struct hid_input_blacklist { ...@@ -333,7 +351,9 @@ static const struct hid_input_blacklist {
{ VENDOR_ID_PETALYNX, DEVICE_ID_PETALYNX_MAXTER_REMOTE, quirk_petalynx_remote }, { VENDOR_ID_PETALYNX, DEVICE_ID_PETALYNX_MAXTER_REMOTE, quirk_petalynx_remote },
{ 0, 0, 0 } { VENDOR_ID_SUNPLUS, DEVICE_ID_SUNPLUS_WDESKTOP, quirk_sunplus_wdesktop },
{ 0, 0, NULL }
}; };
int hidinput_mapping_quirks(struct hid_usage *usage, int hidinput_mapping_quirks(struct hid_usage *usage,
......
...@@ -71,6 +71,14 @@ config LOGITECH_FF ...@@ -71,6 +71,14 @@ config LOGITECH_FF
Note: if you say N here, this device will still be supported, but without Note: if you say N here, this device will still be supported, but without
force feedback. force feedback.
config LOGIRUMBLEPAD2_FF
bool "Logitech Rumblepad 2 support"
depends on HID_FF
select INPUT_FF_MEMLESS if USB_HID
help
Say Y here if you want to enable force feedback support for Logitech
Rumblepad 2 devices.
config PANTHERLORD_FF config PANTHERLORD_FF
bool "PantherLord/GreenAsia based device support" bool "PantherLord/GreenAsia based device support"
depends on HID_FF depends on HID_FF
...@@ -80,8 +88,8 @@ config PANTHERLORD_FF ...@@ -80,8 +88,8 @@ config PANTHERLORD_FF
or adapter and want to enable force feedback support for it. or adapter and want to enable force feedback support for it.
config THRUSTMASTER_FF config THRUSTMASTER_FF
bool "ThrustMaster devices support (EXPERIMENTAL)" bool "ThrustMaster devices support"
depends on HID_FF && EXPERIMENTAL depends on HID_FF
select INPUT_FF_MEMLESS if USB_HID select INPUT_FF_MEMLESS if USB_HID
help help
Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or
......
...@@ -16,6 +16,9 @@ endif ...@@ -16,6 +16,9 @@ endif
ifeq ($(CONFIG_LOGITECH_FF),y) ifeq ($(CONFIG_LOGITECH_FF),y)
usbhid-objs += hid-lgff.o usbhid-objs += hid-lgff.o
endif endif
ifeq ($(CONFIG_LOGIRUMBLEPAD2_FF),y)
usbhid-objs += hid-lg2ff.o
endif
ifeq ($(CONFIG_PANTHERLORD_FF),y) ifeq ($(CONFIG_PANTHERLORD_FF),y)
usbhid-objs += hid-plff.o usbhid-objs += hid-plff.o
endif endif
......
...@@ -82,6 +82,7 @@ static int hid_start_in(struct hid_device *hid) ...@@ -82,6 +82,7 @@ static int hid_start_in(struct hid_device *hid)
spin_lock_irqsave(&usbhid->inlock, flags); spin_lock_irqsave(&usbhid->inlock, flags);
if (hid->open > 0 && !test_bit(HID_SUSPENDED, &usbhid->iofl) && if (hid->open > 0 && !test_bit(HID_SUSPENDED, &usbhid->iofl) &&
!test_bit(HID_DISCONNECTED, &usbhid->iofl) &&
!test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) { !test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) {
rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC); rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC);
if (rc != 0) if (rc != 0)
...@@ -155,7 +156,7 @@ static void hid_io_error(struct hid_device *hid) ...@@ -155,7 +156,7 @@ static void hid_io_error(struct hid_device *hid)
spin_lock_irqsave(&usbhid->inlock, flags); spin_lock_irqsave(&usbhid->inlock, flags);
/* Stop when disconnected */ /* Stop when disconnected */
if (usb_get_intfdata(usbhid->intf) == NULL) if (test_bit(HID_DISCONNECTED, &usbhid->iofl))
goto done; goto done;
/* If it has been a while since the last error, we'll assume /* If it has been a while since the last error, we'll assume
...@@ -341,7 +342,7 @@ static void hid_irq_out(struct urb *urb) ...@@ -341,7 +342,7 @@ static void hid_irq_out(struct urb *urb)
if (usbhid->outhead != usbhid->outtail) { if (usbhid->outhead != usbhid->outtail) {
if (hid_submit_out(hid)) { if (hid_submit_out(hid)) {
clear_bit(HID_OUT_RUNNING, &usbhid->iofl); clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
wake_up(&hid->wait); wake_up(&usbhid->wait);
} }
spin_unlock_irqrestore(&usbhid->outlock, flags); spin_unlock_irqrestore(&usbhid->outlock, flags);
return; return;
...@@ -349,7 +350,7 @@ static void hid_irq_out(struct urb *urb) ...@@ -349,7 +350,7 @@ static void hid_irq_out(struct urb *urb)
clear_bit(HID_OUT_RUNNING, &usbhid->iofl); clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
spin_unlock_irqrestore(&usbhid->outlock, flags); spin_unlock_irqrestore(&usbhid->outlock, flags);
wake_up(&hid->wait); wake_up(&usbhid->wait);
} }
/* /*
...@@ -391,7 +392,7 @@ static void hid_ctrl(struct urb *urb) ...@@ -391,7 +392,7 @@ static void hid_ctrl(struct urb *urb)
if (usbhid->ctrlhead != usbhid->ctrltail) { if (usbhid->ctrlhead != usbhid->ctrltail) {
if (hid_submit_ctrl(hid)) { if (hid_submit_ctrl(hid)) {
clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
wake_up(&hid->wait); wake_up(&usbhid->wait);
} }
spin_unlock_irqrestore(&usbhid->ctrllock, flags); spin_unlock_irqrestore(&usbhid->ctrllock, flags);
return; return;
...@@ -399,7 +400,7 @@ static void hid_ctrl(struct urb *urb) ...@@ -399,7 +400,7 @@ static void hid_ctrl(struct urb *urb)
clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
spin_unlock_irqrestore(&usbhid->ctrllock, flags); spin_unlock_irqrestore(&usbhid->ctrllock, flags);
wake_up(&hid->wait); wake_up(&usbhid->wait);
} }
void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir) void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir)
...@@ -478,7 +479,8 @@ int usbhid_wait_io(struct hid_device *hid) ...@@ -478,7 +479,8 @@ int usbhid_wait_io(struct hid_device *hid)
{ {
struct usbhid_device *usbhid = hid->driver_data; struct usbhid_device *usbhid = hid->driver_data;
if (!wait_event_timeout(hid->wait, (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl) && if (!wait_event_timeout(usbhid->wait,
(!test_bit(HID_CTRL_RUNNING, &usbhid->iofl) &&
!test_bit(HID_OUT_RUNNING, &usbhid->iofl)), !test_bit(HID_OUT_RUNNING, &usbhid->iofl)),
10*HZ)) { 10*HZ)) {
dbg_hid("timeout waiting for ctrl or out queue to clear\n"); dbg_hid("timeout waiting for ctrl or out queue to clear\n");
...@@ -610,10 +612,11 @@ static void usbhid_set_leds(struct hid_device *hid) ...@@ -610,10 +612,11 @@ static void usbhid_set_leds(struct hid_device *hid)
/* /*
* Traverse the supplied list of reports and find the longest * Traverse the supplied list of reports and find the longest
*/ */
static void hid_find_max_report(struct hid_device *hid, unsigned int type, int *max) static void hid_find_max_report(struct hid_device *hid, unsigned int type,
unsigned int *max)
{ {
struct hid_report *report; struct hid_report *report;
int size; unsigned int size;
list_for_each_entry(report, &hid->report_enum[type].report_list, list) { list_for_each_entry(report, &hid->report_enum[type].report_list, list) {
size = ((report->size - 1) >> 3) + 1; size = ((report->size - 1) >> 3) + 1;
...@@ -705,9 +708,9 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) ...@@ -705,9 +708,9 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
struct hid_descriptor *hdesc; struct hid_descriptor *hdesc;
struct hid_device *hid; struct hid_device *hid;
u32 quirks = 0; u32 quirks = 0;
unsigned rsize = 0; unsigned int insize = 0, rsize = 0;
char *rdesc; char *rdesc;
int n, len, insize = 0; int n, len;
struct usbhid_device *usbhid; struct usbhid_device *usbhid;
quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor), quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor),
...@@ -800,6 +803,22 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) ...@@ -800,6 +803,22 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
goto fail; goto fail;
} }
hid->name[0] = 0;
if (dev->manufacturer)
strlcpy(hid->name, dev->manufacturer, sizeof(hid->name));
if (dev->product) {
if (dev->manufacturer)
strlcat(hid->name, " ", sizeof(hid->name));
strlcat(hid->name, dev->product, sizeof(hid->name));
}
if (!strlen(hid->name))
snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x",
le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
for (n = 0; n < interface->desc.bNumEndpoints; n++) { for (n = 0; n < interface->desc.bNumEndpoints; n++) {
struct usb_endpoint_descriptor *endpoint; struct usb_endpoint_descriptor *endpoint;
...@@ -812,6 +831,14 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) ...@@ -812,6 +831,14 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
interval = endpoint->bInterval; interval = endpoint->bInterval;
/* Some vendors give fullspeed interval on highspeed devides */
if (quirks & HID_QUIRK_FULLSPEED_INTERVAL &&
dev->speed == USB_SPEED_HIGH) {
interval = fls(endpoint->bInterval*8);
printk(KERN_INFO "%s: Fixing fullspeed to highspeed interval: %d -> %d\n",
hid->name, endpoint->bInterval, interval);
}
/* Change the polling interval of mice. */ /* Change the polling interval of mice. */
if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0) if (hid->collection->usage == HID_GD_MOUSE && hid_mousepoll_interval > 0)
interval = hid_mousepoll_interval; interval = hid_mousepoll_interval;
...@@ -844,8 +871,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) ...@@ -844,8 +871,7 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
goto fail; goto fail;
} }
init_waitqueue_head(&hid->wait); init_waitqueue_head(&usbhid->wait);
INIT_WORK(&usbhid->reset_work, hid_reset); INIT_WORK(&usbhid->reset_work, hid_reset);
setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid); setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid);
...@@ -859,22 +885,6 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) ...@@ -859,22 +885,6 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf)
usbhid->intf = intf; usbhid->intf = intf;
usbhid->ifnum = interface->desc.bInterfaceNumber; usbhid->ifnum = interface->desc.bInterfaceNumber;
hid->name[0] = 0;
if (dev->manufacturer)
strlcpy(hid->name, dev->manufacturer, sizeof(hid->name));
if (dev->product) {
if (dev->manufacturer)
strlcat(hid->name, " ", sizeof(hid->name));
strlcat(hid->name, dev->product, sizeof(hid->name));
}
if (!strlen(hid->name))
snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x",
le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
hid->bus = BUS_USB; hid->bus = BUS_USB;
hid->vendor = le16_to_cpu(dev->descriptor.idVendor); hid->vendor = le16_to_cpu(dev->descriptor.idVendor);
hid->product = le16_to_cpu(dev->descriptor.idProduct); hid->product = le16_to_cpu(dev->descriptor.idProduct);
...@@ -932,6 +942,7 @@ static void hid_disconnect(struct usb_interface *intf) ...@@ -932,6 +942,7 @@ static void hid_disconnect(struct usb_interface *intf)
spin_lock_irq(&usbhid->inlock); /* Sync with error handler */ spin_lock_irq(&usbhid->inlock); /* Sync with error handler */
usb_set_intfdata(intf, NULL); usb_set_intfdata(intf, NULL);
set_bit(HID_DISCONNECTED, &usbhid->iofl);
spin_unlock_irq(&usbhid->inlock); spin_unlock_irq(&usbhid->inlock);
usb_kill_urb(usbhid->urbin); usb_kill_urb(usbhid->urbin);
usb_kill_urb(usbhid->urbout); usb_kill_urb(usbhid->urbout);
......
...@@ -59,6 +59,9 @@ static struct hid_ff_initializer inits[] = { ...@@ -59,6 +59,9 @@ static struct hid_ff_initializer inits[] = {
{ 0x46d, 0xc295, hid_lgff_init }, /* Logitech MOMO force wheel */ { 0x46d, 0xc295, hid_lgff_init }, /* Logitech MOMO force wheel */
{ 0x46d, 0xca03, hid_lgff_init }, /* Logitech MOMO force wheel */ { 0x46d, 0xca03, hid_lgff_init }, /* Logitech MOMO force wheel */
#endif #endif
#ifdef CONFIG_LOGIRUMBLEPAD2_FF
{ 0x46d, 0xc218, hid_lg2ff_init }, /* Logitech Rumblepad 2 */
#endif
#ifdef CONFIG_PANTHERLORD_FF #ifdef CONFIG_PANTHERLORD_FF
{ 0x810, 0x0001, hid_plff_init }, /* "Twin USB Joystick" */ { 0x810, 0x0001, hid_plff_init }, /* "Twin USB Joystick" */
{ 0xe8f, 0x0003, hid_plff_init }, /* "GreenAsia Inc. USB Joystick " */ { 0xe8f, 0x0003, hid_plff_init }, /* "GreenAsia Inc. USB Joystick " */
......
/*
* Force feedback support for Logitech Rumblepad 2
*
* Copyright (c) 2008 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
*/
#include <linux/input.h>
#include <linux/usb.h>
#include <linux/hid.h>
#include "usbhid.h"
struct lg2ff_device {
struct hid_report *report;
};
static int play_effect(struct input_dev *dev, void *data,
struct ff_effect *effect)
{
struct hid_device *hid = input_get_drvdata(dev);
struct lg2ff_device *lg2ff = data;
int weak, strong;
strong = effect->u.rumble.strong_magnitude;
weak = effect->u.rumble.weak_magnitude;
if (weak || strong) {
weak = weak * 0xff / 0xffff;
strong = strong * 0xff / 0xffff;
lg2ff->report->field[0]->value[0] = 0x51;
lg2ff->report->field[0]->value[2] = weak;
lg2ff->report->field[0]->value[4] = strong;
} else {
lg2ff->report->field[0]->value[0] = 0xf3;
lg2ff->report->field[0]->value[2] = 0x00;
lg2ff->report->field[0]->value[4] = 0x00;
}
usbhid_submit_report(hid, lg2ff->report, USB_DIR_OUT);
return 0;
}
int hid_lg2ff_init(struct hid_device *hid)
{
struct lg2ff_device *lg2ff;
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-lg2ff: no output report found\n");
return -ENODEV;
}
report = list_entry(report_list->next, struct hid_report, list);
if (report->maxfield < 1) {
printk(KERN_ERR "hid-lg2ff: output report is empty\n");
return -ENODEV;
}
if (report->field[0]->report_count < 7) {
printk(KERN_ERR "hid-lg2ff: not enough values in the field\n");
return -ENODEV;
}
lg2ff = kmalloc(sizeof(struct lg2ff_device), GFP_KERNEL);
if (!lg2ff)
return -ENOMEM;
set_bit(FF_RUMBLE, dev->ffbit);
error = input_ff_create_memless(dev, lg2ff, play_effect);
if (error) {
kfree(lg2ff);
return error;
}
lg2ff->report = report;
report->field[0]->value[0] = 0xf3;
report->field[0]->value[1] = 0x00;
report->field[0]->value[2] = 0x00;
report->field[0]->value[3] = 0x00;
report->field[0]->value[4] = 0x00;
report->field[0]->value[5] = 0x00;
report->field[0]->value[6] = 0x00;
usbhid_submit_report(hid, report, USB_DIR_OUT);
printk(KERN_INFO "Force feedback for Logitech Rumblepad 2 by "
"Anssi Hannula <anssi.hannula@gmail.com>\n");
return 0;
}
...@@ -32,6 +32,9 @@ ...@@ -32,6 +32,9 @@
#define USB_VENDOR_ID_ADS_TECH 0x06e1 #define USB_VENDOR_ID_ADS_TECH 0x06e1
#define USB_DEVICE_ID_ADS_TECH_RADIO_SI470X 0xa155 #define USB_DEVICE_ID_ADS_TECH_RADIO_SI470X 0xa155
#define USB_VENDOR_ID_AFATECH 0x15a4
#define USB_DEVICE_ID_AFATECH_AF9016 0x9016
#define USB_VENDOR_ID_AIPTEK 0x08ca #define USB_VENDOR_ID_AIPTEK 0x08ca
#define USB_DEVICE_ID_AIPTEK_01 0x0001 #define USB_DEVICE_ID_AIPTEK_01 0x0001
#define USB_DEVICE_ID_AIPTEK_10 0x0010 #define USB_DEVICE_ID_AIPTEK_10 0x0010
...@@ -124,6 +127,9 @@ ...@@ -124,6 +127,9 @@
#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100 #define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100
#define USB_DEVICE_ID_DELORME_EM_LT20 0x0200 #define USB_DEVICE_ID_DELORME_EM_LT20 0x0200
#define USB_VENDOR_ID_DMI 0x0c0b
#define USB_DEVICE_ID_DMI_ENC 0x5fab
#define USB_VENDOR_ID_ELO 0x04E7 #define USB_VENDOR_ID_ELO 0x04E7
#define USB_DEVICE_ID_ELO_TS2700 0x0020 #define USB_DEVICE_ID_ELO_TS2700 0x0020
...@@ -199,17 +205,6 @@ ...@@ -199,17 +205,6 @@
#define USB_DEVICE_ID_GTCO_502 0x0502 #define USB_DEVICE_ID_GTCO_502 0x0502
#define USB_DEVICE_ID_GTCO_503 0x0503 #define USB_DEVICE_ID_GTCO_503 0x0503
#define USB_DEVICE_ID_GTCO_504 0x0504 #define USB_DEVICE_ID_GTCO_504 0x0504
#define USB_DEVICE_ID_GTCO_600 0x0600
#define USB_DEVICE_ID_GTCO_601 0x0601
#define USB_DEVICE_ID_GTCO_602 0x0602
#define USB_DEVICE_ID_GTCO_603 0x0603
#define USB_DEVICE_ID_GTCO_604 0x0604
#define USB_DEVICE_ID_GTCO_605 0x0605
#define USB_DEVICE_ID_GTCO_606 0x0606
#define USB_DEVICE_ID_GTCO_607 0x0607
#define USB_DEVICE_ID_GTCO_608 0x0608
#define USB_DEVICE_ID_GTCO_609 0x0609
#define USB_DEVICE_ID_GTCO_609 0x0609
#define USB_DEVICE_ID_GTCO_1000 0x1000 #define USB_DEVICE_ID_GTCO_1000 0x1000
#define USB_DEVICE_ID_GTCO_1001 0x1001 #define USB_DEVICE_ID_GTCO_1001 0x1001
#define USB_DEVICE_ID_GTCO_1002 0x1002 #define USB_DEVICE_ID_GTCO_1002 0x1002
...@@ -320,6 +315,7 @@ ...@@ -320,6 +315,7 @@
#define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500 0xc512 #define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500 0xc512
#define USB_DEVICE_ID_MX3000_RECEIVER 0xc513 #define USB_DEVICE_ID_MX3000_RECEIVER 0xc513
#define USB_DEVICE_ID_DINOVO_EDGE 0xc714 #define USB_DEVICE_ID_DINOVO_EDGE 0xc714
#define USB_DEVICE_ID_DINOVO_MINI 0xc71f
#define USB_VENDOR_ID_MCC 0x09db #define USB_VENDOR_ID_MCC 0x09db
#define USB_DEVICE_ID_MCC_PMD1024LS 0x0076 #define USB_DEVICE_ID_MCC_PMD1024LS 0x0076
...@@ -332,6 +328,7 @@ ...@@ -332,6 +328,7 @@
#define USB_VENDOR_ID_MICROSOFT 0x045e #define USB_VENDOR_ID_MICROSOFT 0x045e
#define USB_DEVICE_ID_SIDEWINDER_GV 0x003b #define USB_DEVICE_ID_SIDEWINDER_GV 0x003b
#define USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0 0x009d #define USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0 0x009d
#define USB_DEVICE_ID_DESKTOP_RECV_1028 0x00f9
#define USB_DEVICE_ID_MS_NE4K 0x00db #define USB_DEVICE_ID_MS_NE4K 0x00db
#define USB_DEVICE_ID_MS_LK6K 0x00f9 #define USB_DEVICE_ID_MS_LK6K 0x00f9
...@@ -377,6 +374,9 @@ ...@@ -377,6 +374,9 @@
#define USB_VENDOR_ID_SUN 0x0430 #define USB_VENDOR_ID_SUN 0x0430
#define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab #define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab
#define USB_VENDOR_ID_SUNPLUS 0x04fc
#define USB_DEVICE_ID_SUNPLUS_WDESKTOP 0x05d8
#define USB_VENDOR_ID_TOPMAX 0x0663 #define USB_VENDOR_ID_TOPMAX 0x0663
#define USB_DEVICE_ID_TOPMAX_COBRAPAD 0x0103 #define USB_DEVICE_ID_TOPMAX_COBRAPAD 0x0103
...@@ -435,9 +435,13 @@ static const struct hid_blacklist { ...@@ -435,9 +435,13 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD }, { USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE, HID_QUIRK_DUPLICATE_USAGES }, { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE, HID_QUIRK_DUPLICATE_USAGES },
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI, HID_QUIRK_DUPLICATE_USAGES },
{ USB_VENDOR_ID_AFATECH, USB_DEVICE_ID_AFATECH_AF9016, HID_QUIRK_FULLSPEED_INTERVAL },
{ USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM, HID_QUIRK_HIDDEV }, { 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_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4, HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT },
{ USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE, HID_QUIRK_HIDDEV | HID_QUIRK_IGNORE_HIDINPUT },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV, HID_QUIRK_HIDINPUT }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_SIDEWINDER_GV, HID_QUIRK_HIDINPUT },
{ USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193, HID_QUIRK_HWHEEL_WHEEL_INVERT }, { USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193, HID_QUIRK_HWHEEL_WHEEL_INVERT },
...@@ -518,16 +522,6 @@ static const struct hid_blacklist { ...@@ -518,16 +522,6 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_502, 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_503, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_504, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_504, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_600, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_601, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_602, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_603, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_604, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_605, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_606, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_607, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_608, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_609, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1000, 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_1001, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1002, HID_QUIRK_IGNORE }, { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1002, HID_QUIRK_IGNORE },
...@@ -601,6 +595,7 @@ static const struct hid_blacklist { ...@@ -601,6 +595,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_2PORTKVM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, 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_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET }, { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D, HID_QUIRK_NOGET }, { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL, HID_QUIRK_NOGET }, { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL, HID_QUIRK_NOGET },
...@@ -608,7 +603,7 @@ static const struct hid_blacklist { ...@@ -608,7 +603,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE, 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 }, { 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_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_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, { 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_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
...@@ -719,6 +714,7 @@ static const struct hid_rdesc_blacklist { ...@@ -719,6 +714,7 @@ static const struct hid_rdesc_blacklist {
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER, HID_QUIRK_RDESC_LOGITECH }, { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER, HID_QUIRK_RDESC_LOGITECH },
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER, HID_QUIRK_RDESC_LOGITECH }, { 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_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2, HID_QUIRK_RDESC_LOGITECH },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_DESKTOP_RECV_1028, HID_QUIRK_RDESC_MICROSOFT_RECV_1028 },
{ USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E, HID_QUIRK_RDESC_BUTTON_CONSUMER }, { USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E, HID_QUIRK_RDESC_BUTTON_CONSUMER },
...@@ -728,6 +724,8 @@ static const struct hid_rdesc_blacklist { ...@@ -728,6 +724,8 @@ static const struct hid_rdesc_blacklist {
{ USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE, HID_QUIRK_RDESC_SAMSUNG_REMOTE }, { USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE, HID_QUIRK_RDESC_SAMSUNG_REMOTE },
{ USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP, HID_QUIRK_RDESC_SUNPLUS_WDESKTOP },
{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1, HID_QUIRK_RDESC_SWAPPED_MIN_MAX }, { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1, HID_QUIRK_RDESC_SWAPPED_MIN_MAX },
{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2, HID_QUIRK_RDESC_SWAPPED_MIN_MAX }, { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2, HID_QUIRK_RDESC_SWAPPED_MIN_MAX },
...@@ -793,7 +791,7 @@ static struct hid_blacklist *usbhid_exists_dquirk(const u16 idVendor, ...@@ -793,7 +791,7 @@ static struct hid_blacklist *usbhid_exists_dquirk(const u16 idVendor,
* *
* Returns: 0 OK, -error on failure. * Returns: 0 OK, -error on failure.
*/ */
int usbhid_modify_dquirk(const u16 idVendor, const u16 idProduct, static int usbhid_modify_dquirk(const u16 idVendor, const u16 idProduct,
const u32 quirks) const u32 quirks)
{ {
struct quirks_list_struct *q_new, *q; struct quirks_list_struct *q_new, *q;
...@@ -1002,6 +1000,17 @@ static void usbhid_fixup_logitech_descriptor(unsigned char *rdesc, int rsize) ...@@ -1002,6 +1000,17 @@ static void usbhid_fixup_logitech_descriptor(unsigned char *rdesc, int rsize)
} }
} }
static void usbhid_fixup_sunplus_wdesktop(unsigned char *rdesc, int rsize)
{
if (rsize >= 107 && rdesc[104] == 0x26
&& rdesc[105] == 0x80
&& rdesc[106] == 0x03) {
printk(KERN_INFO "Fixing up Sunplus Wireless Desktop report descriptor\n");
rdesc[105] = rdesc[110] = 0x03;
rdesc[106] = rdesc[111] = 0x21;
}
}
/* /*
* Samsung IrDA remote controller (reports as Cypress USB Mouse). * Samsung IrDA remote controller (reports as Cypress USB Mouse).
* *
...@@ -1089,6 +1098,28 @@ static void usbhid_fixup_button_consumer_descriptor(unsigned char *rdesc, int rs ...@@ -1089,6 +1098,28 @@ static void usbhid_fixup_button_consumer_descriptor(unsigned char *rdesc, int rs
} }
} }
/*
* Microsoft Wireless Desktop Receiver (Model 1028) has several
* 'Usage Min/Max' where it ought to have 'Physical Min/Max'
*/
static void usbhid_fixup_microsoft_descriptor(unsigned char *rdesc, int rsize)
{
if (rsize == 571 && rdesc[284] == 0x19
&& rdesc[286] == 0x2a
&& rdesc[304] == 0x19
&& rdesc[306] == 0x29
&& rdesc[352] == 0x1a
&& rdesc[355] == 0x2a
&& rdesc[557] == 0x19
&& rdesc[559] == 0x29) {
printk(KERN_INFO "Fixing up Microsoft Wireless Receiver Model 1028 report descriptor\n");
rdesc[284] = rdesc[304] = rdesc[558] = 0x35;
rdesc[352] = 0x36;
rdesc[286] = rdesc[355] = 0x46;
rdesc[306] = rdesc[559] = 0x45;
}
}
static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned rsize) static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned rsize)
{ {
if ((quirks & HID_QUIRK_RDESC_CYMOTION)) if ((quirks & HID_QUIRK_RDESC_CYMOTION))
...@@ -1112,6 +1143,11 @@ static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned ...@@ -1112,6 +1143,11 @@ static void __usbhid_fixup_report_descriptor(__u32 quirks, char *rdesc, unsigned
if (quirks & HID_QUIRK_RDESC_SAMSUNG_REMOTE) if (quirks & HID_QUIRK_RDESC_SAMSUNG_REMOTE)
usbhid_fixup_samsung_irda_descriptor(rdesc, rsize); usbhid_fixup_samsung_irda_descriptor(rdesc, rsize);
if (quirks & HID_QUIRK_RDESC_MICROSOFT_RECV_1028)
usbhid_fixup_microsoft_descriptor(rdesc, rsize);
if (quirks & HID_QUIRK_RDESC_SUNPLUS_WDESKTOP)
usbhid_fixup_sunplus_wdesktop(rdesc, rsize);
} }
/** /**
...@@ -1150,5 +1186,4 @@ void usbhid_fixup_report_descriptor(const u16 idVendor, const u16 idProduct, ...@@ -1150,5 +1186,4 @@ void usbhid_fixup_report_descriptor(const u16 idVendor, const u16 idProduct,
else if (paramVendor == idVendor && paramProduct == idProduct) else if (paramVendor == idVendor && paramProduct == idProduct)
__usbhid_fixup_report_descriptor(quirks, rdesc, rsize); __usbhid_fixup_report_descriptor(quirks, rdesc, rsize);
} }
} }
...@@ -393,6 +393,153 @@ static unsigned int hiddev_poll(struct file *file, poll_table *wait) ...@@ -393,6 +393,153 @@ static unsigned int hiddev_poll(struct file *file, poll_table *wait)
/* /*
* "ioctl" file op * "ioctl" file op
*/ */
static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd, void __user *user_arg)
{
struct hid_device *hid = hiddev->hid;
struct hiddev_report_info rinfo;
struct hiddev_usage_ref_multi *uref_multi = NULL;
struct hiddev_usage_ref *uref;
struct hid_report *report;
struct hid_field *field;
int i;
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;
}
switch (cmd) {
case HIDIOCGUCODE:
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;
default:
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;
}
}
static noinline int hiddev_ioctl_string(struct hiddev *hiddev, unsigned int cmd, void __user *user_arg)
{
struct hid_device *hid = hiddev->hid;
struct usb_device *dev = hid_to_usb_dev(hid);
int idx, len;
char *buf;
if (get_user(idx, (int __user *)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;
}
static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) 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_list *list = file->private_data;
...@@ -402,8 +549,6 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd ...@@ -402,8 +549,6 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
struct hiddev_collection_info cinfo; struct hiddev_collection_info cinfo;
struct hiddev_report_info rinfo; struct hiddev_report_info rinfo;
struct hiddev_field_info finfo; struct hiddev_field_info finfo;
struct hiddev_usage_ref_multi *uref_multi = NULL;
struct hiddev_usage_ref *uref;
struct hiddev_devinfo dinfo; struct hiddev_devinfo dinfo;
struct hid_report *report; struct hid_report *report;
struct hid_field *field; struct hid_field *field;
...@@ -470,30 +615,7 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd ...@@ -470,30 +615,7 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
} }
case HIDIOCGSTRING: case HIDIOCGSTRING:
{ return hiddev_ioctl_string(hiddev, cmd, user_arg);
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: case HIDIOCINITREPORT:
usbhid_init_reports(hid); usbhid_init_reports(hid);
...@@ -578,121 +700,13 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd ...@@ -578,121 +700,13 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
return 0; return 0;
case HIDIOCGUCODE: case HIDIOCGUCODE:
uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL); /* fall through */
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 HIDIOCGUSAGE:
case HIDIOCSUSAGE: case HIDIOCSUSAGE:
case HIDIOCGUSAGES: case HIDIOCGUSAGES:
case HIDIOCSUSAGES: case HIDIOCSUSAGES:
case HIDIOCGCOLLECTIONINDEX: case HIDIOCGCOLLECTIONINDEX:
uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL); return hiddev_ioctl_usage(hiddev, cmd, user_arg);
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: case HIDIOCGCOLLECTIONINFO:
if (copy_from_user(&cinfo, user_arg, sizeof(cinfo))) if (copy_from_user(&cinfo, user_arg, sizeof(cinfo)))
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/wait.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/input.h> #include <linux/input.h>
...@@ -77,7 +78,7 @@ struct usbhid_device { ...@@ -77,7 +78,7 @@ struct usbhid_device {
unsigned long stop_retry; /* Time to give up, in jiffies */ unsigned long stop_retry; /* Time to give up, in jiffies */
unsigned int retry_delay; /* Delay length in ms */ unsigned int retry_delay; /* Delay length in ms */
struct work_struct reset_work; /* Task context for resets */ struct work_struct reset_work; /* Task context for resets */
wait_queue_head_t wait; /* For sleeping */
}; };
#define hid_to_usb_dev(hid_dev) \ #define hid_to_usb_dev(hid_dev) \
......
...@@ -211,7 +211,9 @@ unifdef-y += hdlcdrv.h ...@@ -211,7 +211,9 @@ unifdef-y += hdlcdrv.h
unifdef-y += hdlc.h unifdef-y += hdlc.h
unifdef-y += hdreg.h unifdef-y += hdreg.h
unifdef-y += hdsmart.h unifdef-y += hdsmart.h
unifdef-y += hid.h
unifdef-y += hiddev.h unifdef-y += hiddev.h
unifdef-y += hidraw.h
unifdef-y += hpet.h unifdef-y += hpet.h
unifdef-y += i2c.h unifdef-y += i2c.h
unifdef-y += i2c-dev.h unifdef-y += i2c-dev.h
......
...@@ -284,6 +284,7 @@ struct hid_item { ...@@ -284,6 +284,7 @@ struct hid_item {
#define HID_QUIRK_2WHEEL_MOUSE_HACK_B8 0x02000000 #define HID_QUIRK_2WHEEL_MOUSE_HACK_B8 0x02000000
#define HID_QUIRK_HWHEEL_WHEEL_INVERT 0x04000000 #define HID_QUIRK_HWHEEL_WHEEL_INVERT 0x04000000
#define HID_QUIRK_MICROSOFT_KEYS 0x08000000 #define HID_QUIRK_MICROSOFT_KEYS 0x08000000
#define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000
/* /*
* Separate quirks for runtime report descriptor fixup * Separate quirks for runtime report descriptor fixup
...@@ -296,6 +297,8 @@ struct hid_item { ...@@ -296,6 +297,8 @@ struct hid_item {
#define HID_QUIRK_RDESC_MACBOOK_JIS 0x00000010 #define HID_QUIRK_RDESC_MACBOOK_JIS 0x00000010
#define HID_QUIRK_RDESC_BUTTON_CONSUMER 0x00000020 #define HID_QUIRK_RDESC_BUTTON_CONSUMER 0x00000020
#define HID_QUIRK_RDESC_SAMSUNG_REMOTE 0x00000040 #define HID_QUIRK_RDESC_SAMSUNG_REMOTE 0x00000040
#define HID_QUIRK_RDESC_MICROSOFT_RECV_1028 0x00000080
#define HID_QUIRK_RDESC_SUNPLUS_WDESKTOP 0x00000100
/* /*
* This is the global environment of the parser. This information is * This is the global environment of the parser. This information is
...@@ -320,7 +323,7 @@ struct hid_global { ...@@ -320,7 +323,7 @@ struct hid_global {
* This is the local environment. It is persistent up the next main-item. * This is the local environment. It is persistent up the next main-item.
*/ */
#define HID_MAX_USAGES 8192 #define HID_MAX_USAGES 12288
#define HID_DEFAULT_NUM_COLLECTIONS 16 #define HID_DEFAULT_NUM_COLLECTIONS 16
struct hid_local { struct hid_local {
...@@ -421,6 +424,7 @@ struct hid_control_fifo { ...@@ -421,6 +424,7 @@ struct hid_control_fifo {
#define HID_RESET_PENDING 4 #define HID_RESET_PENDING 4
#define HID_SUSPENDED 5 #define HID_SUSPENDED 5
#define HID_CLEAR_HALT 6 #define HID_CLEAR_HALT 6
#define HID_DISCONNECTED 7
struct hid_input { struct hid_input {
struct list_head list; struct list_head list;
...@@ -452,8 +456,6 @@ struct hid_device { /* device report descriptor */ ...@@ -452,8 +456,6 @@ struct hid_device { /* device report descriptor */
void *hidraw; void *hidraw;
int minor; /* Hiddev minor number */ int minor; /* Hiddev minor number */
wait_queue_head_t wait; /* For sleeping */
int open; /* is the device open by anyone? */ int open; /* is the device open by anyone? */
char name[128]; /* Device name */ char name[128]; /* Device name */
char phys[64]; /* Device physical location */ char phys[64]; /* Device physical location */
...@@ -530,14 +532,12 @@ int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int ...@@ -530,14 +532,12 @@ int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int
int hidinput_mapping_quirks(struct hid_usage *, struct input_dev *, unsigned long **, int *); int hidinput_mapping_quirks(struct hid_usage *, struct input_dev *, unsigned long **, int *);
int hidinput_event_quirks(struct hid_device *, struct hid_field *, struct hid_usage *, __s32); int hidinput_event_quirks(struct hid_device *, struct hid_field *, struct hid_usage *, __s32);
int hidinput_apple_event(struct hid_device *, struct input_dev *, struct hid_usage *, __s32); int hidinput_apple_event(struct hid_device *, struct input_dev *, struct hid_usage *, __s32);
void hid_input_field(struct hid_device *hid, struct hid_field *field, __u8 *data, int interrupt);
void hid_output_report(struct hid_report *report, __u8 *data); void hid_output_report(struct hid_report *report, __u8 *data);
void hid_free_device(struct hid_device *device); void hid_free_device(struct hid_device *device);
struct hid_device *hid_parse_report(__u8 *start, unsigned size); struct hid_device *hid_parse_report(__u8 *start, unsigned size);
/* HID quirks API */ /* HID quirks API */
u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct); u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct);
int usbhid_modify_dquirk(const u16 idVendor, const u16 idProduct, const u32 quirks);
int usbhid_quirks_init(char **quirks_param); int usbhid_quirks_init(char **quirks_param);
void usbhid_quirks_exit(void); void usbhid_quirks_exit(void);
void usbhid_fixup_report_descriptor(const u16, const u16, char *, unsigned, char **); void usbhid_fixup_report_descriptor(const u16, const u16, char *, unsigned, char **);
...@@ -546,6 +546,7 @@ void usbhid_fixup_report_descriptor(const u16, const u16, char *, unsigned, char ...@@ -546,6 +546,7 @@ void usbhid_fixup_report_descriptor(const u16, const u16, char *, unsigned, char
int hid_ff_init(struct hid_device *hid); int hid_ff_init(struct hid_device *hid);
int hid_lgff_init(struct hid_device *hid); int hid_lgff_init(struct hid_device *hid);
int hid_lg2ff_init(struct hid_device *hid);
int hid_plff_init(struct hid_device *hid); int hid_plff_init(struct hid_device *hid);
int hid_tmff_init(struct hid_device *hid); int hid_tmff_init(struct hid_device *hid);
int hid_zpff_init(struct hid_device *hid); int hid_zpff_init(struct hid_device *hid);
...@@ -566,7 +567,11 @@ static inline int hid_ff_init(struct hid_device *hid) { return -1; } ...@@ -566,7 +567,11 @@ static inline int hid_ff_init(struct hid_device *hid) { return -1; }
#define dbg_hid_line(format, arg...) if (hid_debug) \ #define dbg_hid_line(format, arg...) if (hid_debug) \
printk(format, ## arg) printk(format, ## arg)
#else #else
#define dbg_hid(format, arg...) do {} while (0) static inline int __attribute__((format(printf, 1, 2)))
dbg_hid(const char *fmt, ...)
{
return 0;
}
#define dbg_hid_line dbg_hid #define dbg_hid_line dbg_hid
#endif #endif
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
*/ */
#include <linux/hid.h> #include <linux/hid.h>
#include <linux/types.h>
struct hidraw_report_descriptor { struct hidraw_report_descriptor {
__u32 size; __u32 size;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment