diff options
Diffstat (limited to 'drivers/hid')
29 files changed, 1172 insertions, 95 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 6ab51ae3c39d..4135055d7203 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -171,6 +171,16 @@ config HID_CHICONY ---help--- Support for Chicony Tactical pad. +config HID_CORSAIR + tristate "Corsair devices" + depends on HID && USB && LEDS_CLASS + ---help--- + Support for Corsair devices that are not fully compliant with the + HID standard. + + Supported devices: + - Vengeance K90 + config HID_PRODIKEYS tristate "Prodikeys PC-MIDI Keyboard support" depends on HID && SND @@ -672,9 +682,8 @@ config HID_SAITEK Supported devices: - PS1000 Dual Analog Pad - - R.A.T.9 Gaming Mouse - - R.A.T.7 Gaming Mouse - - M.M.O.7 Gaming Mouse + - Saitek R.A.T.7, R.A.T.9, M.M.O.7 Gaming Mice + - Mad Catz R.A.T.5, R.A.T.9 Gaming Mice config HID_SAMSUNG tristate "Samsung InfraRed remote control or keyboards" diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index e6441bc7dae4..edaa0f264bde 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_HID_BELKIN) += hid-belkin.o obj-$(CONFIG_HID_BETOP_FF) += hid-betopff.o obj-$(CONFIG_HID_CHERRY) += hid-cherry.o obj-$(CONFIG_HID_CHICONY) += hid-chicony.o +obj-$(CONFIG_HID_CORSAIR) += hid-corsair.o obj-$(CONFIG_HID_CP2112) += hid-cp2112.o obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o obj-$(CONFIG_HID_DRAGONRISE) += hid-dr.o diff --git a/drivers/hid/hid-appleir.c b/drivers/hid/hid-appleir.c index 0e6a42d37eb6..07cbc70f00e7 100644 --- a/drivers/hid/hid-appleir.c +++ b/drivers/hid/hid-appleir.c @@ -256,7 +256,7 @@ out: return 0; } -static void appleir_input_configured(struct hid_device *hid, +static int appleir_input_configured(struct hid_device *hid, struct hid_input *hidinput) { struct input_dev *input_dev = hidinput->input; @@ -275,6 +275,8 @@ static void appleir_input_configured(struct hid_device *hid, for (i = 0; i < ARRAY_SIZE(appleir_key_table); i++) set_bit(appleir->keymap[i], input_dev->keybit); clear_bit(KEY_RESERVED, input_dev->keybit); + + return 0; } static int appleir_input_mapping(struct hid_device *hid, diff --git a/drivers/hid/hid-aureal.c b/drivers/hid/hid-aureal.c index 340ba9d394a0..3280aff28e90 100644 --- a/drivers/hid/hid-aureal.c +++ b/drivers/hid/hid-aureal.c @@ -23,7 +23,8 @@ static __u8 *aureal_report_fixup(struct hid_device *hdev, __u8 *rdesc, if (*rsize >= 54 && rdesc[52] == 0x25 && rdesc[53] == 0x01) { dev_info(&hdev->dev, "fixing Aureal Cy se W-01RN USB_V3.1 report descriptor.\n"); rdesc[53] = 0x65; - } return rdesc; + } + return rdesc; } static const struct hid_device_id aureal_devices[] = { diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index bcd914a63af2..0bd972b7614c 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -725,6 +725,7 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type) if (hid->vendor == USB_VENDOR_ID_MICROSOFT && (hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3 || + hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 || hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP || hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 || hid->product == USB_DEVICE_ID_MS_POWER_COVER) && @@ -1611,7 +1612,7 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask) "Multi-Axis Controller" }; const char *type, *bus; - char buf[64]; + char buf[64] = ""; unsigned int i; int len; int ret; @@ -1678,6 +1679,9 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask) case BUS_BLUETOOTH: bus = "BLUETOOTH"; break; + case BUS_I2C: + bus = "I2C"; + break; default: bus = "<UNKNOWN>"; } @@ -1828,6 +1832,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS2) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_AK1D) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_ACER_SWITCH12) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K90) }, { HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_PRODIKEYS_PCMIDI) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_CP2112) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) }, @@ -1896,6 +1901,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G29_WHEEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) }, @@ -1928,6 +1934,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER) }, @@ -1981,6 +1988,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7_OLD) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT5) }, { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT9) }, #endif { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, diff --git a/drivers/hid/hid-corsair.c b/drivers/hid/hid-corsair.c new file mode 100644 index 000000000000..bcefb9ebb026 --- /dev/null +++ b/drivers/hid/hid-corsair.c @@ -0,0 +1,673 @@ +/* + * HID driver for Corsair devices + * + * Supported devices: + * - Vengeance K90 Keyboard + * + * Copyright (c) 2015 Clement Vuchener + */ + +/* + * 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 <linux/leds.h> + +#include "hid-ids.h" + +#define CORSAIR_USE_K90_MACRO (1<<0) +#define CORSAIR_USE_K90_BACKLIGHT (1<<1) + +struct k90_led { + struct led_classdev cdev; + int brightness; + struct work_struct work; + bool removed; +}; + +struct k90_drvdata { + struct k90_led record_led; +}; + +struct corsair_drvdata { + unsigned long quirks; + struct k90_drvdata *k90; + struct k90_led *backlight; +}; + +#define K90_GKEY_COUNT 18 + +static int corsair_usage_to_gkey(unsigned int usage) +{ + /* G1 (0xd0) to G16 (0xdf) */ + if (usage >= 0xd0 && usage <= 0xdf) + return usage - 0xd0 + 1; + /* G17 (0xe8) to G18 (0xe9) */ + if (usage >= 0xe8 && usage <= 0xe9) + return usage - 0xe8 + 17; + return 0; +} + +static unsigned short corsair_gkey_map[K90_GKEY_COUNT] = { + BTN_TRIGGER_HAPPY1, + BTN_TRIGGER_HAPPY2, + BTN_TRIGGER_HAPPY3, + BTN_TRIGGER_HAPPY4, + BTN_TRIGGER_HAPPY5, + BTN_TRIGGER_HAPPY6, + BTN_TRIGGER_HAPPY7, + BTN_TRIGGER_HAPPY8, + BTN_TRIGGER_HAPPY9, + BTN_TRIGGER_HAPPY10, + BTN_TRIGGER_HAPPY11, + BTN_TRIGGER_HAPPY12, + BTN_TRIGGER_HAPPY13, + BTN_TRIGGER_HAPPY14, + BTN_TRIGGER_HAPPY15, + BTN_TRIGGER_HAPPY16, + BTN_TRIGGER_HAPPY17, + BTN_TRIGGER_HAPPY18, +}; + +module_param_array_named(gkey_codes, corsair_gkey_map, ushort, NULL, S_IRUGO); +MODULE_PARM_DESC(gkey_codes, "Key codes for the G-keys"); + +static unsigned short corsair_record_keycodes[2] = { + BTN_TRIGGER_HAPPY19, + BTN_TRIGGER_HAPPY20 +}; + +module_param_array_named(recordkey_codes, corsair_record_keycodes, ushort, + NULL, S_IRUGO); +MODULE_PARM_DESC(recordkey_codes, "Key codes for the MR (start and stop record) button"); + +static unsigned short corsair_profile_keycodes[3] = { + BTN_TRIGGER_HAPPY21, + BTN_TRIGGER_HAPPY22, + BTN_TRIGGER_HAPPY23 +}; + +module_param_array_named(profilekey_codes, corsair_profile_keycodes, ushort, + NULL, S_IRUGO); +MODULE_PARM_DESC(profilekey_codes, "Key codes for the profile buttons"); + +#define CORSAIR_USAGE_SPECIAL_MIN 0xf0 +#define CORSAIR_USAGE_SPECIAL_MAX 0xff + +#define CORSAIR_USAGE_MACRO_RECORD_START 0xf6 +#define CORSAIR_USAGE_MACRO_RECORD_STOP 0xf7 + +#define CORSAIR_USAGE_PROFILE 0xf1 +#define CORSAIR_USAGE_M1 0xf1 +#define CORSAIR_USAGE_M2 0xf2 +#define CORSAIR_USAGE_M3 0xf3 +#define CORSAIR_USAGE_PROFILE_MAX 0xf3 + +#define CORSAIR_USAGE_META_OFF 0xf4 +#define CORSAIR_USAGE_META_ON 0xf5 + +#define CORSAIR_USAGE_LIGHT 0xfa +#define CORSAIR_USAGE_LIGHT_OFF 0xfa +#define CORSAIR_USAGE_LIGHT_DIM 0xfb +#define CORSAIR_USAGE_LIGHT_MEDIUM 0xfc +#define CORSAIR_USAGE_LIGHT_BRIGHT 0xfd +#define CORSAIR_USAGE_LIGHT_MAX 0xfd + +/* USB control protocol */ + +#define K90_REQUEST_BRIGHTNESS 49 +#define K90_REQUEST_MACRO_MODE 2 +#define K90_REQUEST_STATUS 4 +#define K90_REQUEST_GET_MODE 5 +#define K90_REQUEST_PROFILE 20 + +#define K90_MACRO_MODE_SW 0x0030 +#define K90_MACRO_MODE_HW 0x0001 + +#define K90_MACRO_LED_ON 0x0020 +#define K90_MACRO_LED_OFF 0x0040 + +/* + * LED class devices + */ + +#define K90_BACKLIGHT_LED_SUFFIX "::backlight" +#define K90_RECORD_LED_SUFFIX "::record" + +static enum led_brightness k90_backlight_get(struct led_classdev *led_cdev) +{ + int ret; + struct k90_led *led = container_of(led_cdev, struct k90_led, cdev); + struct device *dev = led->cdev.dev->parent; + struct usb_interface *usbif = to_usb_interface(dev->parent); + struct usb_device *usbdev = interface_to_usbdev(usbif); + int brightness; + char data[8]; + + ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), + K90_REQUEST_STATUS, + USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, 0, 0, data, 8, + USB_CTRL_SET_TIMEOUT); + if (ret < 0) { + dev_warn(dev, "Failed to get K90 initial state (error %d).\n", + ret); + return -EIO; + } + brightness = data[4]; + if (brightness < 0 || brightness > 3) { + dev_warn(dev, + "Read invalid backlight brightness: %02hhx.\n", + data[4]); + return -EIO; + } + return brightness; +} + +static enum led_brightness k90_record_led_get(struct led_classdev *led_cdev) +{ + struct k90_led *led = container_of(led_cdev, struct k90_led, cdev); + + return led->brightness; +} + +static void k90_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct k90_led *led = container_of(led_cdev, struct k90_led, cdev); + + led->brightness = brightness; + schedule_work(&led->work); +} + +static void k90_backlight_work(struct work_struct *work) +{ + int ret; + struct k90_led *led = container_of(work, struct k90_led, work); + struct device *dev; + struct usb_interface *usbif; + struct usb_device *usbdev; + + if (led->removed) + return; + + dev = led->cdev.dev->parent; + usbif = to_usb_interface(dev->parent); + usbdev = interface_to_usbdev(usbif); + + ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), + K90_REQUEST_BRIGHTNESS, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, led->brightness, 0, + NULL, 0, USB_CTRL_SET_TIMEOUT); + if (ret != 0) + dev_warn(dev, "Failed to set backlight brightness (error: %d).\n", + ret); +} + +static void k90_record_led_work(struct work_struct *work) +{ + int ret; + struct k90_led *led = container_of(work, struct k90_led, work); + struct device *dev; + struct usb_interface *usbif; + struct usb_device *usbdev; + int value; + + if (led->removed) + return; + + dev = led->cdev.dev->parent; + usbif = to_usb_interface(dev->parent); + usbdev = interface_to_usbdev(usbif); + + if (led->brightness > 0) + value = K90_MACRO_LED_ON; + else + value = K90_MACRO_LED_OFF; + + ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), + K90_REQUEST_MACRO_MODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); + if (ret != 0) + dev_warn(dev, "Failed to set record LED state (error: %d).\n", + ret); +} + +/* + * Keyboard attributes + */ + +static ssize_t k90_show_macro_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + struct usb_interface *usbif = to_usb_interface(dev->parent); + struct usb_device *usbdev = interface_to_usbdev(usbif); + const char *macro_mode; + char data[8]; + + ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), + K90_REQUEST_GET_MODE, + USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, 0, 0, data, 2, + USB_CTRL_SET_TIMEOUT); + if (ret < 0) { + dev_warn(dev, "Failed to get K90 initial mode (error %d).\n", + ret); + return -EIO; + } + + switch (data[0]) { + case K90_MACRO_MODE_HW: + macro_mode = "HW"; + break; + + case K90_MACRO_MODE_SW: + macro_mode = "SW"; + break; + default: + dev_warn(dev, "K90 in unknown mode: %02hhx.\n", + data[0]); + return -EIO; + } + + return snprintf(buf, PAGE_SIZE, "%s\n", macro_mode); +} + +static ssize_t k90_store_macro_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct usb_interface *usbif = to_usb_interface(dev->parent); + struct usb_device *usbdev = interface_to_usbdev(usbif); + __u16 value; + + if (strncmp(buf, "SW", 2) == 0) + value = K90_MACRO_MODE_SW; + else if (strncmp(buf, "HW", 2) == 0) + value = K90_MACRO_MODE_HW; + else + return -EINVAL; + + ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), + K90_REQUEST_MACRO_MODE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); + if (ret != 0) { + dev_warn(dev, "Failed to set macro mode.\n"); + return ret; + } + + return count; +} + +static ssize_t k90_show_current_profile(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + struct usb_interface *usbif = to_usb_interface(dev->parent); + struct usb_device *usbdev = interface_to_usbdev(usbif); + int current_profile; + char data[8]; + + ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), + K90_REQUEST_STATUS, + USB_DIR_IN | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, 0, 0, data, 8, + USB_CTRL_SET_TIMEOUT); + if (ret < 0) { + dev_warn(dev, "Failed to get K90 initial state (error %d).\n", + ret); + return -EIO; + } + current_profile = data[7]; + if (current_profile < 1 || current_profile > 3) { + dev_warn(dev, "Read invalid current profile: %02hhx.\n", + data[7]); + return -EIO; + } + + return snprintf(buf, PAGE_SIZE, "%d\n", current_profile); +} + +static ssize_t k90_store_current_profile(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct usb_interface *usbif = to_usb_interface(dev->parent); + struct usb_device *usbdev = interface_to_usbdev(usbif); + int profile; + + if (kstrtoint(buf, 10, &profile)) + return -EINVAL; + if (profile < 1 || profile > 3) + return -EINVAL; + + ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), + K90_REQUEST_PROFILE, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, profile, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); + if (ret != 0) { + dev_warn(dev, "Failed to change current profile (error %d).\n", + ret); + return ret; + } + + return count; +} + +static DEVICE_ATTR(macro_mode, 0644, k90_show_macro_mode, k90_store_macro_mode); +static DEVICE_ATTR(current_profile, 0644, k90_show_current_profile, + k90_store_current_profile); + +static struct attribute *k90_attrs[] = { + &dev_attr_macro_mode.attr, + &dev_attr_current_profile.attr, + NULL +}; + +static const struct attribute_group k90_attr_group = { + .attrs = k90_attrs, +}; + +/* + * Driver functions + */ + +static int k90_init_backlight(struct hid_device *dev) +{ + int ret; + struct corsair_drvdata *drvdata = hid_get_drvdata(dev); + size_t name_sz; + char *name; + + drvdata->backlight = kzalloc(sizeof(struct k90_led), GFP_KERNEL); + if (!drvdata->backlight) { + ret = -ENOMEM; + goto fail_backlight_alloc; + } + + name_sz = + strlen(dev_name(&dev->dev)) + sizeof(K90_BACKLIGHT_LED_SUFFIX); + name = kzalloc(name_sz, GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto fail_name_alloc; + } + snprintf(name, name_sz, "%s" K90_BACKLIGHT_LED_SUFFIX, + dev_name(&dev->dev)); + drvdata->backlight->removed = false; + drvdata->backlight->cdev.name = name; + drvdata->backlight->cdev.max_brightness = 3; + drvdata->backlight->cdev.brightness_set = k90_brightness_set; + drvdata->backlight->cdev.brightness_get = k90_backlight_get; + INIT_WORK(&drvdata->backlight->work, k90_backlight_work); + ret = led_classdev_register(&dev->dev, &drvdata->backlight->cdev); + if (ret != 0) + goto fail_register_cdev; + + return 0; + +fail_register_cdev: + kfree(drvdata->backlight->cdev.name); +fail_name_alloc: + kfree(drvdata->backlight); + drvdata->backlight = NULL; +fail_backlight_alloc: + return ret; +} + +static int k90_init_macro_functions(struct hid_device *dev) +{ + int ret; + struct corsair_drvdata *drvdata = hid_get_drvdata(dev); + struct k90_drvdata *k90; + size_t name_sz; + char *name; + + k90 = kzalloc(sizeof(struct k90_drvdata), GFP_KERNEL); + if (!k90) { + ret = -ENOMEM; + goto fail_drvdata; + } + drvdata->k90 = k90; + + /* Init LED device for record LED */ + name_sz = strlen(dev_name(&dev->dev)) + sizeof(K90_RECORD_LED_SUFFIX); + name = kzalloc(name_sz, GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto fail_record_led_alloc; + } + snprintf(name, name_sz, "%s" K90_RECORD_LED_SUFFIX, + dev_name(&dev->dev)); + k90->record_led.removed = false; + k90->record_led.cdev.name = name; + k90->record_led.cdev.max_brightness = 1; + k90->record_led.cdev.brightness_set = k90_brightness_set; + k90->record_led.cdev.brightness_get = k90_record_led_get; + INIT_WORK(&k90->record_led.work, k90_record_led_work); + k90->record_led.brightness = 0; + ret = led_classdev_register(&dev->dev, &k90->record_led.cdev); + if (ret != 0) + goto fail_record_led; + + /* Init attributes */ + ret = sysfs_create_group(&dev->dev.kobj, &k90_attr_group); + if (ret != 0) + goto fail_sysfs; + + return 0; + +fail_sysfs: + k90->record_led.removed = true; + led_classdev_unregister(&k90->record_led.cdev); + cancel_work_sync(&k90->record_led.work); +fail_record_led: + kfree(k90->record_led.cdev.name); +fail_record_led_alloc: + kfree(k90); +fail_drvdata: + drvdata->k90 = NULL; + return ret; +} + +static void k90_cleanup_backlight(struct hid_device *dev) +{ + struct corsair_drvdata *drvdata = hid_get_drvdata(dev); + + if (drvdata->backlight) { + drvdata->backlight->removed = true; + led_classdev_unregister(&drvdata->backlight->cdev); + cancel_work_sync(&drvdata->backlight->work); + kfree(drvdata->backlight->cdev.name); + kfree(drvdata->backlight); + } +} + +static void k90_cleanup_macro_functions(struct hid_device *dev) +{ + struct corsair_drvdata *drvdata = hid_get_drvdata(dev); + struct k90_drvdata *k90 = drvdata->k90; + + if (k90) { + sysfs_remove_group(&dev->dev.kobj, &k90_attr_group); + + k90->record_led.removed = true; + led_classdev_unregister(&k90->record_led.cdev); + cancel_work_sync(&k90->record_led.work); + kfree(k90->record_led.cdev.name); + + kfree(k90); + } +} + +static int corsair_probe(struct hid_device *dev, const struct hid_device_id *id) +{ + int ret; + unsigned long quirks = id->driver_data; + struct corsair_drvdata *drvdata; + struct usb_interface *usbif = to_usb_interface(dev->dev.parent); + + drvdata = devm_kzalloc(&dev->dev, sizeof(struct corsair_drvdata), + GFP_KERNEL); + if (drvdata == NULL) + return -ENOMEM; + drvdata->quirks = quirks; + hid_set_drvdata(dev, drvdata); + + ret = hid_parse(dev); + if (ret != 0) { + hid_err(dev, "parse failed\n"); + return ret; + } + ret = hid_hw_start(dev, HID_CONNECT_DEFAULT); + if (ret != 0) { + hid_err(dev, "hw start failed\n"); + return ret; + } + + if (usbif->cur_altsetting->desc.bInterfaceNumber == 0) { + if (quirks & CORSAIR_USE_K90_MACRO) { + ret = k90_init_macro_functions(dev); + if (ret != 0) + hid_warn(dev, "Failed to initialize K90 macro functions.\n"); + } + if (quirks & CORSAIR_USE_K90_BACKLIGHT) { + ret = k90_init_backlight(dev); + if (ret != 0) + hid_warn(dev, "Failed to initialize K90 backlight.\n"); + } + } + + return 0; +} + +static void corsair_remove(struct hid_device *dev) +{ + k90_cleanup_macro_functions(dev); + k90_cleanup_backlight(dev); + + hid_hw_stop(dev); +} + +static int corsair_event(struct hid_device *dev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct corsair_drvdata *drvdata = hid_get_drvdata(dev); + + if (!drvdata->k90) + return 0; + + switch (usage->hid & HID_USAGE) { + case CORSAIR_USAGE_MACRO_RECORD_START: + drvdata->k90->record_led.brightness = 1; + break; + case CORSAIR_USAGE_MACRO_RECORD_STOP: + drvdata->k90->record_led.brightness = 0; + break; + default: + break; + } + + return 0; +} + +static int corsair_input_mapping(struct hid_device *dev, + struct hid_input *input, + struct hid_field *field, + struct hid_usage *usage, unsigned long **bit, + int *max) +{ + int gkey; + + gkey = corsair_usage_to_gkey(usage->hid & HID_USAGE); + if (gkey != 0) { + hid_map_usage_clear(input, usage, bit, max, EV_KEY, + corsair_gkey_map[gkey - 1]); + return 1; + } + if ((usage->hid & HID_USAGE) >= CORSAIR_USAGE_SPECIAL_MIN && + (usage->hid & HID_USAGE) <= CORSAIR_USAGE_SPECIAL_MAX) { + switch (usage->hid & HID_USAGE) { + case CORSAIR_USAGE_MACRO_RECORD_START: + hid_map_usage_clear(input, usage, bit, max, EV_KEY, + corsair_record_keycodes[0]); + return 1; + + case CORSAIR_USAGE_MACRO_RECORD_STOP: + hid_map_usage_clear(input, usage, bit, max, EV_KEY, + corsair_record_keycodes[1]); + return 1; + + case CORSAIR_USAGE_M1: + hid_map_usage_clear(input, usage, bit, max, EV_KEY, + corsair_profile_keycodes[0]); + return 1; + + case CORSAIR_USAGE_M2: + hid_map_usage_clear(input, usage, bit, max, EV_KEY, + corsair_profile_keycodes[1]); + return 1; + + case CORSAIR_USAGE_M3: + hid_map_usage_clear(input, usage, bit, max, EV_KEY, + corsair_profile_keycodes[2]); + return 1; + + default: + return -1; + } + } + + return 0; +} + +static const struct hid_device_id corsair_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K90), + .driver_data = CORSAIR_USE_K90_MACRO | + CORSAIR_USE_K90_BACKLIGHT }, + {} +}; + +MODULE_DEVICE_TABLE(hid, corsair_devices); + +static struct hid_driver corsair_driver = { + .name = "corsair", + .id_table = corsair_devices, + .probe = corsair_probe, + .event = corsair_event, + .remove = corsair_remove, + .input_mapping = corsair_input_mapping, +}; + +static int __init corsair_init(void) +{ + return hid_register_driver(&corsair_driver); +} + +static void corsair_exit(void) +{ + hid_unregister_driver(&corsair_driver); +} + +module_init(corsair_init); +module_exit(corsair_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Clement Vuchener"); +MODULE_DESCRIPTION("HID driver for Corsair devices"); diff --git a/drivers/hid/hid-dr.c b/drivers/hid/hid-dr.c index ce0644424f58..1d78ba3b799e 100644 --- a/drivers/hid/hid-dr.c +++ b/drivers/hid/hid-dr.c @@ -234,6 +234,58 @@ static __u8 pid0011_rdesc_fixed[] = { 0xC0 /* End Collection */ }; +static __u8 pid0006_rdesc_fixed[] = { + 0x05, 0x01, /* Usage Page (Generic Desktop) */ + 0x09, 0x04, /* Usage (Joystick) */ + 0xA1, 0x01, /* Collection (Application) */ + 0xA1, 0x02, /* Collection (Logical) */ + 0x75, 0x08, /* Report Size (8) */ + 0x95, 0x05, /* Report Count (5) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255) */ + 0x35, 0x00, /* Physical Minimum (0) */ + 0x46, 0xFF, 0x00, /* Physical Maximum (255) */ + 0x09, 0x30, /* Usage (X) */ + 0x09, 0x33, /* Usage (Ry) */ + 0x09, 0x32, /* Usage (Z) */ + 0x09, 0x31, /* Usage (Y) */ + 0x09, 0x34, /* Usage (Ry) */ + 0x81, 0x02, /* Input (Variable) */ + 0x75, 0x04, /* Report Size (4) */ + 0x95, 0x01, /* Report Count (1) */ + 0x25, 0x07, /* Logical Maximum (7) */ + 0x46, 0x3B, 0x01, /* Physical Maximum (315) */ + 0x65, 0x14, /* Unit (Centimeter) */ + 0x09, 0x39, /* Usage (Hat switch) */ + 0x81, 0x42, /* Input (Variable) */ + 0x65, 0x00, /* Unit (None) */ + 0x75, 0x01, /* Report Size (1) */ + 0x95, 0x0C, /* Report Count (12) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x45, 0x01, /* Physical Maximum (1) */ + 0x05, 0x09, /* Usage Page (Button) */ + 0x19, 0x01, /* Usage Minimum (0x01) */ + 0x29, 0x0C, /* Usage Maximum (0x0C) */ + 0x81, 0x02, /* Input (Variable) */ + 0x06, 0x00, 0xFF, /* Usage Page (Vendor Defined) */ + 0x75, 0x01, /* Report Size (1) */ + 0x95, 0x08, /* Report Count (8) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x45, 0x01, /* Physical Maximum (1) */ + 0x09, 0x01, /* Usage (0x01) */ + 0x81, 0x02, /* Input (Variable) */ + 0xC0, /* End Collection */ + 0xA1, 0x02, /* Collection (Logical) */ + 0x75, 0x08, /* Report Size (8) */ + 0x95, 0x07, /* Report Count (7) */ + 0x46, 0xFF, 0x00, /* Physical Maximum (255) */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255) */ + 0x09, 0x02, /* Usage (0x02) */ + 0x91, 0x02, /* Output (Variable) */ + 0xC0, /* End Collection */ + 0xC0 /* End Collection */ +}; + static __u8 *dr_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { @@ -244,6 +296,12 @@ static __u8 *dr_report_fixup(struct hid_device *hdev, __u8 *rdesc, *rsize = sizeof(pid0011_rdesc_fixed); } break; + case 0x0006: + if (*rsize == sizeof(pid0006_rdesc_fixed)) { + rdesc = pid0006_rdesc_fixed; + *rsize = sizeof(pid0006_rdesc_fixed); + } + break; } return rdesc; } diff --git a/drivers/hid/hid-elecom.c b/drivers/hid/hid-elecom.c index d0bd13b62dc2..6e3848a8d8dd 100644 --- a/drivers/hid/hid-elecom.c +++ b/drivers/hid/hid-elecom.c @@ -27,7 +27,7 @@ static __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc, hid_info(hdev, "Fixing up Elecom BM084 report descriptor\n"); rdesc[47] = 0x00; } - return rdesc; + return rdesc; } static const struct hid_device_id elecom_devices[] = { diff --git a/drivers/hid/hid-elo.c b/drivers/hid/hid-elo.c index 4e49462870ab..aad8c162a825 100644 --- a/drivers/hid/hid-elo.c +++ b/drivers/hid/hid-elo.c @@ -37,7 +37,7 @@ 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, +static int elo_input_configured(struct hid_device *hdev, struct hid_input *hidinput) { struct input_dev *input = hidinput->input; @@ -45,6 +45,8 @@ static void elo_input_configured(struct hid_device *hdev, set_bit(BTN_TOUCH, input->keybit); set_bit(ABS_PRESSURE, input->absbit); input_set_abs_params(input, ABS_PRESSURE, 0, 256, 0, 0); + + return 0; } static void elo_process_data(struct input_dev *input, const u8 *data, int size) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index f769208276ae..ac1feea51be3 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -251,6 +251,9 @@ #define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500 #define USB_DEVICE_ID_CODEMERCS_IOW_LAST 0x15ff +#define USB_VENDOR_ID_CORSAIR 0x1b1c +#define USB_DEVICE_ID_CORSAIR_K90 0x1b02 + #define USB_VENDOR_ID_CREATIVELABS 0x041e #define USB_DEVICE_ID_PRODIKEYS_PCMIDI 0x2801 @@ -288,6 +291,7 @@ #define USB_DEVICE_ID_DMI_ENC 0x5fab #define USB_VENDOR_ID_DRAGONRISE 0x0079 +#define USB_DEVICE_ID_DRAGONRISE_WIIU 0x1800 #define USB_VENDOR_ID_DWAV 0x0eef #define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER 0x0001 @@ -510,6 +514,7 @@ #define USB_VENDOR_ID_ITE 0x048d #define USB_DEVICE_ID_ITE_LENOVO_YOGA 0x8386 +#define USB_DEVICE_ID_ITE_LENOVO_YOGA2 0x8350 #define USB_VENDOR_ID_JABRA 0x0b0e #define USB_DEVICE_ID_JABRA_SPEAK_410 0x0412 @@ -646,6 +651,7 @@ #define USB_VENDOR_ID_MADCATZ 0x0738 #define USB_DEVICE_ID_MADCATZ_BEATPAD 0x4540 +#define USB_DEVICE_ID_MADCATZ_RAT5 0x1705 #define USB_DEVICE_ID_MADCATZ_RAT9 0x1709 #define USB_VENDOR_ID_MCC 0x09db @@ -679,6 +685,7 @@ #define USB_DEVICE_ID_MS_TOUCH_COVER_2 0x07a7 #define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9 #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3 0x07dc +#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 0x07e2 #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP 0x07dd #define USB_DEVICE_ID_MS_TYPE_COVER_3 0x07de #define USB_DEVICE_ID_MS_POWER_COVER 0x07da diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 53aeaf6252c7..2ba6bf69b7d0 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1510,8 +1510,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) * UGCI) cram a lot of unrelated inputs into the * same interface. */ hidinput->report = report; - if (drv->input_configured) - drv->input_configured(hid, hidinput); + if (drv->input_configured && + drv->input_configured(hid, hidinput)) + goto out_cleanup; if (input_register_device(hidinput->input)) goto out_cleanup; hidinput = NULL; @@ -1532,8 +1533,9 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) } if (hidinput) { - if (drv->input_configured) - drv->input_configured(hid, hidinput); + if (drv->input_configured && + drv->input_configured(hid, hidinput)) + goto out_cleanup; if (input_register_device(hidinput->input)) goto out_cleanup; } diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c index e4bc6cb6d7fa..8979f1fd5208 100644 --- a/drivers/hid/hid-lenovo.c +++ b/drivers/hid/hid-lenovo.c @@ -848,7 +848,7 @@ static void lenovo_remove(struct hid_device *hdev) hid_hw_stop(hdev); } -static void lenovo_input_configured(struct hid_device *hdev, +static int lenovo_input_configured(struct hid_device *hdev, struct hid_input *hi) { switch (hdev->product) { @@ -863,6 +863,8 @@ static void lenovo_input_configured(struct hid_device *hdev, } break; } + + return 0; } diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index 5332fb7d072a..c20ac76c0a8c 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -620,6 +620,7 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi, usage->code == ABS_Y || usage->code == ABS_Z || usage->code == ABS_RZ)) { switch (hdev->product) { + case USB_DEVICE_ID_LOGITECH_G29_WHEEL: case USB_DEVICE_ID_LOGITECH_WHEEL: case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL: case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: @@ -658,10 +659,18 @@ static int lg_event(struct hid_device *hdev, struct hid_field *field, static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) { + struct usb_interface *iface = to_usb_interface(hdev->dev.parent); + __u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber; unsigned int connect_mask = HID_CONNECT_DEFAULT; struct lg_drv_data *drv_data; int ret; + /* Only work with the 1st interface (G29 presents multiple) */ + if (iface_num != 0) { + dbg_hid("%s: ignoring ifnum %d\n", __func__, iface_num); + return -ENODEV; + } + drv_data = kzalloc(sizeof(struct lg_drv_data), GFP_KERNEL); if (!drv_data) { hid_err(hdev, "Insufficient memory, cannot allocate driver data\n"); diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index 02cec83caac3..fbddcb37ae98 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -45,7 +45,8 @@ #define LG4FF_MODE_G25_IDX 3 #define LG4FF_MODE_DFGT_IDX 4 #define LG4FF_MODE_G27_IDX 5 -#define LG4FF_MODE_MAX_IDX 6 +#define LG4FF_MODE_G29_IDX 6 +#define LG4FF_MODE_MAX_IDX 7 #define LG4FF_MODE_NATIVE BIT(LG4FF_MODE_NATIVE_IDX) #define LG4FF_MODE_DFEX BIT(LG4FF_MODE_DFEX_IDX) @@ -53,6 +54,7 @@ #define LG4FF_MODE_G25 BIT(LG4FF_MODE_G25_IDX) #define LG4FF_MODE_DFGT BIT(LG4FF_MODE_DFGT_IDX) #define LG4FF_MODE_G27 BIT(LG4FF_MODE_G27_IDX) +#define LG4FF_MODE_G29 BIT(LG4FF_MODE_G29_IDX) #define LG4FF_DFEX_TAG "DF-EX" #define LG4FF_DFEX_NAME "Driving Force / Formula EX" @@ -62,6 +64,8 @@ #define LG4FF_G25_NAME "G25 Racing Wheel" #define LG4FF_G27_TAG "G27" #define LG4FF_G27_NAME "G27 Racing Wheel" +#define LG4FF_G29_TAG "G29" +#define LG4FF_G29_NAME "G29 Racing Wheel" #define LG4FF_DFGT_TAG "DFGT" #define LG4FF_DFGT_NAME "Driving Force GT" @@ -114,16 +118,12 @@ struct lg4ff_compat_mode_switch { }; struct lg4ff_wheel_ident_info { + const u32 modes; const u16 mask; const u16 result; const u16 real_product_id; }; -struct lg4ff_wheel_ident_checklist { - const u32 count; - const struct lg4ff_wheel_ident_info *models[]; -}; - struct lg4ff_multimode_wheel { const u16 product_id; const u32 alternate_modes; @@ -144,6 +144,7 @@ static const struct lg4ff_wheel lg4ff_devices[] = { {USB_DEVICE_ID_LOGITECH_G25_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25}, {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25}, {USB_DEVICE_ID_LOGITECH_G27_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25}, + {USB_DEVICE_ID_LOGITECH_G29_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25}, {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2, lg4ff_wheel_effects, 40, 270, NULL}, {USB_DEVICE_ID_LOGITECH_WII_WHEEL, lg4ff_wheel_effects, 40, 270, NULL} }; @@ -161,6 +162,9 @@ static const struct lg4ff_multimode_wheel lg4ff_multimode_wheels[] = { {USB_DEVICE_ID_LOGITECH_G27_WHEEL, LG4FF_MODE_NATIVE | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX, LG4FF_G27_TAG, LG4FF_G27_NAME}, + {USB_DEVICE_ID_LOGITECH_G29_WHEEL, + LG4FF_MODE_NATIVE | LG4FF_MODE_G29 | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX, + LG4FF_G29_TAG, LG4FF_G29_NAME}, }; static const struct lg4ff_alternate_mode lg4ff_alternate_modes[] = { @@ -169,41 +173,61 @@ static const struct lg4ff_alternate_mode lg4ff_alternate_modes[] = { [LG4FF_MODE_DFP_IDX] = {USB_DEVICE_ID_LOGITECH_DFP_WHEEL, LG4FF_DFP_TAG, LG4FF_DFP_NAME}, [LG4FF_MODE_G25_IDX] = {USB_DEVICE_ID_LOGITECH_G25_WHEEL, LG4FF_G25_TAG, LG4FF_G25_NAME}, [LG4FF_MODE_DFGT_IDX] = {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, LG4FF_DFGT_TAG, LG4FF_DFGT_NAME}, - [LG4FF_MODE_G27_IDX] = {USB_DEVICE_ID_LOGITECH_G27_WHEEL, LG4FF_G27_TAG, LG4FF_G27_NAME} + [LG4FF_MODE_G27_IDX] = {USB_DEVICE_ID_LOGITECH_G27_WHEEL, LG4FF_G27_TAG, LG4FF_G27_NAME}, + [LG4FF_MODE_G29_IDX] = {USB_DEVICE_ID_LOGITECH_G29_WHEEL, LG4FF_G29_TAG, LG4FF_G29_NAME}, }; /* Multimode wheel identificators */ static const struct lg4ff_wheel_ident_info lg4ff_dfp_ident_info = { + LG4FF_MODE_DFP | LG4FF_MODE_DFEX, 0xf000, 0x1000, USB_DEVICE_ID_LOGITECH_DFP_WHEEL }; static const struct lg4ff_wheel_ident_info lg4ff_g25_ident_info = { + LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX, 0xff00, 0x1200, USB_DEVICE_ID_LOGITECH_G25_WHEEL }; static const struct lg4ff_wheel_ident_info lg4ff_g27_ident_info = { + LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX, 0xfff0, 0x1230, USB_DEVICE_ID_LOGITECH_G27_WHEEL }; static const struct lg4ff_wheel_ident_info lg4ff_dfgt_ident_info = { + LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX, 0xff00, 0x1300, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL }; +static const struct lg4ff_wheel_ident_info lg4ff_g29_ident_info = { + LG4FF_MODE_G29 | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX, + 0xfff8, + 0x1350, + USB_DEVICE_ID_LOGITECH_G29_WHEEL +}; + +static const struct lg4ff_wheel_ident_info lg4ff_g29_ident_info2 = { + LG4FF_MODE_G29 | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX, + 0xff00, + 0x8900, + USB_DEVICE_ID_LOGITECH_G29_WHEEL +}; + /* Multimode wheel identification checklists */ -static const struct lg4ff_wheel_ident_checklist lg4ff_main_checklist = { - 4, - {&lg4ff_dfgt_ident_info, - &lg4ff_g27_ident_info, - &lg4ff_g25_ident_info, - &lg4ff_dfp_ident_info} +static const struct lg4ff_wheel_ident_info *lg4ff_main_checklist[] = { + &lg4ff_g29_ident_info, + &lg4ff_g29_ident_info2, + &lg4ff_dfgt_ident_info, + &lg4ff_g27_ident_info, + &lg4ff_g25_ident_info, + &lg4ff_dfp_ident_info }; /* Compatibility mode switching commands */ @@ -238,6 +262,12 @@ static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_g27 = { 0xf8, 0x09, 0x04, 0x01, 0x00, 0x00, 0x00} /* Switch mode to G27 with detach */ }; +static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_g29 = { + 2, + {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */ + 0xf8, 0x09, 0x05, 0x01, 0x01, 0x00, 0x00} /* Switch mode to G29 with detach */ +}; + /* EXT_CMD1 - Understood by DFP, G25, G27 and DFGT */ static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext01_dfp = { 1, @@ -651,6 +681,23 @@ static const struct lg4ff_compat_mode_switch *lg4ff_get_mode_switch_command(cons return NULL; } break; + case USB_DEVICE_ID_LOGITECH_G29_WHEEL: + switch (target_product_id) { + case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: + return &lg4ff_mode_switch_ext09_dfp; + case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL: + return &lg4ff_mode_switch_ext09_dfgt; + case USB_DEVICE_ID_LOGITECH_G25_WHEEL: + return &lg4ff_mode_switch_ext09_g25; + case USB_DEVICE_ID_LOGITECH_G27_WHEEL: + return &lg4ff_mode_switch_ext09_g27; + case USB_DEVICE_ID_LOGITECH_G29_WHEEL: + return &lg4ff_mode_switch_ext09_g29; + /* G29 can only be switched to DF-EX, DFP, DFGT, G25, G27 or its native mode */ + default: + return NULL; + } + break; case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL: switch (target_product_id) { case USB_DEVICE_ID_LOGITECH_WHEEL: @@ -1037,41 +1084,28 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde static u16 lg4ff_identify_multimode_wheel(struct hid_device *hid, const u16 reported_product_id, const u16 bcdDevice) { - const struct lg4ff_wheel_ident_checklist *checklist; - int i, from_idx, to_idx; + u32 current_mode; + int i; - switch (reported_product_id) { - case USB_DEVICE_ID_LOGITECH_WHEEL: - case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: - checklist = &lg4ff_main_checklist; - from_idx = 0; - to_idx = checklist->count - 1; - break; - case USB_DEVICE_ID_LOGITECH_G25_WHEEL: - checklist = &lg4ff_main_checklist; - from_idx = 0; - to_idx = checklist->count - 2; /* End identity check at G25 */ - break; - case USB_DEVICE_ID_LOGITECH_G27_WHEEL: - checklist = &lg4ff_main_checklist; - from_idx = 1; /* Start identity check at G27 */ - to_idx = checklist->count - 3; /* End identity check at G27 */ - break; - case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL: - checklist = &lg4ff_main_checklist; - from_idx = 0; - to_idx = checklist->count - 4; /* End identity check at DFGT */ - break; - default: - return 0; + /* identify current mode from USB PID */ + for (i = 1; i < ARRAY_SIZE(lg4ff_alternate_modes); i++) { + dbg_hid("Testing whether PID is %X\n", lg4ff_alternate_modes[i].product_id); + if (reported_product_id == lg4ff_alternate_modes[i].product_id) + break; } - for (i = from_idx; i <= to_idx; i++) { - const u16 mask = checklist->models[i]->mask; - const u16 result = checklist->models[i]->result; - const u16 real_product_id = checklist->models[i]->real_product_id; + if (i == ARRAY_SIZE(lg4ff_alternate_modes)) + return 0; + + current_mode = BIT(i); + + for (i = 0; i < ARRAY_SIZE(lg4ff_main_checklist); i++) { + const u16 mask = lg4ff_main_checklist[i]->mask; + const u16 result = lg4ff_main_checklist[i]->result; + const u16 real_product_id = lg4ff_main_checklist[i]->real_product_id; - if ((bcdDevice & mask) == result) { + if ((current_mode & lg4ff_main_checklist[i]->modes) && \ + (bcdDevice & mask) == result) { dbg_hid("Found wheel with real PID %X whose reported PID is %X\n", real_product_id, reported_product_id); return real_product_id; } @@ -1246,12 +1280,13 @@ int lg4ff_init(struct hid_device *hid) entry->wdata.set_range(hid, entry->wdata.range); #ifdef CONFIG_LEDS_CLASS - /* register led subsystem - G27 only */ + /* register led subsystem - G27/G29 only */ entry->wdata.led_state = 0; for (j = 0; j < 5; j++) entry->wdata.led[j] = NULL; - if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_G27_WHEEL) { + if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_G27_WHEEL || + lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_G29_WHEEL) { struct led_classdev *led; size_t name_sz; char *name; diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 484196459305..5fd97860aec4 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -33,6 +33,11 @@ module_param(disable_raw_mode, bool, 0644); MODULE_PARM_DESC(disable_raw_mode, "Disable Raw mode reporting for touchpads and keep firmware gestures."); +static bool disable_tap_to_click; +module_param(disable_tap_to_click, bool, 0644); +MODULE_PARM_DESC(disable_tap_to_click, + "Disable Tap-To-Click mode reporting for touchpads (only on the K400 currently)."); + #define REPORT_ID_HIDPP_SHORT 0x10 #define REPORT_ID_HIDPP_LONG 0x11 @@ -41,10 +46,15 @@ MODULE_PARM_DESC(disable_raw_mode, #define HIDPP_QUIRK_CLASS_WTP BIT(0) #define HIDPP_QUIRK_CLASS_M560 BIT(1) +#define HIDPP_QUIRK_CLASS_K400 BIT(2) /* bits 2..20 are reserved for classes */ -#define HIDPP_QUIRK_DELAYED_INIT BIT(21) +#define HIDPP_QUIRK_CONNECT_EVENTS BIT(21) #define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS BIT(22) +#define HIDPP_QUIRK_NO_HIDINPUT BIT(23) + +#define HIDPP_QUIRK_DELAYED_INIT (HIDPP_QUIRK_NO_HIDINPUT | \ + HIDPP_QUIRK_CONNECT_EVENTS) /* * There are two hidpp protocols in use, the first version hidpp10 is known @@ -553,6 +563,52 @@ static char *hidpp_get_device_name(struct hidpp_device *hidpp) } /* -------------------------------------------------------------------------- */ +/* 0x6010: Touchpad FW items */ +/* -------------------------------------------------------------------------- */ + +#define HIDPP_PAGE_TOUCHPAD_FW_ITEMS 0x6010 + +#define CMD_TOUCHPAD_FW_ITEMS_SET 0x10 + +struct hidpp_touchpad_fw_items { + uint8_t presence; + uint8_t desired_state; + uint8_t state; + uint8_t persistent; +}; + +/** + * send a set state command to the device by reading the current items->state + * field. items is then filled with the current state. + */ +static int hidpp_touchpad_fw_items_set(struct hidpp_device *hidpp, + u8 feature_index, + struct hidpp_touchpad_fw_items *items) +{ + struct hidpp_report response; + int ret; + u8 *params = (u8 *)response.fap.params; + + ret = hidpp_send_fap_command_sync(hidpp, feature_index, + CMD_TOUCHPAD_FW_ITEMS_SET, &items->state, 1, &response); + + if (ret > 0) { + hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n", + __func__, ret); + return -EPROTO; + } + if (ret) + return ret; + + items->presence = params[0]; + items->desired_state = params[1]; + items->state = params[2]; + items->persistent = params[3]; + + return 0; +} + +/* -------------------------------------------------------------------------- */ /* 0x6100: TouchPadRawXY */ /* -------------------------------------------------------------------------- */ @@ -1132,6 +1188,75 @@ static int m560_input_mapping(struct hid_device *hdev, struct hid_input *hi, return -1; } +/* ------------------------------------------------------------------------- */ +/* Logitech K400 devices */ +/* ------------------------------------------------------------------------- */ + +/* + * The Logitech K400 keyboard has an embedded touchpad which is seen + * as a mouse from the OS point of view. There is a hardware shortcut to disable + * tap-to-click but the setting is not remembered accross reset, annoying some + * users. + * + * We can toggle this feature from the host by using the feature 0x6010: + * Touchpad FW items + */ + +struct k400_private_data { + u8 feature_index; +}; + +static int k400_disable_tap_to_click(struct hidpp_device *hidpp) +{ + struct k400_private_data *k400 = hidpp->private_data; + struct hidpp_touchpad_fw_items items = {}; + int ret; + u8 feature_type; + + if (!k400->feature_index) { + ret = hidpp_root_get_feature(hidpp, + HIDPP_PAGE_TOUCHPAD_FW_ITEMS, + &k400->feature_index, &feature_type); + if (ret) + /* means that the device is not powered up */ + return ret; + } + + ret = hidpp_touchpad_fw_items_set(hidpp, k400->feature_index, &items); + if (ret) + return ret; + + return 0; +} + +static int k400_allocate(struct hid_device *hdev) +{ + struct hidpp_device *hidpp = hid_get_drvdata(hdev); + struct k400_private_data *k400; + + k400 = devm_kzalloc(&hdev->dev, sizeof(struct k400_private_data), + GFP_KERNEL); + if (!k400) + return -ENOMEM; + + hidpp->private_data = k400; + + return 0; +}; + +static int k400_connect(struct hid_device *hdev, bool connected) +{ + struct hidpp_device *hidpp = hid_get_drvdata(hdev); + + if (!connected) + return 0; + + if (!disable_tap_to_click) + return 0; + + return k400_disable_tap_to_click(hidpp); +} + /* -------------------------------------------------------------------------- */ /* Generic HID++ devices */ /* -------------------------------------------------------------------------- */ @@ -1160,13 +1285,15 @@ static void hidpp_populate_input(struct hidpp_device *hidpp, m560_populate_input(hidpp, input, origin_is_hid_core); } -static void hidpp_input_configured(struct hid_device *hdev, +static int hidpp_input_configured(struct hid_device *hdev, struct hid_input *hidinput) { struct hidpp_device *hidpp = hid_get_drvdata(hdev); struct input_dev *input = hidinput->input; hidpp_populate_input(hidpp, input, true); + + return 0; } static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data, @@ -1203,7 +1330,7 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data, if (unlikely(hidpp_report_is_connect_event(report))) { atomic_set(&hidpp->connected, !(report->rap.params[0] & (1 << 6))); - if ((hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT) && + if ((hidpp->quirks & HIDPP_QUIRK_CONNECT_EVENTS) && (schedule_work(&hidpp->work) == 0)) dbg_hid("%s: connect event already queued\n", __func__); return 1; @@ -1328,23 +1455,30 @@ static void hidpp_connect_event(struct hidpp_device *hidpp) ret = m560_send_config_command(hdev, connected); if (ret) return; + } else if (hidpp->quirks & HIDPP_QUIRK_CLASS_K400) { + ret = k400_connect(hdev, connected); + if (ret) + return; } if (!connected || hidpp->delayed_input) return; + /* the device is already connected, we can ask for its name and + * protocol */ if (!hidpp->protocol_major) { ret = !hidpp_is_connected(hidpp); if (ret) { hid_err(hdev, "Can not get the protocol version.\n"); return; } + hid_info(hdev, "HID++ %u.%u device connected.\n", + hidpp->protocol_major, hidpp->protocol_minor); } - /* the device is already connected, we can ask for its name and - * protocol */ - hid_info(hdev, "HID++ %u.%u device connected.\n", - hidpp->protocol_major, hidpp->protocol_minor); + if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT)) + /* if HID created the input nodes for us, we can stop now */ + return; if (!hidpp->name || hidpp->name == hdev->name) { name = hidpp_get_device_name(hidpp); @@ -1397,7 +1531,8 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) if (disable_raw_mode) { hidpp->quirks &= ~HIDPP_QUIRK_CLASS_WTP; - hidpp->quirks &= ~HIDPP_QUIRK_DELAYED_INIT; + hidpp->quirks &= ~HIDPP_QUIRK_CONNECT_EVENTS; + hidpp->quirks &= ~HIDPP_QUIRK_NO_HIDINPUT; } if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) { @@ -1408,6 +1543,10 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) ret = m560_allocate(hdev); if (ret) goto allocate_fail; + } else if (hidpp->quirks & HIDPP_QUIRK_CLASS_K400) { + ret = k400_allocate(hdev); + if (ret) + goto allocate_fail; } INIT_WORK(&hidpp->work, delayed_work_cb); @@ -1448,7 +1587,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) /* Block incoming packets */ hid_device_io_stop(hdev); - if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT) + if (hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT) connect_mask &= ~HID_CONNECT_HIDINPUT; ret = hid_hw_start(hdev, connect_mask); @@ -1457,7 +1596,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) goto hid_hw_start_fail; } - if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT) { + if (hidpp->quirks & HIDPP_QUIRK_CONNECT_EVENTS) { /* Allow incoming packets */ hid_device_io_start(hdev); @@ -1502,6 +1641,10 @@ static const struct hid_device_id hidpp_devices[] = { HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, USB_VENDOR_ID_LOGITECH, 0x402d), .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 }, + { /* Keyboard logitech K400 */ + HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, + USB_VENDOR_ID_LOGITECH, 0x4024), + .driver_data = HIDPP_QUIRK_CONNECT_EVENTS | HIDPP_QUIRK_CLASS_K400 }, { HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, USB_VENDOR_ID_LOGITECH, HID_ANY_ID)}, diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 29a74c1efcb8..d6fa496d0ca2 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -471,18 +471,22 @@ static int magicmouse_input_mapping(struct hid_device *hdev, return 0; } -static void magicmouse_input_configured(struct hid_device *hdev, +static int magicmouse_input_configured(struct hid_device *hdev, struct hid_input *hi) { struct magicmouse_sc *msc = hid_get_drvdata(hdev); + int ret; - int ret = magicmouse_setup_input(msc->input, hdev); + ret = magicmouse_setup_input(msc->input, hdev); if (ret) { hid_err(hdev, "magicmouse setup input failed (%d)\n", ret); /* clean msc->input to notify probe() of the failure */ msc->input = NULL; + return ret; } + + return 0; } diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index 9aa3515090a7..77a2cf3e4afe 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -278,6 +278,8 @@ static const struct hid_device_id ms_devices[] = { .driver_data = MS_DUPLICATE_USAGES }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3), .driver_data = MS_HIDINPUT }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2), + .driver_data = MS_HIDINPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP), .driver_data = MS_HIDINPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3), diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 426b2f1a3450..3d664d01305e 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -309,6 +309,41 @@ static struct attribute_group mt_attribute_group = { .attrs = sysfs_attrs }; +static void mt_get_feature(struct hid_device *hdev, struct hid_report *report) +{ + struct mt_device *td = hid_get_drvdata(hdev); + int ret, size = hid_report_len(report); + u8 *buf; + + /* + * Only fetch the feature report if initial reports are not already + * been retrieved. Currently this is only done for Windows 8 touch + * devices. + */ + if (!(hdev->quirks & HID_QUIRK_NO_INIT_REPORTS)) + return; + if (td->mtclass.name != MT_CLS_WIN_8) + return; + + buf = hid_alloc_report_buf(report, GFP_KERNEL); + if (!buf) + return; + + ret = hid_hw_raw_request(hdev, report->id, buf, size, + HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + if (ret < 0) { + dev_warn(&hdev->dev, "failed to fetch feature %d\n", + report->id); + } else { + ret = hid_report_raw_event(hdev, HID_FEATURE_REPORT, buf, + size, 0); + if (ret) + dev_warn(&hdev->dev, "failed to report feature\n"); + } + + kfree(buf); +} + static void mt_feature_mapping(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage) { @@ -327,6 +362,8 @@ static void mt_feature_mapping(struct hid_device *hdev, break; case HID_DG_CONTACTMAX: + mt_get_feature(hdev, field->report); + td->maxcontact_report_id = field->report->id; td->maxcontacts = field->value[0]; if (!td->maxcontacts && @@ -343,6 +380,7 @@ static void mt_feature_mapping(struct hid_device *hdev, break; } + mt_get_feature(hdev, field->report); if (field->value[usage->usage_index] == MT_BUTTONTYPE_CLICKPAD) td->is_buttonpad = true; @@ -725,12 +763,13 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report) mt_sync_frame(td, report->field[0]->hidinput->input); } -static void mt_touch_input_configured(struct hid_device *hdev, +static int mt_touch_input_configured(struct hid_device *hdev, struct hid_input *hi) { struct mt_device *td = hid_get_drvdata(hdev); struct mt_class *cls = &td->mtclass; struct input_dev *input = hi->input; + int ret; if (!td->maxcontacts) td->maxcontacts = MT_DEFAULT_MAXCONTACT; @@ -752,9 +791,12 @@ static void mt_touch_input_configured(struct hid_device *hdev, if (td->is_buttonpad) __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); - input_mt_init_slots(input, td->maxcontacts, td->mt_flags); + ret = input_mt_init_slots(input, td->maxcontacts, td->mt_flags); + if (ret) + return ret; td->mt_flags = 0; + return 0; } static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, @@ -930,15 +972,19 @@ static void mt_post_parse(struct mt_device *td) cls->quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE; } -static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi) +static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi) { struct mt_device *td = hid_get_drvdata(hdev); char *name; const char *suffix = NULL; struct hid_field *field = hi->report->field[0]; + int ret; - if (hi->report->id == td->mt_report_id) - mt_touch_input_configured(hdev, hi); + if (hi->report->id == td->mt_report_id) { + ret = mt_touch_input_configured(hdev, hi); + if (ret) + return ret; + } /* * some egalax touchscreens have "application == HID_DG_TOUCHSCREEN" @@ -968,6 +1014,9 @@ static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi) case HID_DG_TOUCHSCREEN: /* we do not set suffix = "Touchscreen" */ break; + case HID_DG_TOUCHPAD: + suffix = "Touchpad"; + break; case HID_GD_SYSTEM_CONTROL: suffix = "System Control"; break; @@ -989,6 +1038,8 @@ static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi) hi->input->name = name; } } + + return 0; } static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) @@ -1026,8 +1077,13 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) * reports. Fortunately, the Win8 spec says that all touches * should be sent during each report, making the initialization * of input reports unnecessary. + * + * In addition some touchpads do not behave well if we read + * all feature reports from them. Instead we prevent + * initial report fetching and then selectively fetch each + * report we are interested in. */ - hdev->quirks |= HID_QUIRK_NO_INIT_INPUT_REPORTS; + hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS; td = devm_kzalloc(&hdev->dev, sizeof(struct mt_device), GFP_KERNEL); if (!td) { diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index 600f2075512f..756d1ef9bd99 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -859,14 +859,14 @@ not_claimed_input: return 1; } -static void ntrig_input_configured(struct hid_device *hid, +static int ntrig_input_configured(struct hid_device *hid, struct hid_input *hidinput) { struct input_dev *input = hidinput->input; if (hidinput->report->maxfield < 1) - return; + return 0; switch (hidinput->report->field[0]->application) { case HID_DG_PEN: @@ -890,6 +890,8 @@ static void ntrig_input_configured(struct hid_device *hid, "N-Trig MultiTouch"; break; } + + return 0; } static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id) diff --git a/drivers/hid/hid-prodikeys.c b/drivers/hid/hid-prodikeys.c index e3e98ccf137b..3a207c0ac0e3 100644 --- a/drivers/hid/hid-prodikeys.c +++ b/drivers/hid/hid-prodikeys.c @@ -427,7 +427,7 @@ static int pcmidi_handle_report4(struct pcmidi_snd *pm, u8 *data) pm->midi_octave = 2; dbg_hid("pcmidi mode: %d octave: %d\n", pm->midi_mode, pm->midi_octave); - continue; + continue; } else key = KEY_MESSENGER; break; @@ -695,7 +695,7 @@ static int pcmidi_snd_initialise(struct pcmidi_snd *pm) if (err < 0) { pk_error("failed to register pc-midi sound card: error %d\n", err); - goto fail_register; + goto fail_register; } dbg_hid("pcmidi_snd_initialise finished ok\n"); diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index 2c148129beb2..67cd059a8f46 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -1173,7 +1173,7 @@ static int rmi_populate(struct hid_device *hdev) return 0; } -static void rmi_input_configured(struct hid_device *hdev, struct hid_input *hi) +static int rmi_input_configured(struct hid_device *hdev, struct hid_input *hi) { struct rmi_data *data = hid_get_drvdata(hdev); struct input_dev *input = hi->input; @@ -1185,10 +1185,10 @@ static void rmi_input_configured(struct hid_device *hdev, struct hid_input *hi) hid_dbg(hdev, "Opening low level driver\n"); ret = hid_hw_open(hdev); if (ret) - return; + return ret; if (!(data->device_flags & RMI_DEVICE)) - return; + return 0; /* Allow incoming hid reports */ hid_device_io_start(hdev); @@ -1228,7 +1228,9 @@ static void rmi_input_configured(struct hid_device *hdev, struct hid_input *hi) input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 0x0f, 0, 0); input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 0x0f, 0, 0); - input_mt_init_slots(input, data->max_fingers, INPUT_MT_POINTER); + ret = input_mt_init_slots(input, data->max_fingers, INPUT_MT_POINTER); + if (ret < 0) + goto exit; if (data->button_count) { __set_bit(EV_KEY, input->evbit); @@ -1244,6 +1246,7 @@ static void rmi_input_configured(struct hid_device *hdev, struct hid_input *hi) exit: hid_device_io_stop(hdev); hid_hw_close(hdev); + return ret; } static int rmi_input_mapping(struct hid_device *hdev, diff --git a/drivers/hid/hid-saitek.c b/drivers/hid/hid-saitek.c index a014f21275d8..2f84b26f1167 100644 --- a/drivers/hid/hid-saitek.c +++ b/drivers/hid/hid-saitek.c @@ -177,6 +177,8 @@ static int saitek_event(struct hid_device *hdev, struct hid_field *field, static const struct hid_device_id saitek_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000), .driver_data = SAITEK_FIX_PS1000 }, + { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT5), + .driver_data = SAITEK_RELEASE_MODE_RAT7 }, { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7_OLD), .driver_data = SAITEK_RELEASE_MODE_RAT7 }, { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7), diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index a76eb2a0a987..92870cdb52d9 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -593,6 +593,20 @@ static __u8 *sensor_hub_report_fixup(struct hid_device *hdev, __u8 *rdesc, } } + /* Checks if the report descriptor of Thinkpad Helix 2 has a logical + * minimum for magnetic flux axis greater than the maximum */ + if (hdev->product == USB_DEVICE_ID_TEXAS_INSTRUMENTS_LENOVO_YOGA && + *rsize == 2558 && rdesc[913] == 0x17 && rdesc[914] == 0x40 && + rdesc[915] == 0x81 && rdesc[916] == 0x08 && + rdesc[917] == 0x00 && rdesc[918] == 0x27 && + rdesc[921] == 0x07 && rdesc[922] == 0x00) { + /* Sets negative logical minimum for mag x, y and z */ + rdesc[914] = rdesc[935] = rdesc[956] = 0xc0; + rdesc[915] = rdesc[936] = rdesc[957] = 0x7e; + rdesc[916] = rdesc[937] = rdesc[958] = 0xf7; + rdesc[917] = rdesc[938] = rdesc[959] = 0xff; + } + return rdesc; } @@ -646,8 +660,8 @@ static int sensor_hub_probe(struct hid_device *hdev, GFP_KERNEL); if (sd->hid_sensor_hub_client_devs == NULL) { hid_err(hdev, "Failed to allocate memory for mfd cells\n"); - ret = -ENOMEM; - goto err_stop_hw; + ret = -ENOMEM; + goto err_stop_hw; } for (i = 0; i < hdev->maxcollection; ++i) { @@ -684,8 +698,8 @@ static int sensor_hub_probe(struct hid_device *hdev, collection->usage); if (name == NULL) { hid_err(hdev, "Failed MFD device name\n"); - ret = -ENOMEM; - goto err_stop_hw; + ret = -ENOMEM; + goto err_stop_hw; } sd->hid_sensor_hub_client_devs[ sd->hid_sensor_client_cnt].name = name; @@ -777,6 +791,9 @@ static const struct hid_device_id sensor_hub_devices[] = { { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_ITE, USB_DEVICE_ID_ITE_LENOVO_YOGA), .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, + { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_ITE, + USB_DEVICE_ID_ITE_LENOVO_YOGA2), + .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, HID_ANY_ID, HID_ANY_ID) }, { } diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 661f94f8ab8b..774cd2210566 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -1360,20 +1360,27 @@ static int sony_register_touchpad(struct hid_input *hi, int touch_count, return 0; } -static void sony_input_configured(struct hid_device *hdev, +static int sony_input_configured(struct hid_device *hdev, struct hid_input *hidinput) { struct sony_sc *sc = hid_get_drvdata(hdev); + int ret; /* * The Dualshock 4 touchpad supports 2 touches and has a * resolution of 1920x942 (44.86 dots/mm). */ if (sc->quirks & DUALSHOCK4_CONTROLLER) { - if (sony_register_touchpad(hidinput, 2, 1920, 942) != 0) + ret = sony_register_touchpad(hidinput, 2, 1920, 942); + if (ret) { hid_err(sc->hdev, - "Unable to initialize multi-touch slots\n"); + "Unable to initialize multi-touch slots: %d\n", + ret); + return ret; + } } + + return 0; } /* diff --git a/drivers/hid/hid-uclogic.c b/drivers/hid/hid-uclogic.c index b905d501e752..85ac43517e3f 100644 --- a/drivers/hid/hid-uclogic.c +++ b/drivers/hid/hid-uclogic.c @@ -731,7 +731,7 @@ static int uclogic_input_mapping(struct hid_device *hdev, struct hid_input *hi, return 0; } -static void uclogic_input_configured(struct hid_device *hdev, +static int uclogic_input_configured(struct hid_device *hdev, struct hid_input *hi) { char *name; @@ -741,7 +741,7 @@ static void uclogic_input_configured(struct hid_device *hdev, /* no report associated (HID_QUIRK_MULTI_INPUT not set) */ if (!hi->report) - return; + return 0; field = hi->report->field[0]; @@ -774,6 +774,8 @@ static void uclogic_input_configured(struct hid_device *hdev, hi->input->name = name; } } + + return 0; } /** diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 2871f3c81a4c..10bd8e6e4c9c 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -1028,6 +1028,7 @@ static int i2c_hid_probe(struct i2c_client *client, snprintf(hid->name, sizeof(hid->name), "%s %04hX:%04hX", client->name, hid->vendor, hid->product); + strlcpy(hid->phys, dev_name(&client->dev), sizeof(hid->phys)); ret = hid_add_device(hid); if (ret) { diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index 1dff8f0015ba..94bb137abe32 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -71,6 +71,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET }, { USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_WIIU, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_009B, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ELAN_TOUCHSCREEN_0103, HID_QUIRK_ALWAYS_POLL }, @@ -91,6 +92,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_2, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TOUCH_COVER_2, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER, HID_QUIRK_NO_INIT_REPORTS }, diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 9db6a8b1150c..8b29949507d1 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1652,6 +1652,7 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev, wacom_map_usage(input, usage, field, EV_KEY, BTN_TOUCH, 0); break; case HID_DG_CONTACTCOUNT: + wacom_wac->hid_data.cc_report = field->report->id; wacom_wac->hid_data.cc_index = field->index; wacom_wac->hid_data.cc_value_index = usage->usage_index; break; @@ -1739,7 +1740,32 @@ static void wacom_wac_finger_pre_report(struct hid_device *hdev, struct wacom_wac *wacom_wac = &wacom->wacom_wac; struct hid_data* hid_data = &wacom_wac->hid_data; - if (hid_data->cc_index >= 0) { + if (hid_data->cc_report != 0 && + hid_data->cc_report != report->id) { + int i; + + hid_data->cc_report = report->id; + hid_data->cc_index = -1; + hid_data->cc_value_index = -1; + + for (i = 0; i < report->maxfield; i++) { + struct hid_field *field = report->field[i]; + int j; + + for (j = 0; j < field->maxusage; j++) { + if (field->usage[j].hid == HID_DG_CONTACTCOUNT) { + hid_data->cc_index = i; + hid_data->cc_value_index = j; + + /* break */ + i = report->maxfield; + j = field->maxusage; + } + } + } + } + if (hid_data->cc_report != 0 && + hid_data->cc_index >= 0) { struct hid_field *field = report->field[hid_data->cc_index]; int value = field->value[hid_data->cc_value_index]; if (value) diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index d9e9515fd149..877c24a5df94 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -203,6 +203,7 @@ struct hid_data { int width; int height; int id; + int cc_report; int cc_index; int cc_value_index; int num_expected; |