summaryrefslogtreecommitdiffstats
path: root/drivers/hid
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/hid-a4tech.c21
-rw-r--r--drivers/hid/hid-apple.c22
-rw-r--r--drivers/hid/hid-core.c142
-rw-r--r--drivers/hid/hid-ids.h5
-rw-r--r--drivers/hid/hid-input.c92
-rw-r--r--drivers/hid/hid-logitech-dj.c55
-rw-r--r--drivers/hid/hid-logitech-dj.h1
-rw-r--r--drivers/hid/hid-magicmouse.c17
-rw-r--r--drivers/hid/hid-multitouch.c107
-rw-r--r--drivers/hid/hid-ntrig.c3
-rw-r--r--drivers/hid/hid-picolcd_cir.c3
-rw-r--r--drivers/hid/hid-picolcd_core.c2
-rw-r--r--drivers/hid/hid-picolcd_fb.c6
-rw-r--r--drivers/hid/hid-pl.c10
-rw-r--r--drivers/hid/hid-roccat-konepure.c3
-rw-r--r--drivers/hid/hid-sensor-hub.c5
-rw-r--r--drivers/hid/hid-sony.c12
-rw-r--r--drivers/hid/hid-speedlink.c11
-rw-r--r--drivers/hid/hid-xinmo.c13
-rw-r--r--drivers/hid/hid-zydacron.c19
-rw-r--r--drivers/hid/hidraw.c82
-rw-r--r--drivers/hid/i2c-hid/i2c-hid.c73
-rw-r--r--drivers/hid/uhid.c26
-rw-r--r--drivers/hid/usbhid/hid-core.c78
-rw-r--r--drivers/hid/usbhid/hid-quirks.c2
-rw-r--r--drivers/hid/usbhid/usbhid.h3
26 files changed, 449 insertions, 364 deletions
diff --git a/drivers/hid/hid-a4tech.c b/drivers/hid/hid-a4tech.c
index 7c5507e..9428ea7 100644
--- a/drivers/hid/hid-a4tech.c
+++ b/drivers/hid/hid-a4tech.c
@@ -90,11 +90,10 @@ static int a4_probe(struct hid_device *hdev, const struct hid_device_id *id)
struct a4tech_sc *a4;
int ret;
- a4 = kzalloc(sizeof(*a4), GFP_KERNEL);
+ a4 = devm_kzalloc(&hdev->dev, sizeof(*a4), GFP_KERNEL);
if (a4 == NULL) {
hid_err(hdev, "can't alloc device descriptor\n");
- ret = -ENOMEM;
- goto err_free;
+ return -ENOMEM;
}
a4->quirks = id->driver_data;
@@ -104,27 +103,16 @@ static int a4_probe(struct hid_device *hdev, const struct hid_device_id *id)
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "parse failed\n");
- goto err_free;
+ return ret;
}
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret) {
hid_err(hdev, "hw start failed\n");
- goto err_free;
+ return ret;
}
return 0;
-err_free:
- kfree(a4);
- return ret;
-}
-
-static void a4_remove(struct hid_device *hdev)
-{
- struct a4tech_sc *a4 = hid_get_drvdata(hdev);
-
- hid_hw_stop(hdev);
- kfree(a4);
}
static const struct hid_device_id a4_devices[] = {
@@ -144,7 +132,6 @@ static struct hid_driver a4_driver = {
.input_mapped = a4_input_mapped,
.event = a4_event,
.probe = a4_probe,
- .remove = a4_remove,
};
module_hid_driver(a4_driver);
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index feae88b..881cf7b 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -349,7 +349,7 @@ static int apple_probe(struct hid_device *hdev,
unsigned int connect_mask = HID_CONNECT_DEFAULT;
int ret;
- asc = kzalloc(sizeof(*asc), GFP_KERNEL);
+ asc = devm_kzalloc(&hdev->dev, sizeof(*asc), GFP_KERNEL);
if (asc == NULL) {
hid_err(hdev, "can't alloc apple descriptor\n");
return -ENOMEM;
@@ -362,7 +362,7 @@ static int apple_probe(struct hid_device *hdev,
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "parse failed\n");
- goto err_free;
+ return ret;
}
if (quirks & APPLE_HIDDEV)
@@ -373,19 +373,10 @@ static int apple_probe(struct hid_device *hdev,
ret = hid_hw_start(hdev, connect_mask);
if (ret) {
hid_err(hdev, "hw start failed\n");
- goto err_free;
+ return ret;
}
return 0;
-err_free:
- kfree(asc);
- return ret;
-}
-
-static void apple_remove(struct hid_device *hdev)
-{
- hid_hw_stop(hdev);
- kfree(hid_get_drvdata(hdev));
}
static const struct hid_device_id apple_devices[] = {
@@ -524,6 +515,12 @@ static const struct hid_device_id apple_devices[] = {
.driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS),
.driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI),
+ .driver_data = APPLE_HAS_FN },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ISO),
+ .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_JIS),
+ .driver_data = APPLE_HAS_FN | APPLE_RDESC_JIS },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO),
@@ -545,7 +542,6 @@ static struct hid_driver apple_driver = {
.id_table = apple_devices,
.report_fixup = apple_report_fixup,
.probe = apple_probe,
- .remove = apple_remove,
.event = apple_event,
.input_mapping = apple_input_mapping,
.input_mapped = apple_input_mapped,
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 627fea6..2c77854 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -63,6 +63,8 @@ struct hid_report *hid_register_report(struct hid_device *device, unsigned type,
struct hid_report_enum *report_enum = device->report_enum + type;
struct hid_report *report;
+ if (id >= HID_MAX_IDS)
+ return NULL;
if (report_enum->report_id_hash[id])
return report_enum->report_id_hash[id];
@@ -404,8 +406,10 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
case HID_GLOBAL_ITEM_TAG_REPORT_ID:
parser->global.report_id = item_udata(item);
- if (parser->global.report_id == 0) {
- hid_err(parser->device, "report_id 0 is invalid\n");
+ if (parser->global.report_id == 0 ||
+ parser->global.report_id >= HID_MAX_IDS) {
+ hid_err(parser->device, "report_id %u is invalid\n",
+ parser->global.report_id);
return -1;
}
return 0;
@@ -575,7 +579,7 @@ static void hid_close_report(struct hid_device *device)
for (i = 0; i < HID_REPORT_TYPES; i++) {
struct hid_report_enum *report_enum = device->report_enum + i;
- for (j = 0; j < 256; j++) {
+ for (j = 0; j < HID_MAX_IDS; j++) {
struct hid_report *report = report_enum->report_id_hash[j];
if (report)
hid_free_report(report);
@@ -677,12 +681,61 @@ static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item)
return NULL;
}
-static void hid_scan_usage(struct hid_device *hid, u32 usage)
+static void hid_scan_input_usage(struct hid_parser *parser, u32 usage)
{
+ struct hid_device *hid = parser->device;
+
if (usage == HID_DG_CONTACTID)
hid->group = HID_GROUP_MULTITOUCH;
}
+static void hid_scan_feature_usage(struct hid_parser *parser, u32 usage)
+{
+ if (usage == 0xff0000c5 && parser->global.report_count == 256 &&
+ parser->global.report_size == 8)
+ parser->scan_flags |= HID_SCAN_FLAG_MT_WIN_8;
+}
+
+static void hid_scan_collection(struct hid_parser *parser, unsigned type)
+{
+ struct hid_device *hid = parser->device;
+
+ if (((parser->global.usage_page << 16) == HID_UP_SENSOR) &&
+ type == HID_COLLECTION_PHYSICAL)
+ hid->group = HID_GROUP_SENSOR_HUB;
+}
+
+static int hid_scan_main(struct hid_parser *parser, struct hid_item *item)
+{
+ __u32 data;
+ int i;
+
+ data = item_udata(item);
+
+ switch (item->tag) {
+ case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:
+ hid_scan_collection(parser, data & 0xff);
+ break;
+ case HID_MAIN_ITEM_TAG_END_COLLECTION:
+ break;
+ case HID_MAIN_ITEM_TAG_INPUT:
+ for (i = 0; i < parser->local.usage_index; i++)
+ hid_scan_input_usage(parser, parser->local.usage[i]);
+ break;
+ case HID_MAIN_ITEM_TAG_OUTPUT:
+ break;
+ case HID_MAIN_ITEM_TAG_FEATURE:
+ for (i = 0; i < parser->local.usage_index; i++)
+ hid_scan_feature_usage(parser, parser->local.usage[i]);
+ break;
+ }
+
+ /* Reset the local parser environment */
+ memset(&parser->local, 0, sizeof(parser->local));
+
+ return 0;
+}
+
/*
* Scan a report descriptor before the device is added to the bus.
* Sets device groups and other properties that determine what driver
@@ -690,48 +743,41 @@ static void hid_scan_usage(struct hid_device *hid, u32 usage)
*/
static int hid_scan_report(struct hid_device *hid)
{
- unsigned int page = 0, delim = 0;
+ struct hid_parser *parser;
+ struct hid_item item;
__u8 *start = hid->dev_rdesc;
__u8 *end = start + hid->dev_rsize;
- unsigned int u, u_min = 0, u_max = 0;
- struct hid_item item;
+ static int (*dispatch_type[])(struct hid_parser *parser,
+ struct hid_item *item) = {
+ hid_scan_main,
+ hid_parser_global,
+ hid_parser_local,
+ hid_parser_reserved
+ };
+ parser = vzalloc(sizeof(struct hid_parser));
+ if (!parser)
+ return -ENOMEM;
+
+ parser->device = hid;
hid->group = HID_GROUP_GENERIC;
- while ((start = fetch_item(start, end, &item)) != NULL) {
- if (item.format != HID_ITEM_FORMAT_SHORT)
- return -EINVAL;
- if (item.type == HID_ITEM_TYPE_GLOBAL) {
- if (item.tag == HID_GLOBAL_ITEM_TAG_USAGE_PAGE)
- page = item_udata(&item) << 16;
- } else if (item.type == HID_ITEM_TYPE_LOCAL) {
- if (delim > 1)
- break;
- u = item_udata(&item);
- if (item.size <= 2)
- u += page;
- switch (item.tag) {
- case HID_LOCAL_ITEM_TAG_DELIMITER:
- delim += !!u;
- break;
- case HID_LOCAL_ITEM_TAG_USAGE:
- hid_scan_usage(hid, u);
- break;
- case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
- u_min = u;
- break;
- case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:
- u_max = u;
- for (u = u_min; u <= u_max; u++)
- hid_scan_usage(hid, u);
- break;
- }
- } else if (page == HID_UP_SENSOR &&
- item.type == HID_ITEM_TYPE_MAIN &&
- item.tag == HID_MAIN_ITEM_TAG_BEGIN_COLLECTION &&
- (item_udata(&item) & 0xff) == HID_COLLECTION_PHYSICAL)
- hid->group = HID_GROUP_SENSOR_HUB;
- }
+ /*
+ * The parsing is simpler than the one in hid_open_report() as we should
+ * be robust against hid errors. Those errors will be raised by
+ * hid_open_report() anyway.
+ */
+ while ((start = fetch_item(start, end, &item)) != NULL)
+ dispatch_type[item.type](parser, &item);
+
+ /*
+ * Handle special flags set during scanning.
+ */
+ if ((parser->scan_flags & HID_SCAN_FLAG_MT_WIN_8) &&
+ (hid->group == HID_GROUP_MULTITOUCH))
+ hid->group = HID_GROUP_MULTITOUCH_WIN_8;
+
+ vfree(parser);
return 0;
}
@@ -1169,7 +1215,12 @@ EXPORT_SYMBOL_GPL(hid_alloc_report_buf);
int hid_set_field(struct hid_field *field, unsigned offset, __s32 value)
{
- unsigned size = field->report_size;
+ unsigned size;
+
+ if (!field)
+ return -1;
+
+ size = field->report_size;
hid_dump_input(field->report->device, field->usage + offset, value);
@@ -1564,6 +1615,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_JIS) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS) },
@@ -1694,6 +1748,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE_OPTICAL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_LUA) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
@@ -2208,6 +2263,9 @@ static const struct hid_device_id hid_mouse_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_ISO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING8_JIS) },
{ 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) },
{ }
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 83622f8..e60e8d5 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -135,6 +135,9 @@
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI 0x0255
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO 0x0256
+#define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI 0x0290
+#define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO 0x0291
+#define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS 0x0292
#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
@@ -656,6 +659,7 @@
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_16 0x0012
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_17 0x0013
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_18 0x0014
+#define USB_DEVICE_ID_NTRIG_DUOSENSE 0x1500
#define USB_VENDOR_ID_ONTRAK 0x0a07
#define USB_DEVICE_ID_ONTRAK_ADU100 0x0064
@@ -714,6 +718,7 @@
#define USB_DEVICE_ID_ROCCAT_KONE 0x2ced
#define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51
#define USB_DEVICE_ID_ROCCAT_KONEPURE 0x2dbe
+#define USB_DEVICE_ID_ROCCAT_KONEPURE_OPTICAL 0x2db4
#define USB_DEVICE_ID_ROCCAT_KONEXTD 0x2e22
#define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50
#define USB_DEVICE_ID_ROCCAT_LUA 0x2c2e
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 7480799..b420f4a 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -340,7 +340,7 @@ static int hidinput_get_battery_property(struct power_supply *psy,
{
struct hid_device *dev = container_of(psy, struct hid_device, battery);
int ret = 0;
- __u8 buf[2] = {};
+ __u8 *buf;
switch (prop) {
case POWER_SUPPLY_PROP_PRESENT:
@@ -349,12 +349,19 @@ static int hidinput_get_battery_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_CAPACITY:
+
+ buf = kmalloc(2 * sizeof(__u8), GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ break;
+ }
ret = dev->hid_get_raw_report(dev, dev->battery_report_id,
- buf, sizeof(buf),
+ buf, 2,
dev->battery_report_type);
if (ret != 2) {
ret = -ENODATA;
+ kfree(buf);
break;
}
ret = 0;
@@ -364,6 +371,7 @@ static int hidinput_get_battery_property(struct power_supply *psy,
buf[1] <= dev->battery_max)
val->intval = (100 * (buf[1] - dev->battery_min)) /
(dev->battery_max - dev->battery_min);
+ kfree(buf);
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
@@ -1137,6 +1145,74 @@ unsigned int hidinput_count_leds(struct hid_device *hid)
}
EXPORT_SYMBOL_GPL(hidinput_count_leds);
+static void hidinput_led_worker(struct work_struct *work)
+{
+ struct hid_device *hid = container_of(work, struct hid_device,
+ led_work);
+ struct hid_field *field;
+ struct hid_report *report;
+ int len;
+ __u8 *buf;
+
+ field = hidinput_get_led_field(hid);
+ if (!field)
+ return;
+
+ /*
+ * field->report is accessed unlocked regarding HID core. So there might
+ * be another incoming SET-LED request from user-space, which changes
+ * the LED state while we assemble our outgoing buffer. However, this
+ * doesn't matter as hid_output_report() correctly converts it into a
+ * boolean value no matter what information is currently set on the LED
+ * field (even garbage). So the remote device will always get a valid
+ * request.
+ * And in case we send a wrong value, a next led worker is spawned
+ * for every SET-LED request so the following worker will send the
+ * correct value, guaranteed!
+ */
+
+ report = field->report;
+
+ /* use custom SET_REPORT request if possible (asynchronous) */
+ if (hid->ll_driver->request)
+ return hid->ll_driver->request(hid, report, HID_REQ_SET_REPORT);
+
+ /* fall back to generic raw-output-report */
+ len = ((report->size - 1) >> 3) + 1 + (report->id > 0);
+ buf = kmalloc(len, GFP_KERNEL);
+ if (!buf)
+ return;
+
+ hid_output_report(report, buf);
+ /* synchronous output report */
+ hid->hid_output_raw_report(hid, buf, len, HID_OUTPUT_REPORT);
+ kfree(buf);
+}
+
+static int hidinput_input_event(struct input_dev *dev, unsigned int type,
+ unsigned int code, int value)
+{
+ struct hid_device *hid = input_get_drvdata(dev);
+ struct hid_field *field;
+ int offset;
+
+ if (type == EV_FF)
+ return input_ff_event(dev, type, code, value);
+
+ if (type != EV_LED)
+ return -1;
+
+ if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) {
+ hid_warn(dev, "event field not found\n");
+ return -1;
+ }
+
+ hid_set_field(field, offset, value);
+
+ schedule_work(&hid->led_work);
+ return 0;
+}
+
static int hidinput_open(struct input_dev *dev)
{
struct hid_device *hid = input_get_drvdata(dev);
@@ -1183,7 +1259,10 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid)
}
input_set_drvdata(input_dev, hid);
- input_dev->event = hid->ll_driver->hidinput_input_event;
+ if (hid->ll_driver->hidinput_input_event)
+ input_dev->event = hid->ll_driver->hidinput_input_event;
+ else if (hid->ll_driver->request || hid->hid_output_raw_report)
+ input_dev->event = hidinput_input_event;
input_dev->open = hidinput_open;
input_dev->close = hidinput_close;
input_dev->setkeycode = hidinput_setkeycode;
@@ -1278,6 +1357,7 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
int i, j, k;
INIT_LIST_HEAD(&hid->inputs);
+ INIT_WORK(&hid->led_work, hidinput_led_worker);
if (!force) {
for (i = 0; i < hid->maxcollection; i++) {
@@ -1379,6 +1459,12 @@ void hidinput_disconnect(struct hid_device *hid)
input_unregister_device(hidinput->input);
kfree(hidinput);
}
+
+ /* led_work is spawned by input_dev callbacks, but doesn't access the
+ * parent input_dev at all. Once all input devices are removed, we
+ * know that led_work will never get restarted, so we can cancel it
+ * synchronously and are safe. */
+ cancel_work_sync(&hid->led_work);
}
EXPORT_SYMBOL_GPL(hidinput_disconnect);
diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
index d318222..7800b14 100644
--- a/drivers/hid/hid-logitech-dj.c
+++ b/drivers/hid/hid-logitech-dj.c
@@ -192,6 +192,7 @@ static struct hid_ll_driver logi_dj_ll_driver;
static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf,
size_t count,
unsigned char report_type);
+static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev);
static void logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev *djrcv_dev,
struct dj_report *dj_report)
@@ -232,6 +233,7 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
if (dj_report->report_params[DEVICE_PAIRED_PARAM_SPFUNCTION] &
SPFUNCTION_DEVICE_LIST_EMPTY) {
dbg_hid("%s: device list is empty\n", __func__);
+ djrcv_dev->querying_devices = false;
return;
}
@@ -242,6 +244,12 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
return;
}
+ if (djrcv_dev->paired_dj_devices[dj_report->device_index]) {
+ /* The device is already known. No need to reallocate it. */
+ dbg_hid("%s: device is already known\n", __func__);
+ return;
+ }
+
dj_hiddev = hid_allocate_device();
if (IS_ERR(dj_hiddev)) {
dev_err(&djrcv_hdev->dev, "%s: hid_allocate_device failed\n",
@@ -305,6 +313,7 @@ static void delayedwork_callback(struct work_struct *work)
struct dj_report dj_report;
unsigned long flags;
int count;
+ int retval;
dbg_hid("%s\n", __func__);
@@ -337,6 +346,25 @@ static void delayedwork_callback(struct work_struct *work)
logi_dj_recv_destroy_djhid_device(djrcv_dev, &dj_report);
break;
default:
+ /* A normal report (i. e. not belonging to a pair/unpair notification)
+ * arriving here, means that the report arrived but we did not have a
+ * paired dj_device associated to the report's device_index, this
+ * means that the original "device paired" notification corresponding
+ * to this dj_device never arrived to this driver. The reason is that
+ * hid-core discards all packets coming from a device while probe() is
+ * executing. */
+ if (!djrcv_dev->paired_dj_devices[dj_report.device_index]) {
+ /* ok, we don't know the device, just re-ask the
+ * receiver for the list of connected devices. */
+ retval = logi_dj_recv_query_paired_devices(djrcv_dev);
+ if (!retval) {
+ /* everything went fine, so just leave */
+ break;
+ }
+ dev_err(&djrcv_dev->hdev->dev,
+ "%s:logi_dj_recv_query_paired_devices "
+ "error:%d\n", __func__, retval);
+ }
dbg_hid("%s: unexpected report type\n", __func__);
}
}
@@ -367,6 +395,12 @@ static void logi_dj_recv_forward_null_report(struct dj_receiver_dev *djrcv_dev,
if (!djdev) {
dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
" is NULL, index %d\n", dj_report->device_index);
+ kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report));
+
+ if (schedule_work(&djrcv_dev->work) == 0) {
+ dbg_hid("%s: did not schedule the work item, was already "
+ "queued\n", __func__);
+ }
return;
}
@@ -397,6 +431,12 @@ static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev,
if (dj_device == NULL) {
dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
" is NULL, index %d\n", dj_report->device_index);
+ kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report));
+
+ if (schedule_work(&djrcv_dev->work) == 0) {
+ dbg_hid("%s: did not schedule the work item, was already "
+ "queued\n", __func__);
+ }
return;
}
@@ -444,6 +484,10 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
struct dj_report *dj_report;
int retval;
+ /* no need to protect djrcv_dev->querying_devices */
+ if (djrcv_dev->querying_devices)
+ return 0;
+
dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL);
if (!dj_report)
return -ENOMEM;
@@ -455,6 +499,7 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
return retval;
}
+
static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
unsigned timeout)
{
@@ -764,10 +809,10 @@ static int logi_dj_probe(struct hid_device *hdev,
}
/* This is enabling the polling urb on the IN endpoint */
- retval = hdev->ll_driver->open(hdev);
+ retval = hid_hw_open(hdev);
if (retval < 0) {
- dev_err(&hdev->dev, "%s:hdev->ll_driver->open returned "
- "error:%d\n", __func__, retval);
+ dev_err(&hdev->dev, "%s:hid_hw_open returned error:%d\n",
+ __func__, retval);
goto llopen_failed;
}
@@ -784,7 +829,7 @@ static int logi_dj_probe(struct hid_device *hdev,
return retval;
logi_dj_recv_query_paired_devices_failed:
- hdev->ll_driver->close(hdev);
+ hid_hw_close(hdev);
llopen_failed:
switch_to_dj_mode_fail:
@@ -826,7 +871,7 @@ static void logi_dj_remove(struct hid_device *hdev)
cancel_work_sync(&djrcv_dev->work);
- hdev->ll_driver->close(hdev);
+ hid_hw_close(hdev);
hid_hw_stop(hdev);
/* I suppose that at this point the only context that can access
diff --git a/drivers/hid/hid-logitech-dj.h b/drivers/hid/hid-logitech-dj.h
index fd28a5e..4a40003 100644
--- a/drivers/hid/hid-logitech-dj.h
+++ b/drivers/hid/hid-logitech-dj.h
@@ -101,6 +101,7 @@ struct dj_receiver_dev {
struct work_struct work;
struct kfifo notif_fifo;
spinlock_t lock;
+ bool querying_devices;
};
struct dj_device {
diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
index a32f5a2..3b43d1c 100644
--- a/drivers/hid/hid-magicmouse.c
+++ b/drivers/hid/hid-magicmouse.c
@@ -484,7 +484,7 @@ static int magicmouse_probe(struct hid_device *hdev,
struct hid_report *report;
int ret;
- msc = kzalloc(sizeof(*msc), GFP_KERNEL);
+ msc = devm_kzalloc(&hdev->dev, sizeof(*msc), GFP_KERNEL);
if (msc == NULL) {
hid_err(hdev, "can't alloc magicmouse descriptor\n");
return -ENOMEM;
@@ -498,13 +498,13 @@ static int magicmouse_probe(struct hid_device *hdev,
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "magicmouse hid parse failed\n");
- goto err_free;
+ return ret;
}
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret) {
hid_err(hdev, "magicmouse hw start failed\n");
- goto err_free;
+ return ret;
}
if (!msc->input) {
@@ -548,19 +548,9 @@ static int magicmouse_probe(struct hid_device *hdev,
return 0;
err_stop_hw:
hid_hw_stop(hdev);
-err_free:
- kfree(msc);
return ret;
}
-static void magicmouse_remove(struct hid_device *hdev)
-{
- struct magicmouse_sc *msc = hid_get_drvdata(hdev);
-
- hid_hw_stop(hdev);
- kfree(msc);
-}
-
static const struct hid_device_id magic_mice[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
USB_DEVICE_ID_APPLE_MAGICMOUSE), .driver_data = 0 },
@@ -574,7 +564,6 @@ static struct hid_driver magicmouse_driver = {
.name = "magicmouse",
.id_table = magic_mice,
.probe = magicmouse_probe,
- .remove = magicmouse_remove,
.raw_event = magicmouse_raw_event,
.input_mapping = magicmouse_input_mapping,
.input_configured = magicmouse_input_configured,
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index cb0e361..ac28f08 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -133,6 +133,7 @@ static void mt_post_parse(struct mt_device *td);
#define MT_CLS_NSMU 0x000a
#define MT_CLS_DUAL_CONTACT_NUMBER 0x0010
#define MT_CLS_DUAL_CONTACT_ID 0x0011
+#define MT_CLS_WIN_8 0x0012
/* vendor specific classes */
#define MT_CLS_3M 0x0101
@@ -205,6 +206,11 @@ static struct mt_class mt_classes[] = {
MT_QUIRK_CONTACT_CNT_ACCURATE |
MT_QUIRK_SLOT_IS_CONTACTID,
.maxcontacts = 2 },
+ { .name = MT_CLS_WIN_8,
+ .quirks = MT_QUIRK_ALWAYS_VALID |
+ MT_QUIRK_IGNORE_DUPLICATES |
+ MT_QUIRK_HOVERING |
+ MT_QUIRK_CONTACT_CNT_ACCURATE },
/*
* vendor specific classes
@@ -261,17 +267,6 @@ static struct mt_class mt_classes[] = {
{ }
};
-static void mt_free_input_name(struct hid_input *hi)
-{
- struct hid_device *hdev = hi->report->device;
- const char *name = hi->input->name;
-
- if (name != hdev->name) {
- hi->input->name = hdev->name;
- kfree(name);
- }
-}
-
static ssize_t mt_show_quirks(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -343,19 +338,6 @@ static void mt_feature_mapping(struct hid_device *hdev,
td->maxcontacts = td->mtclass.maxcontacts;
break;
- case 0xff0000c5:
- if (field->report_count == 256 && field->report_size == 8) {
- /* Win 8 devices need special quirks */
- __s32 *quirks = &td->mtclass.quirks;
- *quirks |= MT_QUIRK_ALWAYS_VALID;
- *quirks |= MT_QUIRK_IGNORE_DUPLICATES;
- *quirks |= MT_QUIRK_HOVERING;
- *quirks |= MT_QUIRK_CONTACT_CNT_ACCURATE;
- *quirks &= ~MT_QUIRK_NOT_SEEN_MEANS_UP;
- *quirks &= ~MT_QUIRK_VALID_IS_INRANGE;
- *quirks &= ~MT_QUIRK_VALID_IS_CONFIDENCE;
- }
- break;
}
}
@@ -415,13 +397,6 @@ static void mt_pen_report(struct hid_device *hid, struct hid_report *report)
static void mt_pen_input_configured(struct hid_device *hdev,
struct hid_input *hi)
{
- char *name = kzalloc(strlen(hi->input->name) + 5, GFP_KERNEL);
- if (name) {
- sprintf(name, "%s Pen", hi->input->name);
- mt_free_input_name(hi);
- hi->input->name = name;
- }
-
/* force BTN_STYLUS to allow tablet matching in udev */
__set_bit(BTN_STYLUS, hi->input->keybit);
}
@@ -928,16 +903,26 @@ static void mt_post_parse(struct mt_device *td)
static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
{
struct mt_device *td = hid_get_drvdata(hdev);
- char *name = kstrdup(hdev->name, GFP_KERNEL);
-
- if (name)
- hi->input->name = name;
+ char *name;
+ const char *suffix = NULL;
if (hi->report->id == td->mt_report_id)
mt_touch_input_configured(hdev, hi);
- if (hi->report->id == td->pen_report_id)
+ if (hi->report->field[0]->physical == HID_DG_STYLUS) {
+ suffix = "Pen";
mt_pen_input_configured(hdev, hi);
+ }
+
+ if (suffix) {
+ name = devm_kzalloc(&hi->input->dev,
+ strlen(hdev->name) + strlen(suffix) + 2,
+ GFP_KERNEL);
+ if (name) {
+ sprintf(name, "%s %s", hdev->name, suffix);
+ hi->input->name = name;
+ }
+ }
}
static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
@@ -945,7 +930,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
int ret, i;
struct mt_device *td;
struct mt_class *mtclass = mt_classes; /* MT_CLS_DEFAULT */
- struct hid_input *hi;
for (i = 0; mt_classes[i].name ; i++) {
if (id->driver_data == mt_classes[i].name) {
@@ -967,7 +951,19 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
hdev->quirks |= HID_QUIRK_MULTI_INPUT;
hdev->quirks |= HID_QUIRK_NO_EMPTY_INPUT;
- td = kzalloc(sizeof(struct mt_device), GFP_KERNEL);
+ /*
+ * Handle special quirks for Windows 8 certified devices.
+ */
+ if (id->group == HID_GROUP_MULTITOUCH_WIN_8)
+ /*
+ * Some multitouch screens do not like to be polled for input
+ * reports. Fortunately, the Win8 spec says that all touches
+ * should be sent during each report, making the initialization
+ * of input reports unnecessary.
+ */
+ hdev->quirks |= HID_QUIRK_NO_INIT_INPUT_REPORTS;
+
+ td = devm_kzalloc(&hdev->dev, sizeof(struct mt_device), GFP_KERNEL);
if (!td) {
dev_err(&hdev->dev, "cannot allocate multitouch data\n");
return -ENOMEM;
@@ -980,11 +976,11 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
td->pen_report_id = -1;
hid_set_drvdata(hdev, td);
- td->fields = kzalloc(sizeof(struct mt_fields), GFP_KERNEL);
+ td->fields = devm_kzalloc(&hdev->dev, sizeof(struct mt_fields),
+ GFP_KERNEL);
if (!td->fields) {
dev_err(&hdev->dev, "cannot allocate multitouch fields data\n");
- ret = -ENOMEM;
- goto fail;
+ return -ENOMEM;
}
if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID)
@@ -992,29 +988,22 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
ret = hid_parse(hdev);
if (ret != 0)
- goto fail;
+ return ret;
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret)
- goto hid_fail;
+ return ret;
ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group);
mt_set_maxcontacts(hdev);
mt_set_input_mode(hdev);
- kfree(td->fields);
+ /* release .fields memory as it is not used anymore */
+ devm_kfree(&hdev->dev, td->fields);
td->fields = NULL;
return 0;
-
-hid_fail:
- list_for_each_entry(hi, &hdev->inputs, list)
- mt_free_input_name(hi);
-fail:
- kfree(td->fields);
- kfree(td);
- return ret;
}
#ifdef CONFIG_PM
@@ -1039,17 +1028,8 @@ static int mt_resume(struct hid_device *hdev)
static void mt_remove(struct hid_device *hdev)
{
- struct mt_device *td = hid_get_drvdata(hdev);
- struct hid_input *hi;
-
sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
- list_for_each_entry(hi, &hdev->inputs, list)
- mt_free_input_name(hi);
-
hid_hw_stop(hdev);
-
- kfree(td);
- hid_set_drvdata(hdev, NULL);
}
static const struct hid_device_id mt_devices[] = {
@@ -1371,6 +1351,11 @@ static const struct hid_device_id mt_devices[] = {
/* Generic MT device */
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH, HID_ANY_ID, HID_ANY_ID) },
+
+ /* Generic Win 8 certified MT device */
+ { .driver_data = MT_CLS_WIN_8,
+ HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH_WIN_8,
+ HID_ANY_ID, HID_ANY_ID) },
{ }
};
MODULE_DEVICE_TABLE(hid, mt_devices);
diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c
index 98d1fdf..600f207 100644
--- a/drivers/hid/hid-ntrig.c
+++ b/drivers/hid/hid-ntrig.c
@@ -115,7 +115,8 @@ static inline int ntrig_get_mode(struct hid_device *hdev)
struct hid_report *report = hdev->report_enum[HID_FEATURE_REPORT].
report_id_hash[0x0d];
- if (!report)
+ if (!report || report->maxfield < 1 ||
+ report->field[0]->report_count < 1)
return -EINVAL;
hid_hw_request(hdev, report, HID_REQ_GET_REPORT);
diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c
index e346038..59d5eb1 100644
--- a/drivers/hid/hid-picolcd_cir.c
+++ b/drivers/hid/hid-picolcd_cir.c
@@ -145,6 +145,7 @@ void picolcd_exit_cir(struct picolcd_data *data)
struct rc_dev *rdev = data->rc_dev;
data->rc_dev = NULL;
- rc_unregister_device(rdev);
+ if (rdev)
+ rc_unregister_device(rdev);
}
diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c
index b48092d..acbb0210 100644
--- a/drivers/hid/hid-picolcd_core.c
+++ b/drivers/hid/hid-picolcd_core.c
@@ -290,7 +290,7 @@ static ssize_t picolcd_operation_mode_store(struct device *dev,
buf += 10;
cnt -= 10;
}
- if (!report)
+ if (!report || report->maxfield != 1)
return -EINVAL;
while (cnt > 0 && (buf[cnt-1] == '\n' || buf[cnt-1] == '\r'))
diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c
index 591f6b2..c930ab8 100644
--- a/drivers/hid/hid-picolcd_fb.c
+++ b/drivers/hid/hid-picolcd_fb.c
@@ -593,10 +593,14 @@ err_nomem:
void picolcd_exit_framebuffer(struct picolcd_data *data)
{
struct fb_info *info = data->fb_info;
- struct picolcd_fb_data *fbdata = info->par;
+ struct picolcd_fb_data *fbdata;
unsigned long flags;
+ if (!info)
+ return;
+
device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate);
+ fbdata = info->par;
/* disconnect framebuffer from HID dev */
spin_lock_irqsave(&fbdata->lock, flags);
diff --git a/drivers/hid/hid-pl.c b/drivers/hid/hid-pl.c
index d29112f..2dcd7d9 100644
--- a/drivers/hid/hid-pl.c
+++ b/drivers/hid/hid-pl.c
@@ -132,8 +132,14 @@ static int plff_init(struct hid_device *hid)
strong = &report->field[0]->value[2];
weak = &report->field[0]->value[3];
debug("detected single-field device");
- } else if (report->maxfield >= 4 && report->field[0]->maxusage == 1 &&
- report->field[0]->usage[0].hid == (HID_UP_LED | 0x43)) {
+ } else if (report->field[0]->maxusage == 1 &&
+ report->field[0]->usage[0].hid ==
+ (HID_UP_LED | 0x43) &&
+ report->maxfield >= 4 &&
+ report->field[0]->report_count >= 1 &&
+ report->field[1]->report_count >= 1 &&
+ report->field[2]->report_count >= 1 &&
+ report->field[3]->report_count >= 1) {
report->field[0]->value[0] = 0x00;
report->field[1]->value[0] = 0x00;
strong = &report->field[2]->value[0];
diff --git a/drivers/hid/hid-roccat-konepure.c b/drivers/hid/hid-roccat-konepure.c
index c79d0b0..5850959 100644
--- a/drivers/hid/hid-roccat-konepure.c
+++ b/drivers/hid/hid-roccat-konepure.c
@@ -262,6 +262,7 @@ static int konepure_raw_event(struct hid_device *hdev,
static const struct hid_device_id konepure_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPURE_OPTICAL) },
{ }
};
@@ -300,5 +301,5 @@ module_init(konepure_init);
module_exit(konepure_exit);
MODULE_AUTHOR("Stefan Achatz");
-MODULE_DESCRIPTION("USB Roccat KonePure driver");
+MODULE_DESCRIPTION("USB Roccat KonePure/Optical driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c
index ca749810..6fca30e 100644
--- a/drivers/hid/hid-sensor-hub.c
+++ b/drivers/hid/hid-sensor-hub.c
@@ -221,7 +221,8 @@ int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
mutex_lock(&data->mutex);
report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT);
- if (!report || (field_index >= report->maxfield)) {
+ if (!report || (field_index >= report->maxfield) ||
+ report->field[field_index]->report_count < 1) {
ret = -EINVAL;
goto done_proc;
}
@@ -416,7 +417,7 @@ static int sensor_hub_raw_event(struct hid_device *hdev,
return 1;
ptr = raw_data;
- ptr++; /*Skip report id*/
+ ptr++; /* Skip report id */
spin_lock_irqsave(&pdata->lock, flags);
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index ecbc749..30dbb6b 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -369,7 +369,8 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi,
if (sc->quirks & PS3REMOTE)
return ps3remote_mapping(hdev, hi, field, usage, bit, max);
- return -1;
+ /* Let hid-core decide for the others */
+ return 0;
}
/*
@@ -623,7 +624,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
struct sony_sc *sc;
unsigned int connect_mask = HID_CONNECT_DEFAULT;
- sc = kzalloc(sizeof(*sc), GFP_KERNEL);
+ sc = devm_kzalloc(&hdev->dev, sizeof(*sc), GFP_KERNEL);
if (sc == NULL) {
hid_err(hdev, "can't alloc sony descriptor\n");
return -ENOMEM;
@@ -635,7 +636,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "parse failed\n");
- goto err_free;
+ return ret;
}
if (sc->quirks & VAIO_RDESC_CONSTANT)
@@ -648,7 +649,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
ret = hid_hw_start(hdev, connect_mask);
if (ret) {
hid_err(hdev, "hw start failed\n");
- goto err_free;
+ return ret;
}
if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
@@ -668,8 +669,6 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
return 0;
err_stop:
hid_hw_stop(hdev);
-err_free:
- kfree(sc);
return ret;
}
@@ -681,7 +680,6 @@ static void sony_remove(struct hid_device *hdev)
buzz_remove(hdev);
hid_hw_stop(hdev);
- kfree(sc);
}
static const struct hid_device_id sony_devices[] = {
diff --git a/drivers/hid/hid-speedlink.c b/drivers/hid/hid-speedlink.c
index a2f587d..7112f3e 100644
--- a/drivers/hid/hid-speedlink.c
+++ b/drivers/hid/hid-speedlink.c
@@ -3,7 +3,7 @@
* Fixes "jumpy" cursor and removes nonexistent keyboard LEDS from
* the HID descriptor.
*
- * Copyright (c) 2011 Stefan Kriwanek <mail@stefankriwanek.de>
+ * Copyright (c) 2011, 2013 Stefan Kriwanek <dev@stefankriwanek.de>
*/
/*
@@ -46,8 +46,13 @@ static int speedlink_event(struct hid_device *hdev, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
/* No other conditions due to usage_table. */
- /* Fix "jumpy" cursor (invalid events sent by device). */
- if (value == 256)
+
+ /* This fixes the "jumpy" cursor occuring due to invalid events sent
+ * by the device. Some devices only send them with value==+256, others
+ * don't. However, catching abs(value)>=256 is restrictive enough not
+ * to interfere with devices that were bug-free (has been tested).
+ */
+ if (abs(value) >= 256)
return 1;
/* Drop useless distance 0 events (on button clicks etc.) as well */
if (value == 0)
diff --git a/drivers/hid/hid-xinmo.c b/drivers/hid/hid-xinmo.c
index 6153e50..7df5227 100644
--- a/drivers/hid/hid-xinmo.c
+++ b/drivers/hid/hid-xinmo.c
@@ -57,16 +57,5 @@ static struct hid_driver xinmo_driver = {
.event = xinmo_event
};
-static int __init xinmo_init(void)
-{
- return hid_register_driver(&xinmo_driver);
-}
-
-static void __exit xinmo_exit(void)
-{
- hid_unregister_driver(&xinmo_driver);
-}
-
-module_init(xinmo_init);
-module_exit(xinmo_exit);
+module_hid_driver(xinmo_driver);
MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-zydacron.c b/drivers/hid/hid-zydacron.c
index e4cddec..1a660bd 100644
--- a/drivers/hid/hid-zydacron.c
+++ b/drivers/hid/hid-zydacron.c
@@ -169,7 +169,7 @@ static int zc_probe(struct hid_device *hdev, const struct hid_device_id *id)
int ret;
struct zc_device *zc;
- zc = kzalloc(sizeof(*zc), GFP_KERNEL);
+ zc = devm_kzalloc(&hdev->dev, sizeof(*zc), GFP_KERNEL);
if (zc == NULL) {
hid_err(hdev, "can't alloc descriptor\n");
return -ENOMEM;
@@ -180,28 +180,16 @@ static int zc_probe(struct hid_device *hdev, const struct hid_device_id *id)
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "parse failed\n");
- goto err_free;
+ return ret;
}
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret) {
hid_err(hdev, "hw start failed\n");
- goto err_free;
+ return ret;
}
return 0;
-err_free:
- kfree(zc);
-
- return ret;
-}
-
-static void zc_remove(struct hid_device *hdev)
-{
- struct zc_device *zc = hid_get_drvdata(hdev);
-
- hid_hw_stop(hdev);
- kfree(zc);
}
static const struct hid_device_id zc_devices[] = {
@@ -217,7 +205,6 @@ static struct hid_driver zc_driver = {
.input_mapping = zc_input_mapping,
.raw_event = zc_raw_event,
.probe = zc_probe,
- .remove = zc_remove,
};
module_hid_driver(zc_driver);
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index a745163..8918dd1 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -113,7 +113,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
__u8 *buf;
int ret = 0;
- if (!hidraw_table[minor]) {
+ if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
ret = -ENODEV;
goto out;
}
@@ -253,6 +253,7 @@ static int hidraw_open(struct inode *inode, struct file *file)
unsigned int minor = iminor(inode);
struct hidraw *dev;
struct hidraw_list *list;
+ unsigned long flags;
int err = 0;
if (!(list = kzalloc(sizeof(struct hidraw_list), GFP_KERNEL))) {
@@ -261,16 +262,11 @@ static int hidraw_open(struct inode *inode, struct file *file)
}
mutex_lock(&minors_lock);
- if (!hidraw_table[minor]) {
+ if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
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++) {
err = hid_hw_power(dev->hid, PM_HINT_FULLON);
@@ -283,9 +279,16 @@ static int hidraw_open(struct inode *inode, struct file *file)
if (err < 0) {
hid_hw_power(dev->hid, PM_HINT_NORMAL);
dev->open--;
+ goto out_unlock;
}
}
+ list->hidraw = hidraw_table[minor];
+ mutex_init(&list->read_mutex);
+ spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags);
+ list_add_tail(&list->node, &hidraw_table[minor]->list);
+ spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags);
+ file->private_data = list;
out_unlock:
mutex_unlock(&minors_lock);
out:
@@ -302,39 +305,41 @@ static int hidraw_fasync(int fd, struct file *file, int on)
return fasync_helper(fd, file, on, &list->fasync);
}
+static void drop_ref(struct hidraw *hidraw, int exists_bit)
+{
+ if (exists_bit) {
+ hid_hw_close(hidraw->hid);
+ hidraw->exist = 0;
+ if (hidraw->open)
+ wake_up_interruptible(&hidraw->wait);
+ } else {
+ --hidraw->open;
+ }
+
+ if (!hidraw->open && !hidraw->exist) {
+ device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
+ hidraw_table[hidraw->minor] = NULL;
+ kfree(hidraw);
+ }
+}
+
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;
- int ret;
- int i;
+ unsigned long flags;
mutex_lock(&minors_lock);
- if (!hidraw_table[minor]) {
- ret = -ENODEV;
- goto unlock;
- }
+ spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags);
list_del(&list->node);
- dev = hidraw_table[minor];
- if (!--dev->open) {
- if (list->hidraw->exist) {
- hid_hw_power(dev->hid, PM_HINT_NORMAL);
- hid_hw_close(dev->hid);
- } else {
- kfree(list->hidraw);
- }
- }
-
- for (i = 0; i < HIDRAW_BUFFER_SIZE; ++i)
- kfree(list->buffer[i].value);
+ spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags);
kfree(list);
- ret = 0;
-unlock:
- mutex_unlock(&minors_lock);
- return ret;
+ drop_ref(hidraw_table[minor], 0);
+
+ mutex_unlock(&minors_lock);
+ return 0;
}
static long hidraw_ioctl(struct file *file, unsigned int cmd,
@@ -457,7 +462,9 @@ int hidraw_report_event(struct hid_device *hid, u8 *data, int len)
struct hidraw *dev = hid->hidraw;
struct hidraw_list *list;
int ret = 0;
+ unsigned long flags;
+ spin_lock_irqsave(&dev->list_lock, flags);
list_for_each_entry(list, &dev->list, node) {
int new_head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1);
@@ -472,6 +479,7 @@ int hidraw_report_event(struct hid_device *hid, u8 *data, int len)
list->head = new_head;
kill_fasync(&list->fasync, SIGIO, POLL_IN);
}
+ spin_unlock_irqrestore(&dev->list_lock, flags);
wake_up_interruptible(&dev->wait);
return ret;
@@ -518,8 +526,8 @@ int hidraw_connect(struct hid_device *hid)
goto out;
}
- mutex_unlock(&minors_lock);
init_waitqueue_head(&dev->wait);
+ spin_lock_init(&dev->list_lock);
INIT_LIST_HEAD(&dev->list);
dev->hid = hid;
@@ -528,6 +536,7 @@ int hidraw_connect(struct hid_device *hid)
dev->exist = 1;
hid->hidraw = dev;
+ mutex_unlock(&minors_lock);
out:
return result;
@@ -539,18 +548,9 @@ void hidraw_disconnect(struct hid_device *hid)
struct hidraw *hidraw = hid->hidraw;
mutex_lock(&minors_lock);
- hidraw->exist = 0;
- device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
+ drop_ref(hidraw, 1);
- hidraw_table[hidraw->minor] = NULL;
-
- if (hidraw->open) {
- hid_hw_close(hid);
- wake_up_interruptible(&hidraw->wait);
- } else {
- kfree(hidraw);
- }
mutex_unlock(&minors_lock);
}
EXPORT_SYMBOL_GPL(hidraw_disconnect);
diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
index 3cb7d96..c133619 100644
--- a/drivers/hid/i2c-hid/i2c-hid.c
+++ b/drivers/hid/i2c-hid/i2c-hid.c
@@ -35,6 +35,7 @@
#include <linux/hid.h>
#include <linux/mutex.h>
#include <linux/acpi.h>
+#include <linux/of.h>
#include <linux/i2c/i2c-hid.h>
@@ -756,29 +757,6 @@ static int i2c_hid_power(struct hid_device *hid, int lvl)
return ret;
}
-static int i2c_hid_hidinput_input_event(struct input_dev *dev,
- unsigned int type, unsigned int code, int value)
-{
- struct hid_device *hid = input_get_drvdata(dev);
- struct hid_field *field;
- int offset;
-
- if (type == EV_FF)
- return input_ff_event(dev, type, code, value);
-
- if (type != EV_LED)
- return -1;
-
- offset = hidinput_find_field(hid, type, code, &field);
-
- if (offset == -1) {
- hid_warn(dev, "event field not found\n");
- return -1;
- }
-
- return hid_set_field(field, offset, value);
-}
-
static struct hid_ll_driver i2c_hid_ll_driver = {
.parse = i2c_hid_parse,
.start = i2c_hid_start,
@@ -787,7 +765,6 @@ static struct hid_ll_driver i2c_hid_ll_driver = {
.close = i2c_hid_close,
.power = i2c_hid_power,
.request = i2c_hid_request,
- .hidinput_input_event = i2c_hid_hidinput_input_event,
};
static int i2c_hid_init_irq(struct i2c_client *client)
@@ -897,8 +874,9 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client,
params[1].integer.value = 1;
params[2].type = ACPI_TYPE_INTEGER;
params[2].integer.value = 1; /* HID function */
- params[3].type = ACPI_TYPE_INTEGER;
- params[3].integer.value = 0;
+ params[3].type = ACPI_TYPE_PACKAGE;
+ params[3].package.count = 0;
+ params[3].package.elements = NULL;
if (ACPI_FAILURE(acpi_evaluate_object(handle, "_DSM", &input, &buf))) {
dev_err(&client->dev, "device _DSM execution failed\n");
@@ -933,6 +911,42 @@ static inline int i2c_hid_acpi_pdata(struct i2c_client *client,
}
#endif
+#ifdef CONFIG_OF
+static int i2c_hid_of_probe(struct i2c_client *client,
+ struct i2c_hid_platform_data *pdata)
+{
+ struct device *dev = &client->dev;
+ u32 val;
+ int ret;
+
+ ret = of_property_read_u32(dev->of_node, "hid-descr-addr", &val);
+ if (ret) {
+ dev_err(&client->dev, "HID register address not provided\n");
+ return -ENODEV;
+ }
+ if (val >> 16) {
+ dev_err(&client->dev, "Bad HID register address: 0x%08x\n",
+ val);
+ return -EINVAL;
+ }
+ pdata->hid_descriptor_address = val;
+
+ return 0;
+}
+
+static const struct of_device_id i2c_hid_of_match[] = {
+ { .compatible = "hid-over-i2c" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, i2c_hid_of_match);
+#else
+static inline int i2c_hid_of_probe(struct i2c_client *client,
+ struct i2c_hid_platform_data *pdata)
+{
+ return -ENODEV;
+}
+#endif
+
static int i2c_hid_probe(struct i2c_client *client,
const struct i2c_device_id *dev_id)
{
@@ -954,7 +968,11 @@ static int i2c_hid_probe(struct i2c_client *client,
if (!ihid)
return -ENOMEM;
- if (!platform_data) {
+ if (client->dev.of_node) {
+ ret = i2c_hid_of_probe(client, &ihid->pdata);
+ if (ret)
+ goto err;
+ } else if (!platform_data) {
ret = i2c_hid_acpi_pdata(client, &ihid->pdata);
if (ret) {
dev_err(&client->dev,
@@ -1095,6 +1113,7 @@ static struct i2c_driver i2c_hid_driver = {
.owner = THIS_MODULE,
.pm = &i2c_hid_pm,
.acpi_match_table = ACPI_PTR(i2c_hid_acpi_match),
+ .of_match_table = of_match_ptr(i2c_hid_of_match),
},
.probe = i2c_hid_probe,
diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c
index fc307e04..5bf2fb7 100644
--- a/drivers/hid/uhid.c
+++ b/drivers/hid/uhid.c
@@ -116,30 +116,6 @@ static void uhid_hid_close(struct hid_device *hid)
uhid_queue_event(uhid, UHID_CLOSE);
}
-static int uhid_hid_input(struct input_dev *input, unsigned int type,
- unsigned int code, int value)
-{
- struct hid_device *hid = input_get_drvdata(input);
- struct uhid_device *uhid = hid->driver_data;
- unsigned long flags;
- struct uhid_event *ev;
-
- ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
- if (!ev)
- return -ENOMEM;
-
- ev->type = UHID_OUTPUT_EV;
- ev->u.output_ev.type = type;
- ev->u.output_ev.code = code;
- ev->u.output_ev.value = value;
-
- spin_lock_irqsave(&uhid->qlock, flags);
- uhid_queue(uhid, ev);
- spin_unlock_irqrestore(&uhid->qlock, flags);
-
- return 0;
-}
-
static int uhid_hid_parse(struct hid_device *hid)
{
struct uhid_device *uhid = hid->driver_data;
@@ -273,7 +249,6 @@ static struct hid_ll_driver uhid_hid_driver = {
.stop = uhid_hid_stop,
.open = uhid_hid_open,
.close = uhid_hid_close,
- .hidinput_input_event = uhid_hid_input,
.parse = uhid_hid_parse,
};
@@ -659,3 +634,4 @@ module_exit(uhid_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Herrmann <dh.herrmann@gmail.com>");
MODULE_DESCRIPTION("User-space I/O driver support for HID subsystem");
+MODULE_ALIAS("devname:" UHID_NAME);
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index ada164e..44df131 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -648,62 +648,6 @@ static void usbhid_submit_report(struct hid_device *hid, struct hid_report *repo
spin_unlock_irqrestore(&usbhid->lock, flags);
}
-/* Workqueue routine to send requests to change LEDs */
-static void hid_led(struct work_struct *work)
-{
- struct usbhid_device *usbhid =
- container_of(work, struct usbhid_device, led_work);
- struct hid_device *hid = usbhid->hid;
- struct hid_field *field;
- unsigned long flags;
-
- field = hidinput_get_led_field(hid);
- if (!field) {
- hid_warn(hid, "LED event field not found\n");
- return;
- }
-
- spin_lock_irqsave(&usbhid->lock, flags);
- if (!test_bit(HID_DISCONNECTED, &usbhid->iofl)) {
- usbhid->ledcount = hidinput_count_leds(hid);
- hid_dbg(usbhid->hid, "New ledcount = %u\n", usbhid->ledcount);
- __usbhid_submit_report(hid, field->report, USB_DIR_OUT);
- }
- spin_unlock_irqrestore(&usbhid->lock, flags);
-}
-
-static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
-{
- struct hid_device *hid = input_get_drvdata(dev);
- struct usbhid_device *usbhid = hid->driver_data;
- struct hid_field *field;
- unsigned long flags;
- int offset;
-
- if (type == EV_FF)
- return input_ff_event(dev, type, code, value);
-
- if (type != EV_LED)
- return -1;
-
- if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) {
- hid_warn(dev, "event field not found\n");
- return -1;
- }
-
- spin_lock_irqsave(&usbhid->lock, flags);
- hid_set_field(field, offset, value);
- spin_unlock_irqrestore(&usbhid->lock, flags);
-
- /*
- * Defer performing requested LED action.
- * This is more likely gather all LED changes into a single URB.
- */
- schedule_work(&usbhid->led_work);
-
- return 0;
-}
-
static int usbhid_wait_io(struct hid_device *hid)
{
struct usbhid_device *usbhid = hid->driver_data;
@@ -806,12 +750,17 @@ void usbhid_init_reports(struct hid_device *hid)
{
struct hid_report *report;
struct usbhid_device *usbhid = hid->driver_data;
+ struct hid_report_enum *report_enum;
int err, ret;
- list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list)
- usbhid_submit_report(hid, report, USB_DIR_IN);
+ if (!(hid->quirks & HID_QUIRK_NO_INIT_INPUT_REPORTS)) {
+ report_enum = &hid->report_enum[HID_INPUT_REPORT];
+ list_for_each_entry(report, &report_enum->report_list, list)
+ usbhid_submit_report(hid, report, USB_DIR_IN);
+ }
- list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
+ report_enum = &hid->report_enum[HID_FEATURE_REPORT];
+ list_for_each_entry(report, &report_enum->report_list, list)
usbhid_submit_report(hid, report, USB_DIR_IN);
err = 0;
@@ -856,7 +805,7 @@ static int hid_find_field_early(struct hid_device *hid, unsigned int page,
return -1;
}
-void usbhid_set_leds(struct hid_device *hid)
+static void usbhid_set_leds(struct hid_device *hid)
{
struct hid_field *field;
int offset;
@@ -866,7 +815,6 @@ void usbhid_set_leds(struct hid_device *hid)
usbhid_submit_report(hid, field->report, USB_DIR_OUT);
}
}
-EXPORT_SYMBOL_GPL(usbhid_set_leds);
/*
* Traverse the supplied list of reports and find the longest
@@ -1273,7 +1221,6 @@ static struct hid_ll_driver usb_hid_driver = {
.open = usbhid_open,
.close = usbhid_close,
.power = usbhid_power,
- .hidinput_input_event = usb_hidinput_input_event,
.request = usbhid_request,
.wait = usbhid_wait_io,
.idle = usbhid_idle,
@@ -1367,8 +1314,6 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *
setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid);
spin_lock_init(&usbhid->lock);
- INIT_WORK(&usbhid->led_work, hid_led);
-
ret = hid_add_device(hid);
if (ret) {
if (ret != -ENODEV)
@@ -1401,7 +1346,6 @@ static void hid_cancel_delayed_stuff(struct usbhid_device *usbhid)
{
del_timer_sync(&usbhid->io_retry);
cancel_work_sync(&usbhid->reset_work);
- cancel_work_sync(&usbhid->led_work);
}
static void hid_cease_io(struct usbhid_device *usbhid)
@@ -1521,15 +1465,17 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
struct usbhid_device *usbhid = hid->driver_data;
int status = 0;
bool driver_suspended = false;
+ unsigned int ledcount;
if (PMSG_IS_AUTO(message)) {
+ ledcount = hidinput_count_leds(hid);
spin_lock_irq(&usbhid->lock); /* Sync with error handler */
if (!test_bit(HID_RESET_PENDING, &usbhid->iofl)
&& !test_bit(HID_CLEAR_HALT, &usbhid->iofl)
&& !test_bit(HID_OUT_RUNNING, &usbhid->iofl)
&& !test_bit(HID_CTRL_RUNNING, &usbhid->iofl)
&& !test_bit(HID_KEYS_PRESSED, &usbhid->iofl)
- && (!usbhid->ledcount || ignoreled))
+ && (!ledcount || ignoreled))
{
set_bit(HID_SUSPENDED, &usbhid->iofl);
spin_unlock_irq(&usbhid->lock);
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 19b8360..0734552 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -109,6 +109,8 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_SIGMA_MICRO, USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X, HID_QUIRK_MULTI_INPUT },
+ { USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS },
+
{ 0, 0 }
};
diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h
index dbb6af6..f633c24 100644
--- a/drivers/hid/usbhid/usbhid.h
+++ b/drivers/hid/usbhid/usbhid.h
@@ -92,9 +92,6 @@ struct usbhid_device {
unsigned int retry_delay; /* Delay length in ms */
struct work_struct reset_work; /* Task context for resets */
wait_queue_head_t wait; /* For sleeping */
- int ledcount; /* counting the number of active leds */
-
- struct work_struct led_work; /* Task context for setting LEDs */
};
#define hid_to_usb_dev(hid_dev) \
OpenPOWER on IntegriCloud