diff options
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/Kconfig | 16 | ||||
-rw-r--r-- | drivers/hid/Makefile | 3 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 10 | ||||
-rw-r--r-- | drivers/hid/hid-elo.c | 273 | ||||
-rw-r--r-- | drivers/hid/hid-holtek-mouse.c | 77 | ||||
-rw-r--r-- | drivers/hid/hid-huion.c | 177 | ||||
-rw-r--r-- | drivers/hid/hid-hyperv.c | 4 | ||||
-rw-r--r-- | drivers/hid/hid-ids.h | 20 | ||||
-rw-r--r-- | drivers/hid/hid-input.c | 11 | ||||
-rw-r--r-- | drivers/hid/hid-kye.c | 21 | ||||
-rw-r--r-- | drivers/hid/hid-multitouch.c | 45 | ||||
-rw-r--r-- | drivers/hid/hid-roccat.c | 16 | ||||
-rw-r--r-- | drivers/hid/i2c-hid/i2c-hid.c | 20 |
13 files changed, 656 insertions, 37 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 8280eba43e14..e25fb9829729 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -217,6 +217,13 @@ config HID_ELECOM ---help--- Support for the ELECOM BM084 (bluetooth mouse). +config HID_ELO + tristate "ELO USB 4000/4500 touchscreen" + depends on USB_HID + ---help--- + Support for the ELO USB 4000/4500 touchscreens. Note that this is for + different devices than those handled by CONFIG_TOUCHSCREEN_USB_ELO. + config HID_EZKEY tristate "Ezkey BTC 8193 keyboard" if EXPERT depends on HID @@ -231,6 +238,9 @@ config HID_HOLTEK Support for Holtek based devices: - Holtek On Line Grip based game controller - Trust GXT 18 Gaming Keyboard + - Sharkoon Drakonia / Perixx MX-2000 gaming mice + - Tracer Sniper TRM-503 / NOVA Gaming Slider X200 / + Zalman ZM-GM1 config HOLTEK_FF bool "Holtek On Line Grip force feedback support" @@ -240,6 +250,12 @@ config HOLTEK_FF Say Y here if you have a Holtek On Line Grip based game controller and want to have force feedback support for it. +config HID_HUION + tristate "Huion tablets" + depends on USB_HID + ---help--- + Support for Huion 580 tablet. + config HID_KEYTOUCH tristate "Keytouch HID devices" depends on HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 833abcf19d68..ffd0d9826d84 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -48,10 +48,13 @@ obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o obj-$(CONFIG_HID_DRAGONRISE) += hid-dr.o obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o obj-$(CONFIG_HID_ELECOM) += hid-elecom.o +obj-$(CONFIG_HID_ELO) += hid-elo.o obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o obj-$(CONFIG_HID_GYRATION) += hid-gyration.o obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o +obj-$(CONFIG_HID_HOLTEK) += hid-holtek-mouse.o obj-$(CONFIG_HID_HOLTEK) += hid-holtekff.o +obj-$(CONFIG_HID_HUION) += hid-huion.o obj-$(CONFIG_HID_HYPERV_MOUSE) += hid-hyperv.o obj-$(CONFIG_HID_ICADE) += hid-icade.o obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index ecd1107a5488..e39dac68063c 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1293,7 +1293,7 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) { ret = hdrv->raw_event(hid, report, data, size); - if (ret != 0) { + if (ret < 0) { ret = ret < 0 ? ret : 0; goto unlock; } @@ -1573,6 +1573,8 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) }, { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0009) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0030) }, { HID_USB_DEVICE(USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II) }, { HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) }, { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) }, @@ -1584,10 +1586,14 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_580) }, { HID_USB_DEVICE(USB_VENDOR_ID_JESS2, USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ION, USB_DEVICE_ID_ICADE) }, { HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) }, { HID_USB_DEVICE(USB_VENDOR_ID_KEYTOUCH, USB_DEVICE_ID_KEYTOUCH_IEC) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_I405X) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X) }, @@ -2044,6 +2050,8 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006) }, { HID_USB_DEVICE(USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1007) }, { HID_USB_DEVICE(USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA) }, + { HID_USB_DEVICE(USB_VENDOR_ID_JABRA, USB_DEVICE_ID_JABRA_SPEAK_410) }, + { HID_USB_DEVICE(USB_VENDOR_ID_JABRA, USB_DEVICE_ID_JABRA_SPEAK_510) }, { HID_USB_DEVICE(USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO) }, { HID_USB_DEVICE(USB_VENDOR_ID_KWORLD, USB_DEVICE_ID_KWORLD_RADIO_FM700) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560) }, diff --git a/drivers/hid/hid-elo.c b/drivers/hid/hid-elo.c new file mode 100644 index 000000000000..f042a6cf8b18 --- /dev/null +++ b/drivers/hid/hid-elo.c @@ -0,0 +1,273 @@ +/* + * HID driver for ELO usb touchscreen 4000/4500 + * + * Copyright (c) 2013 Jiri Slaby + * + * Data parsing taken from elousb driver by Vojtech Pavlik. + * + * This driver is licensed under the terms of GPLv2. + */ + +#include <linux/hid.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/workqueue.h> + +#include "hid-ids.h" + +#define ELO_PERIODIC_READ_INTERVAL HZ +#define ELO_SMARTSET_CMD_TIMEOUT 2000 /* msec */ + +/* Elo SmartSet commands */ +#define ELO_FLUSH_SMARTSET_RESPONSES 0x02 /* Flush all pending smartset responses */ +#define ELO_SEND_SMARTSET_COMMAND 0x05 /* Send a smartset command */ +#define ELO_GET_SMARTSET_RESPONSE 0x06 /* Get a smartset response */ +#define ELO_DIAG 0x64 /* Diagnostics command */ +#define ELO_SMARTSET_PACKET_SIZE 8 + +struct elo_priv { + struct usb_device *usbdev; + struct delayed_work work; + unsigned char buffer[ELO_SMARTSET_PACKET_SIZE]; +}; + +static struct workqueue_struct *wq; +static bool use_fw_quirk = true; +module_param(use_fw_quirk, bool, S_IRUGO); +MODULE_PARM_DESC(use_fw_quirk, "Do periodic pokes for broken M firmwares (default = true)"); + +static void elo_input_configured(struct hid_device *hdev, + struct hid_input *hidinput) +{ + struct input_dev *input = hidinput->input; + + set_bit(BTN_TOUCH, input->keybit); + set_bit(ABS_PRESSURE, input->absbit); + input_set_abs_params(input, ABS_PRESSURE, 0, 256, 0, 0); +} + +static void elo_process_data(struct input_dev *input, const u8 *data, int size) +{ + int press; + + input_report_abs(input, ABS_X, (data[3] << 8) | data[2]); + input_report_abs(input, ABS_Y, (data[5] << 8) | data[4]); + + press = 0; + if (data[1] & 0x80) + press = (data[7] << 8) | data[6]; + input_report_abs(input, ABS_PRESSURE, press); + + if (data[1] & 0x03) { + input_report_key(input, BTN_TOUCH, 1); + input_sync(input); + } + + if (data[1] & 0x04) + input_report_key(input, BTN_TOUCH, 0); + + input_sync(input); +} + +static int elo_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *data, int size) +{ + struct hid_input *hidinput; + + if (!(hdev->claimed & HID_CLAIMED_INPUT) || list_empty(&hdev->inputs)) + return 0; + + hidinput = list_first_entry(&hdev->inputs, struct hid_input, list); + + switch (report->id) { + case 0: + if (data[0] == 'T') { /* Mandatory ELO packet marker */ + elo_process_data(hidinput->input, data, size); + return 1; + } + break; + default: /* unknown report */ + /* Unknown report type; pass upstream */ + hid_info(hdev, "unknown report type %d\n", report->id); + break; + } + + return 0; +} + +static int elo_smartset_send_get(struct usb_device *dev, u8 command, + void *data) +{ + unsigned int pipe; + u8 dir; + + if (command == ELO_SEND_SMARTSET_COMMAND) { + pipe = usb_sndctrlpipe(dev, 0); + dir = USB_DIR_OUT; + } else if (command == ELO_GET_SMARTSET_RESPONSE) { + pipe = usb_rcvctrlpipe(dev, 0); + dir = USB_DIR_IN; + } else + return -EINVAL; + + return usb_control_msg(dev, pipe, command, + dir | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, data, ELO_SMARTSET_PACKET_SIZE, + ELO_SMARTSET_CMD_TIMEOUT); +} + +static int elo_flush_smartset_responses(struct usb_device *dev) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + ELO_FLUSH_SMARTSET_RESPONSES, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); +} + +static void elo_work(struct work_struct *work) +{ + struct elo_priv *priv = container_of(work, struct elo_priv, work.work); + struct usb_device *dev = priv->usbdev; + unsigned char *buffer = priv->buffer; + int ret; + + ret = elo_flush_smartset_responses(dev); + if (ret < 0) { + dev_err(&dev->dev, "initial FLUSH_SMARTSET_RESPONSES failed, error %d\n", + ret); + goto fail; + } + + /* send Diagnostics command */ + *buffer = ELO_DIAG; + ret = elo_smartset_send_get(dev, ELO_SEND_SMARTSET_COMMAND, buffer); + if (ret < 0) { + dev_err(&dev->dev, "send Diagnostics Command failed, error %d\n", + ret); + goto fail; + } + + /* get the result */ + ret = elo_smartset_send_get(dev, ELO_GET_SMARTSET_RESPONSE, buffer); + if (ret < 0) { + dev_err(&dev->dev, "get Diagnostics Command response failed, error %d\n", + ret); + goto fail; + } + + /* read the ack */ + if (*buffer != 'A') { + ret = elo_smartset_send_get(dev, ELO_GET_SMARTSET_RESPONSE, + buffer); + if (ret < 0) { + dev_err(&dev->dev, "get acknowledge response failed, error %d\n", + ret); + goto fail; + } + } + +fail: + ret = elo_flush_smartset_responses(dev); + if (ret < 0) + dev_err(&dev->dev, "final FLUSH_SMARTSET_RESPONSES failed, error %d\n", + ret); + queue_delayed_work(wq, &priv->work, ELO_PERIODIC_READ_INTERVAL); +} + +/* + * Not all Elo devices need the periodic HID descriptor reads. + * Only firmware version M needs this. + */ +static bool elo_broken_firmware(struct usb_device *dev) +{ + return use_fw_quirk && le16_to_cpu(dev->descriptor.bcdDevice) == 0x10d; +} + +static int elo_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + struct elo_priv *priv; + int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + INIT_DELAYED_WORK(&priv->work, elo_work); + priv->usbdev = interface_to_usbdev(to_usb_interface(hdev->dev.parent)); + + hid_set_drvdata(hdev, priv); + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "parse failed\n"); + goto err_free; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + hid_err(hdev, "hw start failed\n"); + goto err_free; + } + + if (elo_broken_firmware(priv->usbdev)) { + hid_info(hdev, "broken firmware found, installing workaround\n"); + queue_delayed_work(wq, &priv->work, ELO_PERIODIC_READ_INTERVAL); + } + + return 0; +err_free: + kfree(priv); + return ret; +} + +static void elo_remove(struct hid_device *hdev) +{ + struct elo_priv *priv = hid_get_drvdata(hdev); + + hid_hw_stop(hdev); + flush_workqueue(wq); + kfree(priv); +} + +static const struct hid_device_id elo_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0009), }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0030), }, + { } +}; +MODULE_DEVICE_TABLE(hid, elo_devices); + +static struct hid_driver elo_driver = { + .name = "elo", + .id_table = elo_devices, + .probe = elo_probe, + .remove = elo_remove, + .raw_event = elo_raw_event, + .input_configured = elo_input_configured, +}; + +static int __init elo_driver_init(void) +{ + int ret; + + wq = create_singlethread_workqueue("elousb"); + if (!wq) + return -ENOMEM; + + ret = hid_register_driver(&elo_driver); + if (ret) + destroy_workqueue(wq); + + return ret; +} +module_init(elo_driver_init); + +static void __exit elo_driver_exit(void) +{ + hid_unregister_driver(&elo_driver); + destroy_workqueue(wq); +} +module_exit(elo_driver_exit); + +MODULE_AUTHOR("Jiri Slaby <jslaby@suse.cz>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-holtek-mouse.c b/drivers/hid/hid-holtek-mouse.c new file mode 100644 index 000000000000..7e6db3cf46f9 --- /dev/null +++ b/drivers/hid/hid-holtek-mouse.c @@ -0,0 +1,77 @@ +/* + * HID driver for Holtek gaming mice + * Copyright (c) 2013 Christian Ohm + * Heavily inspired by various other HID drivers that adjust the report + * descriptor. +*/ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/hid.h> +#include <linux/module.h> +#include <linux/usb.h> + +#include "hid-ids.h" + +/* + * The report descriptor of some Holtek based gaming mice specifies an + * excessively large number of consumer usages (2^15), which is more than + * HID_MAX_USAGES. This prevents proper parsing of the report descriptor. + * + * This driver fixes the report descriptor for: + * - USB ID 04d9:a067, sold as Sharkoon Drakonia and Perixx MX-2000 + * - USB ID 04d9:a04a, sold as Tracer Sniper TRM-503, NOVA Gaming Slider X200 + * and Zalman ZM-GM1 + */ + +static __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + + if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { + /* Change usage maximum and logical maximum from 0x7fff to + * 0x2fff, so they don't exceed HID_MAX_USAGES */ + switch (hdev->product) { + case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067: + if (*rsize >= 122 && rdesc[115] == 0xff && rdesc[116] == 0x7f + && rdesc[120] == 0xff && rdesc[121] == 0x7f) { + hid_info(hdev, "Fixing up report descriptor\n"); + rdesc[116] = rdesc[121] = 0x2f; + } + break; + case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A: + if (*rsize >= 113 && rdesc[106] == 0xff && rdesc[107] == 0x7f + && rdesc[111] == 0xff && rdesc[112] == 0x7f) { + hid_info(hdev, "Fixing up report descriptor\n"); + rdesc[107] = rdesc[112] = 0x2f; + } + break; + } + + } + return rdesc; +} + +static const struct hid_device_id holtek_mouse_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, + USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, + USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A) }, + { } +}; +MODULE_DEVICE_TABLE(hid, holtek_mouse_devices); + +static struct hid_driver holtek_mouse_driver = { + .name = "holtek_mouse", + .id_table = holtek_mouse_devices, + .report_fixup = holtek_mouse_report_fixup, +}; + +module_hid_driver(holtek_mouse_driver); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-huion.c b/drivers/hid/hid-huion.c new file mode 100644 index 000000000000..cbf4da4689ba --- /dev/null +++ b/drivers/hid/hid-huion.c @@ -0,0 +1,177 @@ +/* + * HID driver for Huion devices not fully compliant with HID standard + * + * Copyright (c) 2013 Martin Rusko + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> +#include <linux/usb.h> +#include "usbhid/usbhid.h" + +#include "hid-ids.h" + +/* Original Huion 580 report descriptor size */ +#define HUION_580_RDESC_ORIG_SIZE 177 + +/* Fixed Huion 580 report descriptor */ +static __u8 huion_580_rdesc_fixed[] = { + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x02, /* Usage (Pen), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x07, /* Report ID (7), */ + 0x09, 0x20, /* Usage (Stylus), */ + 0xA0, /* Collection (Physical), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x09, 0x44, /* Usage (Barrel Switch), */ + 0x09, 0x46, /* Usage (Tablet Pick), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x09, 0x32, /* Usage (In Range), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 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, 0x07, /* Logical Maximum (2047), */ + 0x81, 0x02, /* Input (Variable), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; + +static __u8 *huion_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + switch (hdev->product) { + case USB_DEVICE_ID_HUION_580: + if (*rsize == HUION_580_RDESC_ORIG_SIZE) { + rdesc = huion_580_rdesc_fixed; + *rsize = sizeof(huion_580_rdesc_fixed); + } + break; + } + return rdesc; +} + +/** + * Enable fully-functional tablet mode by reading special string + * descriptor. + * + * @hdev: HID device + * + * The specific string descriptor and data were discovered by sniffing + * the Windows driver traffic. + */ +static int huion_tablet_enable(struct hid_device *hdev) +{ + int rc; + char buf[22]; + + rc = usb_string(hid_to_usb_dev(hdev), 0x64, buf, sizeof(buf)); + if (rc < 0) + return rc; + + return 0; +} + +static int huion_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + + /* Ignore interfaces 1 (mouse) and 2 (keyboard) for Huion 580 tablet, + * as they are not used + */ + switch (id->product) { + case USB_DEVICE_ID_HUION_580: + if (intf->cur_altsetting->desc.bInterfaceNumber != 0x00) + return -ENODEV; + break; + } + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "parse failed\n"); + goto err; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + hid_err(hdev, "hw start failed\n"); + goto err; + } + + switch (id->product) { + case USB_DEVICE_ID_HUION_580: + ret = huion_tablet_enable(hdev); + if (ret) { + hid_err(hdev, "tablet enabling failed\n"); + goto enabling_err; + } + break; + } + + return 0; +enabling_err: + hid_hw_stop(hdev); +err: + return ret; +} + +static int huion_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *data, int size) +{ + /* If this is a pen input report then invert the in-range bit */ + if (report->type == HID_INPUT_REPORT && report->id == 0x07 && size >= 2) + data[1] ^= 0x40; + + return 0; +} + +static const struct hid_device_id huion_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_580) }, + { } +}; +MODULE_DEVICE_TABLE(hid, huion_devices); + +static struct hid_driver huion_driver = { + .name = "huion", + .id_table = huion_devices, + .probe = huion_probe, + .report_fixup = huion_report_fixup, + .raw_event = huion_raw_event, +}; +module_hid_driver(huion_driver); + +MODULE_AUTHOR("Martin Rusko"); +MODULE_DESCRIPTION("Huion HID driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c index aa3fec0d9dc6..713217380b44 100644 --- a/drivers/hid/hid-hyperv.c +++ b/drivers/hid/hid-hyperv.c @@ -199,13 +199,11 @@ static void mousevsc_on_receive_device_info(struct mousevsc_dev *input_device, if (desc->bLength == 0) goto cleanup; - input_device->hid_desc = kzalloc(desc->bLength, GFP_ATOMIC); + input_device->hid_desc = kmemdup(desc, desc->bLength, GFP_ATOMIC); if (!input_device->hid_desc) goto cleanup; - memcpy(input_device->hid_desc, desc, desc->bLength); - input_device->report_desc_size = desc->desc[0].wDescriptorLength; if (input_device->report_desc_size == 0) { input_device->dev_info_status = -EINVAL; diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 508c0072b630..c5aea29f164f 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -248,6 +248,9 @@ #define USB_DEVICE_ID_CYPRESS_BARCODE_4 0xed81 #define USB_DEVICE_ID_CYPRESS_TRUETOUCH 0xc001 +#define USB_VENDOR_ID_DATA_MODUL 0x7374 +#define USB_VENDOR_ID_DATA_MODUL_EASYMAXTOUCH 0x1201 + #define USB_VENDOR_ID_DEALEXTREAME 0x10c5 #define USB_DEVICE_ID_DEALEXTREAME_RADIO_SI4701 0x819a @@ -272,16 +275,15 @@ #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_725E 0x725e #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7262 0x7262 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B 0x726b -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72AA 0x72aa #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1 0x72a1 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72AA 0x72aa +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4 0x72c4 +#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0 0x72d0 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA 0x72fa #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302 0x7302 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7349 0x7349 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_73F7 0x73f7 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001 -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224 0x7224 -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0 0x72d0 -#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4 0x72c4 #define USB_VENDOR_ID_ELECOM 0x056e #define USB_DEVICE_ID_ELECOM_BM084 0x0061 @@ -425,6 +427,9 @@ #define USB_DEVICE_ID_UGCI_FLYING 0x0020 #define USB_DEVICE_ID_UGCI_FIGHTING 0x0030 +#define USB_VENDOR_ID_HUION 0x256c +#define USB_DEVICE_ID_HUION_580 0x006e + #define USB_VENDOR_ID_IDEACOM 0x1cb6 #define USB_DEVICE_ID_IDEACOM_IDC6650 0x6650 #define USB_DEVICE_ID_IDEACOM_IDC6651 0x6651 @@ -440,6 +445,8 @@ #define USB_VENDOR_ID_HOLTEK_ALT 0x04d9 #define USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD 0xa055 +#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067 0xa067 +#define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A 0xa04a #define USB_VENDOR_ID_IMATION 0x0718 #define USB_DEVICE_ID_DISC_STAKKA 0xd000 @@ -447,6 +454,10 @@ #define USB_VENDOR_ID_IRTOUCHSYSTEMS 0x6615 #define USB_DEVICE_ID_IRTOUCH_INFRARED_USB 0x0070 +#define USB_VENDOR_ID_JABRA 0x0b0e +#define USB_DEVICE_ID_JABRA_SPEAK_410 0x0412 +#define USB_DEVICE_ID_JABRA_SPEAK_510 0x0420 + #define USB_VENDOR_ID_JESS 0x0c45 #define USB_DEVICE_ID_JESS_YUREX 0x1010 @@ -467,6 +478,7 @@ #define USB_VENDOR_ID_KYE 0x0458 #define USB_DEVICE_ID_KYE_ERGO_525V 0x0087 +#define USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE 0x0138 #define USB_DEVICE_ID_KYE_GPEN_560 0x5003 #define USB_DEVICE_ID_KYE_EASYPEN_I405X 0x5010 #define USB_DEVICE_ID_KYE_MOUSEPEN_I608X 0x5011 diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 945b8158ec4c..7480799e535c 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -354,10 +354,10 @@ static int hidinput_get_battery_property(struct power_supply *psy, dev->battery_report_type); if (ret != 2) { - if (ret >= 0) - ret = -EINVAL; + ret = -ENODATA; break; } + ret = 0; if (dev->battery_min < dev->battery_max && buf[1] >= dev->battery_min && @@ -1042,9 +1042,14 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct /* * Ignore out-of-range values as per HID specification, - * section 5.10 and 6.2.25 + * section 5.10 and 6.2.25. + * + * The logical_minimum < logical_maximum check is done so that we + * don't unintentionally discard values sent by devices which + * don't specify logical min and max. */ if ((field->flags & HID_MAIN_ITEM_VARIABLE) && + (field->logical_minimum < field->logical_maximum) && (value < field->logical_minimum || value > field->logical_maximum)) { dbg_hid("Ignoring out-of-range value %x\n", value); diff --git a/drivers/hid/hid-kye.c b/drivers/hid/hid-kye.c index 6af90dbdc3d4..1e2ee2aa84a0 100644 --- a/drivers/hid/hid-kye.c +++ b/drivers/hid/hid-kye.c @@ -314,6 +314,25 @@ static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc, *rsize = sizeof(easypen_m610x_rdesc_fixed); } break; + case USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE: + /* + * the fixup that need to be done: + * - change Usage Maximum in the Comsumer Control + * (report ID 3) to a reasonable value + */ + if (*rsize >= 135 && + /* Usage Page (Consumer Devices) */ + rdesc[104] == 0x05 && rdesc[105] == 0x0c && + /* Usage (Consumer Control) */ + rdesc[106] == 0x09 && rdesc[107] == 0x01 && + /* Usage Maximum > 12287 */ + rdesc[114] == 0x2a && rdesc[116] > 0x2f) { + hid_info(hdev, + "fixing up Genius Gila Gaming Mouse " + "report descriptor\n"); + rdesc[116] = 0x2f; + } + break; } return rdesc; } @@ -407,6 +426,8 @@ static const struct hid_device_id kye_devices[] = { USB_DEVICE_ID_KYE_MOUSEPEN_I608X) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KYE, + USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE) }, { } }; MODULE_DEVICE_TABLE(hid, kye_devices); diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index dc3ae5c56f56..cb0e361d7a4b 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -264,9 +264,12 @@ 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 (hi->input->name != hdev->name) - kfree(hi->input->name); + if (name != hdev->name) { + hi->input->name = hdev->name; + kfree(name); + } } static ssize_t mt_show_quirks(struct device *dev, @@ -1040,11 +1043,11 @@ static void mt_remove(struct hid_device *hdev) struct hid_input *hi; sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group); - hid_hw_stop(hdev); - list_for_each_entry(hi, &hdev->inputs, list) mt_free_input_name(hi); + hid_hw_stop(hdev); + kfree(td); hid_set_drvdata(hdev, NULL); } @@ -1108,6 +1111,11 @@ static const struct hid_device_id mt_devices[] = { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_TRUETOUCH) }, + /* Data Modul easyMaxTouch */ + { .driver_data = MT_CLS_DEFAULT, + MT_USB_DEVICE(USB_VENDOR_ID_DATA_MODUL, + USB_VENDOR_ID_DATA_MODUL_EASYMAXTOUCH) }, + /* eGalax devices (resistive) */ { .driver_data = MT_CLS_EGALAX, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, @@ -1117,34 +1125,40 @@ static const struct hid_device_id mt_devices[] = { USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_480E) }, /* eGalax devices (capacitive) */ - { .driver_data = MT_CLS_EGALAX, - MT_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C) }, { .driver_data = MT_CLS_EGALAX_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7207) }, - { .driver_data = MT_CLS_EGALAX_SERIAL, + { .driver_data = MT_CLS_EGALAX, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_725E) }, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_720C) }, { .driver_data = MT_CLS_EGALAX_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224) }, { .driver_data = MT_CLS_EGALAX_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_722A) }, - { .driver_data = MT_CLS_EGALAX, + { .driver_data = MT_CLS_EGALAX_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B) }, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_725E) }, { .driver_data = MT_CLS_EGALAX_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7262) }, { .driver_data = MT_CLS_EGALAX, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_726B) }, + { .driver_data = MT_CLS_EGALAX, + MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72A1) }, { .driver_data = MT_CLS_EGALAX_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72AA) }, { .driver_data = MT_CLS_EGALAX, + HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4) }, + { .driver_data = MT_CLS_EGALAX, + HID_USB_DEVICE(USB_VENDOR_ID_DWAV, + USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0) }, + { .driver_data = MT_CLS_EGALAX, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA) }, { .driver_data = MT_CLS_EGALAX, @@ -1159,15 +1173,6 @@ static const struct hid_device_id mt_devices[] = { { .driver_data = MT_CLS_EGALAX_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_DWAV, USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) }, - { .driver_data = MT_CLS_EGALAX, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224) }, - { .driver_data = MT_CLS_EGALAX, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0) }, - { .driver_data = MT_CLS_EGALAX, - HID_USB_DEVICE(USB_VENDOR_ID_DWAV, - USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4) }, /* Elo TouchSystems IntelliTouch Plus panel */ { .driver_data = MT_CLS_DUAL_CONTACT_ID, diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c index b59b3df9ca95..65c4ccfcbd29 100644 --- a/drivers/hid/hid-roccat.c +++ b/drivers/hid/hid-roccat.c @@ -366,7 +366,7 @@ void roccat_disconnect(int minor) mutex_lock(&devices_lock); devices[minor] = NULL; mutex_unlock(&devices_lock); - + if (device->open) { hid_hw_close(device->hid); wake_up_interruptible(&device->wait); @@ -426,13 +426,23 @@ static int __init roccat_init(void) if (retval < 0) { pr_warn("can't get major number\n"); - return retval; + goto error; } cdev_init(&roccat_cdev, &roccat_ops); - cdev_add(&roccat_cdev, dev_id, ROCCAT_MAX_DEVICES); + retval = cdev_add(&roccat_cdev, dev_id, ROCCAT_MAX_DEVICES); + if (retval < 0) { + pr_warn("cannot add cdev\n"); + goto cleanup_alloc_chrdev_region; + } return 0; + + + cleanup_alloc_chrdev_region: + unregister_chrdev_region(dev_id, ROCCAT_MAX_DEVICES); + error: + return retval; } static void __exit roccat_exit(void) diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 2b1799a3b212..879b0ed701a3 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -108,6 +108,7 @@ static const struct i2c_hid_cmd hid_reset_cmd = { I2C_HID_CMD(0x01), static const struct i2c_hid_cmd hid_get_report_cmd = { I2C_HID_CMD(0x02) }; static const struct i2c_hid_cmd hid_set_report_cmd = { I2C_HID_CMD(0x03) }; static const struct i2c_hid_cmd hid_set_power_cmd = { I2C_HID_CMD(0x08) }; +static const struct i2c_hid_cmd hid_no_cmd = { .length = 0 }; /* * These definitions are not used here, but are defined by the spec. @@ -259,8 +260,11 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType, { struct i2c_hid *ihid = i2c_get_clientdata(client); u8 *args = ihid->argsbuf; + const struct i2c_hid_cmd * hidcmd = &hid_set_report_cmd; int ret; u16 dataRegister = le16_to_cpu(ihid->hdesc.wDataRegister); + u16 outputRegister = le16_to_cpu(ihid->hdesc.wOutputRegister); + u16 maxOutputLength = le16_to_cpu(ihid->hdesc.wMaxOutputLength); /* hidraw already checked that data_len < HID_MAX_BUFFER_SIZE */ u16 size = 2 /* size */ + @@ -278,8 +282,18 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType, reportID = 0x0F; } - args[index++] = dataRegister & 0xFF; - args[index++] = dataRegister >> 8; + /* + * use the data register for feature reports or if the device does not + * support the output register + */ + if (reportType == 0x03 || maxOutputLength == 0) { + args[index++] = dataRegister & 0xFF; + args[index++] = dataRegister >> 8; + } else { + args[index++] = outputRegister & 0xFF; + args[index++] = outputRegister >> 8; + hidcmd = &hid_no_cmd; + } args[index++] = size & 0xFF; args[index++] = size >> 8; @@ -289,7 +303,7 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType, memcpy(&args[index], buf, data_len); - ret = __i2c_hid_command(client, &hid_set_report_cmd, reportID, + ret = __i2c_hid_command(client, hidcmd, reportID, reportType, args, args_len, NULL, 0); if (ret) { dev_err(&client->dev, "failed to set a report to device.\n"); |