diff options
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/Kconfig | 18 | ||||
-rw-r--r-- | drivers/hid/Makefile | 2 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 11 | ||||
-rw-r--r-- | drivers/hid/hid-cp2112.c | 111 | ||||
-rw-r--r-- | drivers/hid/hid-huion.c | 265 | ||||
-rw-r--r-- | drivers/hid/hid-ids.h | 6 | ||||
-rw-r--r-- | drivers/hid/hid-lenovo-tpkbd.c | 462 | ||||
-rw-r--r-- | drivers/hid/hid-lenovo.c | 708 | ||||
-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 |
15 files changed, 1175 insertions, 642 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 5e79c6ad914f..e02cf59b048d 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -331,18 +331,20 @@ config HID_LCPOWER ---help--- Support for LC-Power RC1000MCE RF remote control. -config HID_LENOVO_TPKBD - tristate "Lenovo ThinkPad USB Keyboard with TrackPoint" +config HID_LENOVO + tristate "Lenovo / Thinkpad devices" depends on HID select NEW_LEDS select LEDS_CLASS ---help--- - Support for the Lenovo ThinkPad USB Keyboard with TrackPoint. + Support for Lenovo devices that are not fully compliant with HID standard. - Say Y here if you have a Lenovo ThinkPad USB Keyboard with TrackPoint - and would like to use device-specific features like changing the - sensitivity of the trackpoint, using the microphone mute button or - controlling the mute and microphone mute LEDs. + Say Y if you want support for the non-compliant features of the Lenovo + Thinkpad standalone keyboards, e.g: + - ThinkPad USB Keyboard with TrackPoint (supports extra LEDs and trackpoint + configuration) + - ThinkPad Compact Bluetooth Keyboard with TrackPoint (supports Fn keys) + - ThinkPad Compact USB Keyboard with TrackPoint (supports Fn keys) config HID_LOGITECH tristate "Logitech devices" if EXPERT @@ -785,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/Makefile b/drivers/hid/Makefile index a6fa6baf368e..5e96be3ab280 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -59,7 +59,7 @@ obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o obj-$(CONFIG_HID_KYE) += hid-kye.o obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o -obj-$(CONFIG_HID_LENOVO_TPKBD) += hid-lenovo-tpkbd.o +obj-$(CONFIG_HID_LENOVO) += hid-lenovo.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 8ed66fd1ea87..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) }, @@ -1796,8 +1798,10 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) }, { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000 ) }, -#if IS_ENABLED(CONFIG_HID_LENOVO_TPKBD) +#if IS_ENABLED(CONFIG_HID_LENOVO) { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CUSBKBD) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CBTKBD) }, #endif { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) }, @@ -2266,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-ids.h b/drivers/hid/hid-ids.h index 48b66bbffc94..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 @@ -561,6 +563,8 @@ #define USB_VENDOR_ID_LENOVO 0x17ef #define USB_DEVICE_ID_LENOVO_TPKBD 0x6009 +#define USB_DEVICE_ID_LENOVO_CUSBKBD 0x6047 +#define USB_DEVICE_ID_LENOVO_CBTKBD 0x6048 #define USB_VENDOR_ID_LG 0x1fd2 #define USB_DEVICE_ID_LG_MULTITOUCH 0x0064 diff --git a/drivers/hid/hid-lenovo-tpkbd.c b/drivers/hid/hid-lenovo-tpkbd.c deleted file mode 100644 index 2d25b6cbbc05..000000000000 --- a/drivers/hid/hid-lenovo-tpkbd.c +++ /dev/null @@ -1,462 +0,0 @@ -/* - * HID driver for Lenovo ThinkPad USB Keyboard with TrackPoint - * - * Copyright (c) 2012 Bernhard Seibold - */ - -/* - * 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/module.h> -#include <linux/sysfs.h> -#include <linux/device.h> -#include <linux/hid.h> -#include <linux/input.h> -#include <linux/leds.h> - -#include "hid-ids.h" - -/* This is only used for the trackpoint part of the driver, hence _tp */ -struct tpkbd_data_pointer { - int led_state; - struct led_classdev led_mute; - struct led_classdev led_micmute; - int press_to_select; - int dragging; - int release_to_select; - int select_right; - int sensitivity; - int press_speed; -}; - -#define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c)) - -static int tpkbd_input_mapping(struct hid_device *hdev, - struct hid_input *hi, struct hid_field *field, - struct hid_usage *usage, unsigned long **bit, int *max) -{ - if (usage->hid == (HID_UP_BUTTON | 0x0010)) { - /* mark the device as pointer */ - hid_set_drvdata(hdev, (void *)1); - map_key_clear(KEY_MICMUTE); - return 1; - } - return 0; -} - -#undef map_key_clear - -static int tpkbd_features_set(struct hid_device *hdev) -{ - struct hid_report *report; - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - - report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4]; - - report->field[0]->value[0] = data_pointer->press_to_select ? 0x01 : 0x02; - report->field[0]->value[0] |= data_pointer->dragging ? 0x04 : 0x08; - report->field[0]->value[0] |= data_pointer->release_to_select ? 0x10 : 0x20; - report->field[0]->value[0] |= data_pointer->select_right ? 0x80 : 0x40; - report->field[1]->value[0] = 0x03; // unknown setting, imitate windows driver - report->field[2]->value[0] = data_pointer->sensitivity; - report->field[3]->value[0] = data_pointer->press_speed; - - hid_hw_request(hdev, report, HID_REQ_SET_REPORT); - return 0; -} - -static ssize_t pointer_press_to_select_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - - return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select); -} - -static ssize_t pointer_press_to_select_store(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t count) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - int value; - - if (kstrtoint(buf, 10, &value)) - return -EINVAL; - if (value < 0 || value > 1) - return -EINVAL; - - data_pointer->press_to_select = value; - tpkbd_features_set(hdev); - - return count; -} - -static ssize_t pointer_dragging_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - - return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging); -} - -static ssize_t pointer_dragging_store(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t count) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - int value; - - if (kstrtoint(buf, 10, &value)) - return -EINVAL; - if (value < 0 || value > 1) - return -EINVAL; - - data_pointer->dragging = value; - tpkbd_features_set(hdev); - - return count; -} - -static ssize_t pointer_release_to_select_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - - return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select); -} - -static ssize_t pointer_release_to_select_store(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t count) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - int value; - - if (kstrtoint(buf, 10, &value)) - return -EINVAL; - if (value < 0 || value > 1) - return -EINVAL; - - data_pointer->release_to_select = value; - tpkbd_features_set(hdev); - - return count; -} - -static ssize_t pointer_select_right_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - - return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right); -} - -static ssize_t pointer_select_right_store(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t count) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - int value; - - if (kstrtoint(buf, 10, &value)) - return -EINVAL; - if (value < 0 || value > 1) - return -EINVAL; - - data_pointer->select_right = value; - tpkbd_features_set(hdev); - - return count; -} - -static ssize_t pointer_sensitivity_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - - return snprintf(buf, PAGE_SIZE, "%u\n", - data_pointer->sensitivity); -} - -static ssize_t pointer_sensitivity_store(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t count) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - int value; - - if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) - return -EINVAL; - - data_pointer->sensitivity = value; - tpkbd_features_set(hdev); - - return count; -} - -static ssize_t pointer_press_speed_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - - return snprintf(buf, PAGE_SIZE, "%u\n", - data_pointer->press_speed); -} - -static ssize_t pointer_press_speed_store(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t count) -{ - struct hid_device *hdev = container_of(dev, struct hid_device, dev); - struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev); - int value; - - if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) - return -EINVAL; - - data_pointer->press_speed = value; - tpkbd_features_set(hdev); - - return count; -} - -static struct device_attribute dev_attr_pointer_press_to_select = - __ATTR(press_to_select, S_IWUSR | S_IRUGO, - pointer_press_to_select_show, - pointer_press_to_select_store); - -static struct device_attribute dev_attr_pointer_dragging = - __ATTR(dragging, S_IWUSR | S_IRUGO, - pointer_dragging_show, - pointer_dragging_store); - -static struct device_attribute dev_attr_pointer_release_to_select = - __ATTR(release_to_select, S_IWUSR | S_IRUGO, - pointer_release_to_select_show, - pointer_release_to_select_store); - -static struct device_attribute dev_attr_pointer_select_right = - __ATTR(select_right, S_IWUSR | S_IRUGO, - pointer_select_right_show, - pointer_select_right_store); - -static struct device_attribute dev_attr_pointer_sensitivity = - __ATTR(sensitivity, S_IWUSR | S_IRUGO, - pointer_sensitivity_show, - pointer_sensitivity_store); - -static struct device_attribute dev_attr_pointer_press_speed = - __ATTR(press_speed, S_IWUSR | S_IRUGO, - pointer_press_speed_show, - pointer_press_speed_store); - -static struct attribute *tpkbd_attributes_pointer[] = { - &dev_attr_pointer_press_to_select.attr, - &dev_attr_pointer_dragging.attr, - &dev_attr_pointer_release_to_select.attr, - &dev_attr_pointer_select_right.attr, - &dev_attr_pointer_sensitivity.attr, - &dev_attr_pointer_press_speed.attr, - NULL -}; - -static const struct attribute_group tpkbd_attr_group_pointer = { - .attrs = tpkbd_attributes_pointer, -}; - -static enum led_brightness tpkbd_led_brightness_get( - struct led_classdev *led_cdev) -{ - struct device *dev = led_cdev->dev->parent; - struct hid_device *hdev = container_of(dev, struct hid_device, dev); |