Commit 56ccd186 authored by Jiri Kosina's avatar Jiri Kosina

Merge branch 'upstream' into for-linus

Conflicts:
	drivers/hid/hid-core.c
parents b3d07e03 d1257081
...@@ -104,6 +104,12 @@ config HID_APPLE ...@@ -104,6 +104,12 @@ config HID_APPLE
Say Y here if you want support for keyboards of Apple iBooks, PowerBooks, Say Y here if you want support for keyboards of Apple iBooks, PowerBooks,
MacBooks, MacBook Pros and Apple Aluminum. MacBooks, MacBook Pros and Apple Aluminum.
config HID_AUREAL
tristate "Aureal"
depends on USB_HID
---help---
Support for Aureal Cy se W-01RN Remote Controller and other Aureal derived remotes.
config HID_BELKIN config HID_BELKIN
tristate "Belkin Flip KVM and Wireless keyboard" if EXPERT tristate "Belkin Flip KVM and Wireless keyboard" if EXPERT
depends on USB_HID depends on USB_HID
......
...@@ -38,6 +38,7 @@ endif ...@@ -38,6 +38,7 @@ endif
obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o
obj-$(CONFIG_HID_ACRUX) += hid-axff.o obj-$(CONFIG_HID_ACRUX) += hid-axff.o
obj-$(CONFIG_HID_APPLE) += hid-apple.o obj-$(CONFIG_HID_APPLE) += hid-apple.o
obj-$(CONFIG_HID_AUREAL) += hid-aureal.o
obj-$(CONFIG_HID_BELKIN) += hid-belkin.o obj-$(CONFIG_HID_BELKIN) += hid-belkin.o
obj-$(CONFIG_HID_CHERRY) += hid-cherry.o obj-$(CONFIG_HID_CHERRY) += hid-cherry.o
obj-$(CONFIG_HID_CHICONY) += hid-chicony.o obj-$(CONFIG_HID_CHICONY) += hid-chicony.o
......
...@@ -234,7 +234,7 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, ...@@ -234,7 +234,7 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
} }
} }
if (iso_layout) { if (iso_layout) {
if (asc->quirks & APPLE_ISO_KEYBOARD) { if (asc->quirks & APPLE_ISO_KEYBOARD) {
trans = apple_find_translation(apple_iso_keyboard, usage->code); trans = apple_find_translation(apple_iso_keyboard, usage->code);
if (trans) { if (trans) {
......
/*
* HID driver for Aureal Cy se W-01RN USB_V3.1 devices
*
* Copyright (c) 2010 Franco Catrin <fcatrin@gmail.com>
* Copyright (c) 2010 Ben Cropley <bcropley@internode.on.net>
*
* Based on HID sunplus driver by
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
* Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>
#include "hid-ids.h"
static __u8 *aureal_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
if (*rsize >= 54 && rdesc[52] == 0x25 && rdesc[53] == 0x01) {
dev_info(&hdev->dev, "fixing Aureal Cy se W-01RN USB_V3.1 report descriptor.\n");
rdesc[53] = 0x65;
} return rdesc;
}
static const struct hid_device_id aureal_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_AUREAL, USB_DEVICE_ID_AUREAL_W01RN) },
{ }
};
MODULE_DEVICE_TABLE(hid, aureal_devices);
static struct hid_driver aureal_driver = {
.name = "aureal",
.id_table = aureal_devices,
.report_fixup = aureal_report_fixup,
};
static int __init aureal_init(void)
{
return hid_register_driver(&aureal_driver);
}
static void __exit aureal_exit(void)
{
hid_unregister_driver(&aureal_driver);
}
module_init(aureal_init);
module_exit(aureal_exit);
MODULE_LICENSE("GPL");
...@@ -230,9 +230,16 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign ...@@ -230,9 +230,16 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
return -1; return -1;
} }
if (parser->global.logical_maximum < parser->global.logical_minimum) { /* Handle both signed and unsigned cases properly */
hid_err(parser->device, "logical range invalid %d %d\n", if ((parser->global.logical_minimum < 0 &&
parser->global.logical_minimum, parser->global.logical_maximum); parser->global.logical_maximum <
parser->global.logical_minimum) ||
(parser->global.logical_minimum >= 0 &&
(__u32)parser->global.logical_maximum <
(__u32)parser->global.logical_minimum)) {
dbg_hid("logical range invalid 0x%x 0x%x\n",
parser->global.logical_minimum,
parser->global.logical_maximum);
return -1; return -1;
} }
...@@ -1149,7 +1156,7 @@ static struct hid_report *hid_get_report(struct hid_report_enum *report_enum, ...@@ -1149,7 +1156,7 @@ static struct hid_report *hid_get_report(struct hid_report_enum *report_enum,
return report; return report;
} }
void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
int interrupt) int interrupt)
{ {
struct hid_report_enum *report_enum = hid->report_enum + type; struct hid_report_enum *report_enum = hid->report_enum + type;
...@@ -1157,10 +1164,11 @@ void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, ...@@ -1157,10 +1164,11 @@ void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
unsigned int a; unsigned int a;
int rsize, csize = size; int rsize, csize = size;
u8 *cdata = data; u8 *cdata = data;
int ret = 0;
report = hid_get_report(report_enum, data); report = hid_get_report(report_enum, data);
if (!report) if (!report)
return; goto out;
if (report_enum->numbered) { if (report_enum->numbered) {
cdata++; cdata++;
...@@ -1180,14 +1188,19 @@ void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, ...@@ -1180,14 +1188,19 @@ void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
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) {
hidraw_report_event(hid, data, size); ret = hidraw_report_event(hid, data, size);
if (ret)
goto out;
}
for (a = 0; a < report->maxfield; a++) for (a = 0; a < report->maxfield; a++)
hid_input_field(hid, report->field[a], cdata, interrupt); hid_input_field(hid, report->field[a], cdata, interrupt);
if (hid->claimed & HID_CLAIMED_INPUT) if (hid->claimed & HID_CLAIMED_INPUT)
hidinput_report_event(hid, report); hidinput_report_event(hid, report);
out:
return ret;
} }
EXPORT_SYMBOL_GPL(hid_report_raw_event); EXPORT_SYMBOL_GPL(hid_report_raw_event);
...@@ -1264,7 +1277,7 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i ...@@ -1264,7 +1277,7 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i
} }
} }
hid_report_raw_event(hid, type, data, size, interrupt); ret = hid_report_raw_event(hid, type, data, size, interrupt);
unlock: unlock:
up(&hid->driver_lock); up(&hid->driver_lock);
...@@ -1496,6 +1509,7 @@ static const struct hid_device_id hid_have_special_driver[] = { ...@@ -1496,6 +1509,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_AUREAL, USB_DEVICE_ID_AUREAL_W01RN) },
{ HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM) }, { HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM) },
{ HID_USB_DEVICE(USB_VENDOR_ID_BAANTO, USB_DEVICE_ID_BAANTO_MT_190W2), }, { HID_USB_DEVICE(USB_VENDOR_ID_BAANTO, USB_DEVICE_ID_BAANTO_MT_190W2), },
{ HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE) },
...@@ -1631,6 +1645,7 @@ static const struct hid_device_id hid_have_special_driver[] = { ...@@ -1631,6 +1645,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SUPER_JOY_BOX_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SUPER_JOY_BOX_3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD) },
......
...@@ -154,6 +154,9 @@ ...@@ -154,6 +154,9 @@
#define USB_DEVICE_ID_ATMEL_MULTITOUCH 0x211c #define USB_DEVICE_ID_ATMEL_MULTITOUCH 0x211c
#define USB_DEVICE_ID_ATMEL_MXT_DIGITIZER 0x2118 #define USB_DEVICE_ID_ATMEL_MXT_DIGITIZER 0x2118
#define USB_VENDOR_ID_AUREAL 0x0755
#define USB_DEVICE_ID_AUREAL_W01RN 0x2626
#define USB_VENDOR_ID_AVERMEDIA 0x07ca #define USB_VENDOR_ID_AVERMEDIA 0x07ca
#define USB_DEVICE_ID_AVER_FM_MR800 0xb800 #define USB_DEVICE_ID_AVER_FM_MR800 0xb800
...@@ -729,6 +732,7 @@ ...@@ -729,6 +732,7 @@
#define USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U 0x0004 #define USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U 0x0004
#define USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U 0x0005 #define USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U 0x0005
#define USB_DEVICE_ID_UCLOGIC_TABLET_WP1062 0x0064 #define USB_DEVICE_ID_UCLOGIC_TABLET_WP1062 0x0064
#define USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850 0x0522
#define USB_VENDOR_ID_UNITEC 0x227d #define USB_VENDOR_ID_UNITEC 0x227d
#define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709 0x0709 #define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709 0x0709
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/hid.h> #include <linux/hid.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/usb.h>
#include "hid-ids.h" #include "hid-ids.h"
...@@ -352,9 +353,125 @@ static __u8 pf1209_rdesc_fixed[] = { ...@@ -352,9 +353,125 @@ static __u8 pf1209_rdesc_fixed[] = {
0xC0 /* End Collection */ 0xC0 /* End Collection */
}; };
/*
* See TWHL850 description, device and HID report descriptors at
* http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Wireless_Tablet_TWHL850
*/
/* Size of the original descriptors of TWHL850 tablet */
#define TWHL850_RDESC_ORIG_SIZE0 182
#define TWHL850_RDESC_ORIG_SIZE1 161
#define TWHL850_RDESC_ORIG_SIZE2 92
/* Fixed PID 0522 tablet report descriptor, interface 0 (stylus) */
static __u8 twhl850_rdesc_fixed0[] = {
0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x09, /* Report ID (9), */
0x09, 0x20, /* Usage (Stylus), */
0xA0, /* Collection (Physical), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x03, /* Report Count (3), */
0x09, 0x42, /* Usage (Tip Switch), */
0x09, 0x44, /* Usage (Barrel Switch), */
0x09, 0x46, /* Usage (Tablet Pick), */
0x81, 0x02, /* Input (Variable), */
0x81, 0x03, /* Input (Constant, Variable), */
0x95, 0x01, /* Report Count (1), */
0x09, 0x32, /* Usage (In Range), */
0x81, 0x02, /* Input (Variable), */
0x81, 0x03, /* Input (Constant, Variable), */
0x75, 0x10, /* Report Size (16), */
0xA4, /* Push, */
0x05, 0x01, /* Usage Page (Desktop), */
0x65, 0x13, /* Unit (Inch), */
0x55, 0xFD, /* Unit Exponent (-3), */
0x34, /* Physical Minimum (0), */
0x09, 0x30, /* Usage (X), */
0x46, 0x40, 0x1F, /* Physical Maximum (8000), */
0x26, 0x00, 0x7D, /* Logical Maximum (32000), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x31, /* Usage (Y), */
0x46, 0x88, 0x13, /* Physical Maximum (5000), */
0x26, 0x20, 0x4E, /* Logical Maximum (20000), */
0x81, 0x02, /* Input (Variable), */
0xB4, /* Pop, */
0x09, 0x30, /* Usage (Tip Pressure), */
0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
0x81, 0x02, /* Input (Variable), */
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
/* Fixed PID 0522 tablet report descriptor, interface 1 (mouse) */
static __u8 twhl850_rdesc_fixed1[] = {
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x02, /* Usage (Mouse), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x01, /* Report ID (1), */
0x09, 0x01, /* Usage (Pointer), */
0xA0, /* Collection (Physical), */
0x05, 0x09, /* Usage Page (Button), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x03, /* Report Count (3), */
0x19, 0x01, /* Usage Minimum (01h), */
0x29, 0x03, /* Usage Maximum (03h), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x05, /* Report Count (5), */
0x81, 0x03, /* Input (Constant, Variable), */
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x30, /* Usage (X), */
0x09, 0x31, /* Usage (Y), */
0x16, 0x00, 0x80, /* Logical Minimum (-32768), */
0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */
0x75, 0x10, /* Report Size (16), */
0x95, 0x02, /* Report Count (2), */
0x81, 0x06, /* Input (Variable, Relative), */
0x09, 0x38, /* Usage (Wheel), */
0x15, 0xFF, /* Logical Minimum (-1), */
0x25, 0x01, /* Logical Maximum (1), */
0x95, 0x01, /* Report Count (1), */
0x75, 0x08, /* Report Size (8), */
0x81, 0x06, /* Input (Variable, Relative), */
0x81, 0x03, /* Input (Constant, Variable), */
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
/* Fixed PID 0522 tablet report descriptor, interface 2 (frame buttons) */
static __u8 twhl850_rdesc_fixed2[] = {
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x06, /* Usage (Keyboard), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x03, /* Report ID (3), */
0x05, 0x07, /* Usage Page (Keyboard), */
0x14, /* Logical Minimum (0), */
0x19, 0xE0, /* Usage Minimum (KB Leftcontrol), */
0x29, 0xE7, /* Usage Maximum (KB Right GUI), */
0x25, 0x01, /* Logical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x08, /* Report Count (8), */
0x81, 0x02, /* Input (Variable), */
0x18, /* Usage Minimum (None), */
0x29, 0xFF, /* Usage Maximum (FFh), */
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x06, /* Report Count (6), */
0x80, /* Input, */
0xC0 /* End Collection */
};
static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize) unsigned int *rsize)
{ {
struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
__u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
switch (hdev->product) { switch (hdev->product) {
case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209: case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209:
if (*rsize == PF1209_RDESC_ORIG_SIZE) { if (*rsize == PF1209_RDESC_ORIG_SIZE) {
...@@ -386,6 +503,28 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, ...@@ -386,6 +503,28 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
*rsize = sizeof(wp1062_rdesc_fixed); *rsize = sizeof(wp1062_rdesc_fixed);
} }
break; break;
case USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850:
switch (iface_num) {
case 0:
if (*rsize == TWHL850_RDESC_ORIG_SIZE0) {
rdesc = twhl850_rdesc_fixed0;
*rsize = sizeof(twhl850_rdesc_fixed0);
}
break;
case 1:
if (*rsize == TWHL850_RDESC_ORIG_SIZE1) {
rdesc = twhl850_rdesc_fixed1;
*rsize = sizeof(twhl850_rdesc_fixed1);
}
break;
case 2:
if (*rsize == TWHL850_RDESC_ORIG_SIZE2) {
rdesc = twhl850_rdesc_fixed2;
*rsize = sizeof(twhl850_rdesc_fixed2);
}
break;
}
break;
} }
return rdesc; return rdesc;
...@@ -402,6 +541,8 @@ static const struct hid_device_id uclogic_devices[] = { ...@@ -402,6 +541,8 @@ static const struct hid_device_id uclogic_devices[] = {
USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) }, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) }, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) },
{ } { }
}; };
MODULE_DEVICE_TABLE(hid, uclogic_devices); MODULE_DEVICE_TABLE(hid, uclogic_devices);
......
...@@ -87,11 +87,13 @@ static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, ...@@ -87,11 +87,13 @@ static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count,
len = list->buffer[list->tail].len > count ? len = list->buffer[list->tail].len > count ?
count : list->buffer[list->tail].len; count : list->buffer[list->tail].len;
if (copy_to_user(buffer, list->buffer[list->tail].value, len)) { if (list->buffer[list->tail].value) {
ret = -EFAULT; if (copy_to_user(buffer, list->buffer[list->tail].value, len)) {
goto out; ret = -EFAULT;
goto out;
}
ret = len;
} }
ret = len;
kfree(list->buffer[list->tail].value); kfree(list->buffer[list->tail].value);
list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1); list->tail = (list->tail + 1) & (HIDRAW_BUFFER_SIZE - 1);
...@@ -437,19 +439,24 @@ static const struct file_operations hidraw_ops = { ...@@ -437,19 +439,24 @@ static const struct file_operations hidraw_ops = {
.llseek = noop_llseek, .llseek = noop_llseek,
}; };
void hidraw_report_event(struct hid_device *hid, u8 *data, int len) int hidraw_report_event(struct hid_device *hid, u8 *data, int len)
{ {
struct hidraw *dev = hid->hidraw; struct hidraw *dev = hid->hidraw;
struct hidraw_list *list; struct hidraw_list *list;
int ret = 0;
list_for_each_entry(list, &dev->list, node) { list_for_each_entry(list, &dev->list, node) {
list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC); if (!(list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC))) {
ret = -ENOMEM;
break;
}
list->buffer[list->head].len = len; list->buffer[list->head].len = len;
list->head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1); list->head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1);
kill_fasync(&list->fasync, SIGIO, POLL_IN); kill_fasync(&list->fasync, SIGIO, POLL_IN);
} }
wake_up_interruptible(&dev->wait); wake_up_interruptible(&dev->wait);
return ret;
} }
EXPORT_SYMBOL_GPL(hidraw_report_event); EXPORT_SYMBOL_GPL(hidraw_report_event);
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <linux/input.h> #include <linux/input.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/string.h>
#include <linux/usb.h> #include <linux/usb.h>
...@@ -86,8 +87,13 @@ static int hid_start_in(struct hid_device *hid) ...@@ -86,8 +87,13 @@ static int hid_start_in(struct hid_device *hid)
!test_bit(HID_REPORTED_IDLE, &usbhid->iofl) && !test_bit(HID_REPORTED_IDLE, &usbhid->iofl) &&
!test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) { !test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) {
rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC); rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC);
if (rc != 0) if (rc != 0) {
clear_bit(HID_IN_RUNNING, &usbhid->iofl); clear_bit(HID_IN_RUNNING, &usbhid->iofl);
if (rc == -ENOSPC)
set_bit(HID_NO_BANDWIDTH, &usbhid->iofl);
} else {
clear_bit(HID_NO_BANDWIDTH, &usbhid->iofl);
}
} }
spin_unlock_irqrestore(&usbhid->lock, flags); spin_unlock_irqrestore(&usbhid->lock, flags);
return rc; return rc;
...@@ -173,8 +179,10 @@ static void hid_io_error(struct hid_device *hid) ...@@ -173,8 +179,10 @@ static void hid_io_error(struct hid_device *hid)
if (time_after(jiffies, usbhid->stop_retry)) { if (time_after(jiffies, usbhid->stop_retry)) {
/* Retries failed, so do a port reset */ /* Retries failed, so do a port reset unless we lack bandwidth*/
if (!test_and_set_bit(HID_RESET_PENDING, &usbhid->iofl)) { if (test_bit(HID_NO_BANDWIDTH, &usbhid->iofl)
&& !test_and_set_bit(HID_RESET_PENDING, &usbhid->iofl)) {
schedule_work(&usbhid->reset_work); schedule_work(&usbhid->reset_work);
goto done; goto done;
} }
...@@ -700,7 +708,7 @@ static int hid_get_class_descriptor(struct usb_device *dev, int ifnum, ...@@ -700,7 +708,7 @@ static int hid_get_class_descriptor(struct usb_device *dev, int ifnum,
int usbhid_open(struct hid_device *hid) int usbhid_open(struct hid_device *hid)
{ {
struct usbhid_device *usbhid = hid->driver_data; struct usbhid_device *usbhid = hid->driver_data;
int res; int res = 0;
mutex_lock(&hid_open_mut); mutex_lock(&hid_open_mut);
if (!hid->open++) { if (!hid->open++) {
...@@ -708,17 +716,27 @@ int usbhid_open(struct hid_device *hid) ...@@ -708,17 +716,27 @@ int usbhid_open(struct hid_device *hid)
/* the device must be awake to reliably request remote wakeup */ /* the device must be awake to reliably request remote wakeup */
if (res < 0) { if (res < 0) {
hid->open--; hid->open--;
mutex_unlock(&hid_open_mut); res = -EIO;
return -EIO; goto done;
} }
usbhid->intf->needs_remote_wakeup = 1; usbhid->intf->needs_remote_wakeup = 1;
if (hid_start_in(hid)) res = hid_start_in(hid);
hid_io_error(hid); if (res) {
if (res != -ENOSPC) {
hid_io_error(hid);
res = 0;
} else {
/* no use opening if resources are insufficient */
hid->open--;
res = -EBUSY;
usbhid->intf->needs_remote_wakeup = 0;
}
}
usb_autopm_put_interface(usbhid->intf); usb_autopm_put_interface(usbhid->intf);
} }
done:
mutex_unlock(&hid_open_mut); mutex_unlock(&hid_open_mut);
return 0; return res;
} }
void usbhid_close(struct hid_device *hid) void usbhid_close(struct hid_device *hid)
...@@ -1347,7 +1365,34 @@ static int hid_post_reset(struct usb_interface *intf) ...@@ -1347,7 +1365,34 @@ static int hid_post_reset(struct usb_interface *intf)
struct usb_device *dev = interface_to_usbdev (intf); struct usb_device *dev = interface_to_usbdev (intf);
struct hid_device *hid = usb_get_intfdata(intf); struct hid_device *hid = usb_get_intfdata(intf);
struct usbhid_device *usbhid = hid->driver_data; struct usbhid_device *usbhid = hid->driver_data;
struct usb_host_interface *interface = intf->cur_altsetting;
int status; int status;
char *rdesc;
/* Fetch and examine the HID report descriptor. If this
* has changed, then rebind. Since usbcore's check of the
* configuration descriptors passed, we already know that
* the size of the HID report descriptor has not changed.
*/
rdesc = kmalloc(hid->rsize, GFP_KERNEL);
if (!rdesc) {
dbg_hid("couldn't allocate rdesc memory (post_reset)\n");
return 1;
}
status = hid_get_class_descriptor(dev,
interface->desc.bInterfaceNumber,
HID_DT_REPORT, rdesc, hid->rsize);
if (status < 0) {
dbg_hid("reading report descriptor failed (post_reset)\n");
kfree(rdesc);
return 1;
}
status = memcmp(rdesc, hid->rdesc, hid->rsize);
kfree(rdesc);
if (status != 0) {
dbg_hid("report descriptor changed\n");
return 1;
}
spin_lock_irq(&usbhid->lock); spin_lock_irq(&usbhid->lock);
clear_bit(HID_RESET_PENDING, &usbhid->iofl); clear_bit(HID_RESET_PENDING, &usbhid->iofl);
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include <linux/hid.h> #include <linux/hid.h>
#include <linux/hiddev.h> #include <linux/hiddev.h>
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/vmalloc.h>
#include "usbhid.h" #include "usbhid.h"
#ifdef CONFIG_USB_DYNAMIC_MINORS #ifdef CONFIG_USB_DYNAMIC_MINORS
...@@ -250,13 +251,13 @@ static int hiddev_release(struct inode * inode, struct file * file) ...@@ -250,13 +251,13 @@ static int hiddev_release(struct inode * inode, struct file * file)
} else { } else {
mutex_unlock(&list->hiddev->existancelock); mutex_unlock(&list->hiddev->existancelock);
kfree(list->hiddev); kfree(list->hiddev);
kfree(list); vfree(list);
return 0; return 0;
} }
} }
mutex_unlock(&list->hiddev->existancelock); mutex_unlock(&list->hiddev->existancelock);
kfree(list); vfree(list);
return 0; return 0;
} }
...@@ -278,7 +279,7 @@ static int hiddev_open(struct inode *inode, struct file *file) ...@@ -278,7 +279,7 @@ static int hiddev_open(struct inode *inode, struct file *file)
hid = usb_get_intfdata(intf); hid = usb_get_intfdata(intf);
hiddev = hid->hiddev; hiddev = hid->hiddev;
if (!(list = kzalloc(sizeof(struct hiddev_list), GFP_KERNEL))) if (!(list = vzalloc(sizeof(struct hiddev_list))))
return -ENOMEM; return -ENOMEM;
mutex_init(&list->thread_lock); mutex_init(&list->thread_lock);
list->hiddev = hiddev; list->hiddev = hiddev;
...@@ -322,7 +323,7 @@ static int hiddev_open(struct inode *inode, struct file *file) ...@@ -322,7 +323,7 @@ static int hiddev_open(struct inode *inode, struct file *file)
mutex_unlock(&hiddev->existancelock); mutex_unlock(&hiddev->existancelock);
bail: bail:
file->private_data = NULL; file->private_data = NULL;
kfree(list); vfree(list);
return res; return res;
} }
......
...@@ -55,6 +55,7 @@ struct usb_interface *usbhid_find_interface(int minor); ...@@ -55,6 +55,7 @@ struct usb_interface *usbhid_find_interface(int minor);
#define HID_STARTED 8 #define HID_STARTED 8
#define HID_REPORTED_IDLE 9 #define HID_REPORTED_IDLE 9
#define HID_KEYS_PRESSED 10 #define HID_KEYS_PRESSED 10
#define HID_NO_BANDWIDTH 11
/* /*
* USB-specific HID struct, to be pointed to * USB-specific HID struct, to be pointed to
......
...@@ -896,7 +896,7 @@ static inline int hid_hw_power(struct hid_device *hdev, int level) ...@@ -896,7 +896,7 @@ static inline int hid_hw_power(struct hid_device *hdev, int level)
return hdev->ll_driver->power ? hdev->ll_driver->power(hdev, level) : 0; return hdev->ll_driver->power ? hdev->ll_driver->power(hdev, level) : 0;
} }
void hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
int interrupt); int interrupt);
extern int hid_generic_init(void); extern int hid_generic_init(void);
......
...@@ -76,13 +76,13 @@ struct hidraw_list { ...@@ -76,13 +76,13 @@ struct hidraw_list {
#ifdef CONFIG_HIDRAW #ifdef CONFIG_HIDRAW
int hidraw_init(void); int hidraw_init(void);
void hidraw_exit(void); void hidraw_exit(void);
void hidraw_report_event(struct hid_device *, u8 *, int); int hidraw_report_event(struct hid_device *, u8 *, int);
int hidraw_connect(struct hid_device *); int hidraw_connect(struct hid_device *);
void hidraw_disconnect(struct hid_device *); void hidraw_disconnect(struct hid_device *);
#else #else
static inline int hidraw_init(void) { return 0; } static inline int hidraw_init(void) { return 0; }
static inline void hidraw_exit(void) { } static inline void hidraw_exit(void) { }
static inline void hidraw_report_event(struct hid_device *hid, u8 *data, int len) { } static inline int hidraw_report_event(struct hid_device *hid, u8 *data, int len) { return 0; }
static inline int hidraw_connect(struct hid_device *hid) { return -1; } static inline int hidraw_connect(struct hid_device *hid) { return -1; }
static inline void hidraw_disconnect(struct hid_device *hid) { } static inline void hidraw_disconnect(struct hid_device *hid) { }
#endif #endif
......
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