diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/hid/Kconfig | 2 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 7 | ||||
-rw-r--r-- | drivers/hid/hid-cp2112.c | 111 | ||||
-rw-r--r-- | drivers/hid/hid-huion.c | 265 | ||||
-rw-r--r-- | drivers/hid/hid-hyperv.c | 6 | ||||
-rw-r--r-- | drivers/hid/hid-ids.h | 4 | ||||
-rw-r--r-- | drivers/hid/hid-picolcd_debugfs.c | 9 | ||||
-rw-r--r-- | drivers/hid/hid-rmi.c | 67 | ||||
-rw-r--r-- | drivers/hid/hid-roccat-lua.c | 2 | ||||
-rw-r--r-- | drivers/hid/hid-sony.c | 132 | ||||
-rw-r--r-- | drivers/hid/i2c-hid/i2c-hid.c | 15 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-core.c | 8 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-quirks.c | 1 |
13 files changed, 458 insertions, 171 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 21cce19d301e..e02cf59b048d 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -787,7 +787,7 @@ config HID_XINMO depends on HID ---help--- Support for Xin-Mo devices that are not fully compliant with the HID - standard. Currently only supports the Xin-Mo Dual Arcade. Say Y here + standard. Currently only supports the Xin-Mo Dual Arcade. Say Y here if you have a Xin-Mo Dual Arcade controller. config HID_ZEROPLUS diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 81b3bb6c4fd1..6c813c6092f8 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -783,7 +783,9 @@ static int hid_scan_report(struct hid_device *hid) * Vendor specific handlings */ if ((hid->vendor == USB_VENDOR_ID_SYNAPTICS) && - (hid->group == HID_GROUP_GENERIC)) + (hid->group == HID_GROUP_GENERIC) && + /* only bind to the mouse interface of composite USB devices */ + (hid->bus != BUS_USB || hid->type == HID_TYPE_USBMOUSE)) /* hid-rmi should take care of them, not hid-generic */ hid->group = HID_GROUP_RMI; @@ -1782,7 +1784,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072) }, { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) }, - { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_580) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, { 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) }, @@ -2268,6 +2270,7 @@ static const struct hid_device_id hid_ignore_list[] = { { 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_JABRA, USB_DEVICE_ID_JABRA_GN9350E) }, { 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-cp2112.c b/drivers/hid/hid-cp2112.c index 56be85a9a77c..a822db5a8338 100644 --- a/drivers/hid/hid-cp2112.c +++ b/drivers/hid/hid-cp2112.c @@ -240,8 +240,6 @@ static int cp2112_gpio_direction_output(struct gpio_chip *chip, u8 buf[5]; int ret; - cp2112_gpio_set(chip, offset, value); - ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, sizeof(buf), HID_FEATURE_REPORT, HID_REQ_GET_REPORT); @@ -260,6 +258,12 @@ static int cp2112_gpio_direction_output(struct gpio_chip *chip, return ret; } + /* + * Set gpio value when output direction is already set, + * as specified in AN495, Rev. 0.2, cpt. 4.4 + */ + cp2112_gpio_set(chip, offset, value); + return 0; } @@ -425,6 +429,105 @@ static int cp2112_write_req(void *buf, u8 slave_address, u8 command, u8 *data, return data_length + 4; } +static int cp2112_i2c_write_req(void *buf, u8 slave_address, u8 *data, + u8 data_length) +{ + struct cp2112_write_req_report *report = buf; + + if (data_length > sizeof(report->data)) + return -EINVAL; + + report->report = CP2112_DATA_WRITE_REQUEST; + report->slave_address = slave_address << 1; + report->length = data_length; + memcpy(report->data, data, data_length); + return data_length + 3; +} + +static int cp2112_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, + int num) +{ + struct cp2112_device *dev = (struct cp2112_device *)adap->algo_data; + struct hid_device *hdev = dev->hdev; + u8 buf[64]; + ssize_t count; + unsigned int retries; + int ret; + + hid_dbg(hdev, "I2C %d messages\n", num); + + if (num != 1) { + hid_err(hdev, + "Multi-message I2C transactions not supported\n"); + return -EOPNOTSUPP; + } + + if (msgs->flags & I2C_M_RD) + count = cp2112_read_req(buf, msgs->addr, msgs->len); + else + count = cp2112_i2c_write_req(buf, msgs->addr, msgs->buf, + msgs->len); + + if (count < 0) + return count; + + ret = hid_hw_power(hdev, PM_HINT_FULLON); + if (ret < 0) { + hid_err(hdev, "power management error: %d\n", ret); + return ret; + } + + ret = cp2112_hid_output(hdev, buf, count, HID_OUTPUT_REPORT); + if (ret < 0) { + hid_warn(hdev, "Error starting transaction: %d\n", ret); + goto power_normal; + } + + for (retries = 0; retries < XFER_STATUS_RETRIES; ++retries) { + ret = cp2112_xfer_status(dev); + if (-EBUSY == ret) + continue; + if (ret < 0) + goto power_normal; + break; + } + + if (XFER_STATUS_RETRIES <= retries) { + hid_warn(hdev, "Transfer timed out, cancelling.\n"); + buf[0] = CP2112_CANCEL_TRANSFER; + buf[1] = 0x01; + + ret = cp2112_hid_output(hdev, buf, 2, HID_OUTPUT_REPORT); + if (ret < 0) + hid_warn(hdev, "Error cancelling transaction: %d\n", + ret); + + ret = -ETIMEDOUT; + goto power_normal; + } + + if (!(msgs->flags & I2C_M_RD)) + goto finish; + + ret = cp2112_read(dev, msgs->buf, msgs->len); + if (ret < 0) + goto power_normal; + if (ret != msgs->len) { + hid_warn(hdev, "short read: %d < %d\n", ret, msgs->len); + ret = -EIO; + goto power_normal; + } + +finish: + /* return the number of transferred messages */ + ret = 1; + +power_normal: + hid_hw_power(hdev, PM_HINT_NORMAL); + hid_dbg(hdev, "I2C transfer finished: %d\n", ret); + return ret; +} + static int cp2112_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data) @@ -591,7 +694,8 @@ power_normal: static u32 cp2112_functionality(struct i2c_adapter *adap) { - return I2C_FUNC_SMBUS_BYTE | + return I2C_FUNC_I2C | + I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA | @@ -601,6 +705,7 @@ static u32 cp2112_functionality(struct i2c_adapter *adap) } static const struct i2c_algorithm smbus_algorithm = { + .master_xfer = cp2112_i2c_xfer, .smbus_xfer = cp2112_xfer, .functionality = cp2112_functionality, }; diff --git a/drivers/hid/hid-huion.c b/drivers/hid/hid-huion.c index cbf4da4689ba..60f44cd1b0ed 100644 --- a/drivers/hid/hid-huion.c +++ b/drivers/hid/hid-huion.c @@ -2,6 +2,7 @@ * HID driver for Huion devices not fully compliant with HID standard * * Copyright (c) 2013 Martin Rusko + * Copyright (c) 2014 Nikolai Kondrashov */ /* @@ -15,67 +16,89 @@ #include <linux/hid.h> #include <linux/module.h> #include <linux/usb.h> +#include <asm/unaligned.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 */ +/* Report descriptor template placeholder head */ +#define HUION_PH_HEAD 0xFE, 0xED, 0x1D + +/* Report descriptor template placeholder IDs */ +enum huion_ph_id { + HUION_PH_ID_X_LM, + HUION_PH_ID_X_PM, + HUION_PH_ID_Y_LM, + HUION_PH_ID_Y_PM, + HUION_PH_ID_PRESSURE_LM, + HUION_PH_ID_NUM +}; + +/* Report descriptor template placeholder */ +#define HUION_PH(_ID) HUION_PH_HEAD, HUION_PH_ID_##_ID + +/* Fixed report descriptor template */ +static const __u8 huion_tablet_rdesc_template[] = { + 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), */ + 0x27, HUION_PH(X_LM), /* Logical Maximum (PLACEHOLDER), */ + 0x47, HUION_PH(X_PM), /* Physical Maximum (PLACEHOLDER), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (Y), */ + 0x27, HUION_PH(Y_LM), /* Logical Maximum (PLACEHOLDER), */ + 0x47, HUION_PH(Y_PM), /* Physical Maximum (PLACEHOLDER), */ + 0x81, 0x02, /* Input (Variable), */ + 0xB4, /* Pop, */ + 0x09, 0x30, /* Usage (Tip Pressure), */ + 0x27, + HUION_PH(PRESSURE_LM), /* Logical Maximum (PLACEHOLDER), */ + 0x81, 0x02, /* Input (Variable), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; + +/* Driver data */ +struct huion_drvdata { + __u8 *rdesc; + unsigned int rsize; }; static __u8 *huion_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { + struct huion_drvdata *drvdata = hid_get_drvdata(hdev); 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); + case USB_DEVICE_ID_HUION_TABLET: + if (drvdata->rdesc != NULL) { + rdesc = drvdata->rdesc; + *rsize = drvdata->rsize; } break; } @@ -83,82 +106,144 @@ static __u8 *huion_report_fixup(struct hid_device *hdev, __u8 *rdesc, } /** - * Enable fully-functional tablet mode by reading special string - * descriptor. + * Enable fully-functional tablet mode and determine device parameters. * * @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]; + struct usb_device *usb_dev = hid_to_usb_dev(hdev); + struct huion_drvdata *drvdata = hid_get_drvdata(hdev); + __le16 buf[6]; - rc = usb_string(hid_to_usb_dev(hdev), 0x64, buf, sizeof(buf)); - if (rc < 0) - return rc; + /* + * Read string descriptor containing tablet parameters. The specific + * string descriptor and data were discovered by sniffing the Windows + * driver traffic. + * NOTE: This enables fully-functional tablet mode. + */ + rc = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, + (USB_DT_STRING << 8) + 0x64, + 0x0409, buf, sizeof(buf), + USB_CTRL_GET_TIMEOUT); + if (rc == -EPIPE) + hid_warn(hdev, "device parameters not found\n"); + else if (rc < 0) + hid_warn(hdev, "failed to get device parameters: %d\n", rc); + else if (rc != sizeof(buf)) + hid_warn(hdev, "invalid device parameters\n"); + else { + s32 params[HUION_PH_ID_NUM]; + s32 resolution; + __u8 *p; + s32 v; + + /* Extract device parameters */ + params[HUION_PH_ID_X_LM] = le16_to_cpu(buf[1]); + params[HUION_PH_ID_Y_LM] = le16_to_cpu(buf[2]); + params[HUION_PH_ID_PRESSURE_LM] = le16_to_cpu(buf[4]); + resolution = le16_to_cpu(buf[5]); + if (resolution == 0) { + params[HUION_PH_ID_X_PM] = 0; + params[HUION_PH_ID_Y_PM] = 0; + } else { + params[HUION_PH_ID_X_PM] = params[HUION_PH_ID_X_LM] * + 1000 / resolution; + params[HUION_PH_ID_Y_PM] = params[HUION_PH_ID_Y_LM] * + 1000 / resolution; + } + + /* Allocate fixed report descriptor */ + drvdata->rdesc = devm_kmalloc(&hdev->dev, + sizeof(huion_tablet_rdesc_template), + GFP_KERNEL); + if (drvdata->rdesc == NULL) { + hid_err(hdev, "failed to allocate fixed rdesc\n"); + return -ENOMEM; + } + drvdata->rsize = sizeof(huion_tablet_rdesc_template); + + /* Format fixed report descriptor */ + memcpy(drvdata->rdesc, huion_tablet_rdesc_template, + drvdata->rsize); + for (p = drvdata->rdesc; + p <= drvdata->rdesc + drvdata->rsize - 4;) { + if (p[0] == 0xFE && p[1] == 0xED && p[2] == 0x1D && + p[3] < sizeof(params)) { + v = params[p[3]]; + put_unaligned(cpu_to_le32(v), (s32 *)p); + p += 4; + } else { + p++; + } + } + } return 0; } static int huion_probe(struct hid_device *hdev, const struct hid_device_id *id) { - int ret; + int rc; struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct huion_drvdata *drvdata; + + /* Allocate and assign driver data */ + drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL); + if (drvdata == NULL) { + hid_err(hdev, "failed to allocate driver data\n"); + return -ENOMEM; + } + hid_set_drvdata(hdev, drvdata); - /* 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; + case USB_DEVICE_ID_HUION_TABLET: + /* If this is the pen interface */ + if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { + rc = huion_tablet_enable(hdev); + if (rc) { + hid_err(hdev, "tablet enabling failed\n"); + return rc; + } + } break; } - ret = hid_parse(hdev); - if (ret) { + rc = hid_parse(hdev); + if (rc) { hid_err(hdev, "parse failed\n"); - goto err; + return rc; } - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); - if (ret) { + rc = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (rc) { 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 rc; } 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) + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + + /* If this is a pen input report */ + if (intf->cur_altsetting->desc.bInterfaceNumber == 0 && + report->type == HID_INPUT_REPORT && + report->id == 0x07 && size >= 2) + /* Invert the in-range bit */ 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) }, + { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) }, { } }; MODULE_DEVICE_TABLE(hid, huion_devices); diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c index f52dbcb7133b..31fad641b744 100644 --- a/drivers/hid/hid-hyperv.c +++ b/drivers/hid/hid-hyperv.c @@ -308,6 +308,9 @@ static void mousevsc_on_receive(struct hv_device *device, memcpy(input_dev->input_buf, input_report->buffer, len); hid_input_report(input_dev->hid_device, HID_INPUT_REPORT, input_dev->input_buf, len, 1); + + pm_wakeup_event(&input_dev->device->device, 0); + break; default: pr_err("unsupported hid msg type - type %d len %d", @@ -549,6 +552,8 @@ static int mousevsc_probe(struct hv_device *device, goto probe_err2; } + device_init_wakeup(&device->device, true); + input_dev->connected = true; input_dev->init_complete = true; @@ -571,6 +576,7 @@ static int mousevsc_remove(struct hv_device *dev) { struct mousevsc_dev *input_dev = hv_get_drvdata(dev); + device_init_wakeup(&dev->device, false); vmbus_close(dev->channel); hid_hw_stop(input_dev->hid_device); hid_destroy_device(input_dev->hid_device); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 315891d07521..d53bdda26207 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -448,7 +448,7 @@ #define USB_DEVICE_ID_UGCI_FIGHTING 0x0030 #define USB_VENDOR_ID_HUION 0x256c -#define USB_DEVICE_ID_HUION_580 0x006e +#define USB_DEVICE_ID_HUION_TABLET 0x006e #define USB_VENDOR_ID_IDEACOM 0x1cb6 #define USB_DEVICE_ID_IDEACOM_IDC6650 0x6650 @@ -479,6 +479,7 @@ #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070 0xa070 #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072 0xa072 #define USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081 0xa081 +#define USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD_A096 0xa096 #define USB_VENDOR_ID_IMATION 0x0718 #define USB_DEVICE_ID_DISC_STAKKA 0xd000 @@ -489,6 +490,7 @@ #define USB_VENDOR_ID_JABRA 0x0b0e #define USB_DEVICE_ID_JABRA_SPEAK_410 0x0412 #define USB_DEVICE_ID_JABRA_SPEAK_510 0x0420 +#define USB_DEVICE_ID_JABRA_GN9350E 0x9350 #define USB_VENDOR_ID_JESS 0x0c45 #define USB_DEVICE_ID_JESS_YUREX 0x1010 diff --git a/drivers/hid/hid-picolcd_debugfs.c b/drivers/hid/hid-picolcd_debugfs.c index 024cdf3c2297..3c13af684410 100644 --- a/drivers/hid/hid-picolcd_debugfs.c +++ b/drivers/hid/hid-picolcd_debugfs.c @@ -883,16 +883,13 @@ void picolcd_exit_devfs(struct picolcd_data *data) dent = data->debug_reset; data->debug_reset = NULL; - if (dent) - debugfs_remove(dent); + debugfs_remove(dent); dent = data->debug_eeprom; data->debug_eeprom = NULL; - if (dent) - debugfs_remove(dent); + debugfs_remove(dent); dent = data->debug_flash; data->debug_flash = NULL; - if (dent) - debugfs_remove(dent); + debugfs_remove(dent); mutex_destroy(&data->mutex_flash); } diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index 578bbe65902b..0dc25142f451 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -377,7 +377,7 @@ static int rmi_input_event(struct hid_device *hdev, u8 *data, int size) irq_mask |= hdata->f30.irq_mask; if (data[1] & ~irq_mask) - hid_warn(hdev, "unknown intr source:%02lx %s:%d\n", + hid_dbg(hdev, "unknown intr source:%02lx %s:%d\n", data[1] & ~irq_mask, __FILE__, __LINE__); if (hdata->f11.interrupt_base < hdata->f30.interrupt_base) { @@ -400,7 +400,7 @@ static int rmi_read_data_event(struct hid_device *hdev, u8 *data, int size) struct rmi_data *hdata = hid_get_drvdata(hdev); if (!test_bit(RMI_READ_REQUEST_PENDING, &hdata->flags)) { - hid_err(hdev, "no read request pending\n"); + hid_dbg(hdev, "no read request pending\n"); return 0; } @@ -549,10 +549,12 @@ static int rmi_populate_f11(struct hid_device *hdev) u8 buf[20]; int ret; bool has_query9; - bool has_query10; + bool has_query10 = false; bool has_query11; bool has_query12; bool has_physical_props; + bool has_gestures; + bool has_rel; unsigned x_size, y_size; u16 query12_offset; @@ -589,19 +591,32 @@ static int rmi_populate_f11(struct hid_device *hdev) return -ENODEV; } - /* query 8 to find out if query 10 exists */ - ret = rmi_read(hdev, data->f11.query_base_addr + 8, buf); - if (ret) { - hid_err(hdev, "can not read gesture information: %d.\n", ret); - return ret; + has_rel = !!(buf[0] & BIT(3)); + has_gestures = !!(buf[0] & BIT(5)); + + if (has_gestures) { + /* query 8 to find out if query 10 exists */ + ret = rmi_read(hdev, data->f11.query_base_addr + 8, buf); + if (ret) { + hid_err(hdev, "can not read gesture information: %d.\n", + ret); + return ret; + } + has_query10 = !!(buf[0] & BIT(2)); } - has_query10 = !!(buf[0] & BIT(2)); /* - * At least 8 queries are guaranteed to be present in F11 - * +1 for query12. + * At least 4 queries are guaranteed to be present in F11 + * +1 for query 5 which is present since absolute events are + * reported and +1 for query 12. */ - query12_offset = 9; + query12_offset = 6; + + if (has_rel) + ++query12_offset; /* query 6 is present */ + + if (has_gestures) + query12_offset += 2; /* query 7 and 8 are present */ if (has_query9) ++query12_offset; @@ -833,6 +848,8 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) struct rmi_data *data = NULL; int ret; size_t alloc_size; + struct hid_report *input_report; + struct hid_report *output_report; data = devm_kzalloc(&hdev->dev, sizeof(struct rmi_data), GFP_KERNEL); if (!data) @@ -851,12 +868,26 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) return ret; } - data->input_report_size = (hdev->report_enum[HID_INPUT_REPORT] - .report_id_hash[RMI_ATTN_REPORT_ID]->size >> 3) - + 1 /* report id */; - data->output_report_size = (hdev->report_enum[HID_OUTPUT_REPORT] - .report_id_hash[RMI_WRITE_REPORT_ID]->size >> 3) - + 1 /* report id */; + input_report = hdev->report_enum[HID_INPUT_REPORT] + .report_id_hash[RMI_ATTN_REPORT_ID]; + if (!input_report) { + hid_err(hdev, "device does not have expected input report\n"); + ret = -ENODEV; + return ret; + } + + data->input_report_size = (input_report->size >> 3) + 1 /* report id */; + + output_report = hdev->report_enum[HID_OUTPUT_REPORT] + .report_id_hash[RMI_WRITE_REPORT_ID]; + if (!output_report) { + hid_err(hdev, "device does not have expected output report\n"); + ret = -ENODEV; + return ret; + } + + data->output_report_size = (output_report->size >> 3) + + 1 /* report id */; alloc_size = data->output_report_size + data->input_report_size; diff --git a/drivers/hid/hid-roccat-lua.c b/drivers/hid/hid-roccat-lua.c index 6adc0fa08d96..65e2e76bf2fe 100644 --- a/drivers/hid/hid-roccat-lua.c +++ b/drivers/hid/hid-roccat-lua.c @@ -61,7 +61,7 @@ static ssize_t lua_sysfs_write(struct file *fp, struct kobject *kobj, return -EINVAL; mutex_lock(&lua->lua_lock); - retval = roccat_common2_send(usb_dev, command, (void *)buf, real_size); + retval = roccat_common2_send(usb_dev, command, buf, real_size); mutex_unlock(&lua->lua_lock); return retval ? retval : real_size; diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 2259eaa8b988..c372368e438c 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -56,32 +56,81 @@ #define MAX_LEDS 4 -static const u8 sixaxis_rdesc_fixup[] = { - 0x95, 0x13, 0x09, 0x01, 0x81, 0x02, 0x95, 0x0C, - 0x81, 0x01, 0x75, 0x10, 0x95, 0x04, 0x26, 0xFF, - 0x03, 0x46, 0xFF, 0x03, 0x09, 0x01, 0x81, 0x02 -}; - -static const u8 sixaxis_rdesc_fixup2[] = { - 0x05, 0x01, 0x09, 0x04, 0xa1, 0x01, 0xa1, 0x02, - 0x85, 0x01, 0x75, 0x08, 0x95, 0x01, 0x15, 0x00, - 0x26, 0xff, 0x00, 0x81, 0x03, 0x75, 0x01, 0x95, - 0x13, 0x15, 0x00, 0x25, 0x01, 0x35, 0x00, 0x45, - 0x01, 0x05, 0x09, 0x19, 0x01, 0x29, 0x13, 0x81, - 0x02, 0x75, 0x01, 0x95, 0x0d, 0x06, 0x00, 0xff, - 0x81, 0x03, 0x15, 0x00, 0x26, 0xff, 0x00, 0x05, - 0x01, 0x09, 0x01, 0xa1, 0x00, 0x75, 0x08, 0x95, - 0x04, 0x35, 0x00, 0x46, 0xff, 0x00, 0x09, 0x30, - 0x09, 0x31, 0x09, 0x32, 0x09, 0x35, 0x81, 0x02, - 0xc0, 0x05, 0x01, 0x95, 0x13, 0x09, 0x01, 0x81, - 0x02, 0x95, 0x0c, 0x81, 0x01, 0x75, 0x10, 0x95, - 0x04, 0x26, 0xff, 0x03, 0x46, 0xff, 0x03, 0x09, - 0x01, 0x81, 0x02, 0xc0, 0xa1, 0x02, 0x85, 0x02, - 0x75, 0x08, 0x95, 0x30, 0x09, 0x01, 0xb1, 0x02, - 0xc0, 0xa1, 0x02, 0x85, 0xee, 0x75, 0x08, 0x95, - 0x30, 0x09, 0x01, 0xb1, 0x02, 0xc0, 0xa1, 0x02, - 0x85, 0xef, 0x75, 0x08, 0x95, 0x30, 0x09, 0x01, - 0xb1, 0x02, 0xc0, 0xc0, +static __u8 sixaxis_rdesc[] = { + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x04, /* Usage (Joystik), */ + 0xA1, 0x01, /* Collection (Application), */ + 0xA1, 0x02, /* Collection (Logical), */ + 0x85, 0x01, /* Report ID (1), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x01, /* Report Count (1), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x13, /* Report Count (19), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x35, 0x00, /* Physical Minimum (0), */ + 0x45, 0x01, /* Physical Maximum (1), */ + 0x05, 0x09, /* Usage Page (Button), */ + 0x19, 0x01, /* Usage Minimum (01h), */ + 0x29, 0x13, /* Usage Maximum (13h), */ + 0x81, 0x02, /* Input (Variable), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x0D, /* Report Count (13), */ + 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xA1, 0x00, /* Collection (Physical), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x04, /* Report Count (4), */ + 0x35, 0x00, /* Physical Minimum (0), */ + 0x46, 0xFF, 0x00, /* Physical Maximum (255), */ + 0x09, 0x30, /* Usage (X), */ + 0x09, 0x31, /* Usage (Y), */ + 0x09, 0x32, /* Usage (Z), */ + 0x09, 0x35, /* Usage (Rz), */ + 0x81, 0x02, /* Input (Variable), */ + 0xC0, /* End Collection, */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x95, 0x13, /* Report Count (19), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x0C, /* Report Count (12), */ + 0x81, 0x01, /* Input (Constant), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x04, /* Report Count (4), */ + 0x26, 0xFF, 0x03, /* Logical Maximum (1023), */ + 0x46, 0xFF, 0x03, /* Physical Maximum (1023), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0x81, 0x02, /* Input (Variable), */ + 0xC0, /* End Collection, */ + 0xA1, 0x02, /* Collection (Logical), */ + 0x85, 0x02, /* Report ID (2), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x30, /* Report Count (48), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0xC0, /* End Collection, */ + 0xA1, 0x02, /* Collection (Logical), */ + 0x85, 0xEE, /* Report ID (238), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x30, /* Report Count (48), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0xC0, /* End Collection, */ + 0xA1, 0x02, /* Collection (Logical), */ + 0x85, 0xEF, /* Report ID (239), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x30, /* Report Count (48), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ }; /* @@ -778,6 +827,13 @@ struct sony_sc { __u8 led_count; }; +static __u8 *sixaxis_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + *rsize = sizeof(sixaxis_rdesc); + return sixaxis_rdesc; +} + static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { @@ -819,8 +875,6 @@ static int ps3remote_mapping(struct hid_device *hdev, struct hid_input *hi, return 1; } - -/* Sony Vaio VGX has wrongly mouse pointer declared as constant */ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { @@ -857,20 +911,8 @@ static __u8 *sony_report_fixup(struct hid_device *hdev, __u8 *rdesc, *rsize = sizeof(dualshock4_bt_rdesc); } - /* The HID descriptor exposed over BT has a trailing zero byte */ - if ((((sc->quirks & SIXAXIS_CONTROLLER_USB) && *rsize == 148) || - ((sc->quirks & SIXAXIS_CONTROLLER_BT) && *rsize == 149)) && - rdesc[83] == 0x75) { - hid_info(hdev, "Fixing up Sony Sixaxis report descriptor\n"); - memcpy((void *)&rdesc[83], (void *)&sixaxis_rdesc_fixup, - sizeof(sixaxis_rdesc_fixup)); - } else if (sc->quirks & SIXAXIS_CONTROLLER_USB && - *rsize > sizeof(sixaxis_rdesc_fixup2)) { - hid_info(hdev, "Sony Sixaxis clone detected. Using original report descriptor (size: %d clone; %d new)\n", - *rsize, (int)sizeof(sixaxis_rdesc_fixup2)); - *rsize = sizeof(sixaxis_rdesc_fixup2); - memcpy(rdesc, &sixaxis_rdesc_fixup2, *rsize); - } + if (sc->quirks & SIXAXIS_CONTROLLER) + return sixaxis_fixup(hdev, rdesc, rsize); if (sc->quirks & PS3REMOTE) return ps3remote_fixup(hdev, rdesc, rsize); @@ -1307,7 +1349,7 @@ static int sony_leds_init(struct sony_sc *sc) static const char * const ds4_name_str[] = { "red", "green", "blue", "global" }; __u8 initial_values[MAX_LEDS] = { 0 }; - __u8 max_brightness[MAX_LEDS] = { 1 }; + __u8 max_brightness[MAX_LEDS] = { [0 ... (MAX_LEDS - 1)] = 1 }; __u8 use_hw_blink[MAX_LEDS] = { 0 }; BUG_ON(!(sc->quirks & SONY_LED_SUPPORT)); @@ -1830,9 +1872,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) if (sc->quirks & VAIO_RDESC_CONSTANT) connect_mask |= HID_CONNECT_HIDDEV_FORCE; - else if (sc->quirks & SIXAXIS_CONTROLLER_USB) - connect_mask |= HID_CONNECT_HIDDEV_FORCE; - else if (sc->quirks & SIXAXIS_CONTROLLER_BT) + else if (sc->quirks & SIXAXIS_CONTROLLER) connect_mask |= HID_CONNECT_HIDDEV_FORCE; ret = hid_hw_start(hdev, connect_mask); diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 21aafc8f48c8..747d54421e73 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -1054,21 +1054,29 @@ static int i2c_hid_remove(struct i2c_client *client) static int i2c_hid_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); + struct i2c_hid *ihid = i2c_get_clientdata(client); + struct hid_device *hid = ihid->hid; + int ret = 0; disable_irq(client->irq); if (device_may_wakeup(&client->dev)) enable_irq_wake(client->irq); + if (hid->driver && hid->driver->suspend) + ret = hid->driver->suspend(hid, PMSG_SUSPEND); + /* Save some power */ i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); - return 0; + return ret; } static int i2c_hid_resume(struct device *dev) { int ret; struct i2c_client *client = to_i2c_client(dev); + struct i2c_hid *ihid = i2c_get_clientdata(client); + struct hid_device *hid = ihid->hid; enable_irq(client->irq); ret = i2c_hid_hwreset(client); @@ -1078,6 +1086,11 @@ static int i2c_hid_resume(struct device *dev) if (device_may_wakeup(&client->dev)) disable_irq_wake(client->irq); + if (hid->driver && hid->driver->reset_resume) { + ret = hid->driver->reset_resume(hid); + return ret; + } + return 0; } #endif diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 7b88f4cb9902..79cf503e37bf 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -58,7 +58,7 @@ module_param_named(ignoreled, ignoreled, uint, 0644); MODULE_PARM_DESC(ignoreled, "Autosuspend with active leds"); /* Quirks specified at module load time */ -static char *quirks_param[MAX_USBHID_BOOT_QUIRKS] = { [ 0 ... (MAX_USBHID_BOOT_QUIRKS - 1) ] = NULL }; +static char *quirks_param[MAX_USBHID_BOOT_QUIRKS]; module_param_array_named(quirks, quirks_param, charp, NULL, 0444); MODULE_PARM_DESC(quirks, "Add/modify USB HID quirks by specifying " " quirks=vendorID:productID:quirks" @@ -536,7 +536,8 @@ static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *re int head; struct usbhid_device *usbhid = hid->driver_data; - if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN) + if (((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN) || + test_bit(HID_DISCONNECTED, &usbhid->iofl)) return; if (usbhid->urbout && dir == USB_DIR_OUT && report->type == HID_OUTPUT_REPORT) { @@ -1366,6 +1367,9 @@ static void usbhid_disconnect(struct usb_interface *intf) return; usbhid = hid->driver_data; + spin_lock_irq(&usbhid->lock); /* Sync with error and led handlers */ + set_bit(HID_DISCONNECTED, &usbhid->iofl); + spin_unlock_irq(&usbhid->lock); hid_destroy_device(hid); kfree(usbhid); } diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 31e6727cd009..0dd568170d6e 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -124,6 +124,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_HD, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_QUAD_HD, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP_V103, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD_A096, HID_QUIRK_NO_INIT_INPUT_REPORTS }, { 0, 0 } }; |