diff options
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/Kconfig | 15 | ||||
-rw-r--r-- | drivers/hid/Makefile | 1 | ||||
-rw-r--r-- | drivers/hid/hid-bigbenff.c | 414 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 2 | ||||
-rw-r--r-- | drivers/hid/hid-cougar.c | 66 | ||||
-rw-r--r-- | drivers/hid/hid-elan.c | 2 | ||||
-rw-r--r-- | drivers/hid/hid-google-hammer.c | 413 | ||||
-rw-r--r-- | drivers/hid/hid-ids.h | 6 | ||||
-rw-r--r-- | drivers/hid/hid-input.c | 19 | ||||
-rw-r--r-- | drivers/hid/hid-magicmouse.c | 142 | ||||
-rw-r--r-- | drivers/hid/hid-quirks.c | 1 | ||||
-rw-r--r-- | drivers/hid/i2c-hid/i2c-hid-core.c | 27 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ipc/hw-ish.h | 1 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ipc/pci-ish.c | 1 | ||||
-rw-r--r-- | drivers/hid/wacom_wac.c | 19 |
15 files changed, 1056 insertions, 73 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 61e1953ff921..6d62e19f1a87 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -182,6 +182,19 @@ config HID_BETOP_FF Currently the following devices are known to be supported: - BETOP 2185 PC & BFM MODE +config HID_BIGBEN_FF + tristate "BigBen Interactive Kids' gamepad support" + depends on USB_HID + depends on NEW_LEDS + depends on LEDS_CLASS + select INPUT_FF_MEMLESS + default !EXPERT + help + Support for the "Kid-friendly Wired Controller" PS3OFMINIPAD + gamepad made by BigBen Interactive, originally sold as a PS3 + accessory. This driver fixes input mapping and adds support for + force feedback effects and LEDs on the device. + config HID_CHERRY tristate "Cherry Cymotion keyboard" depends on HID @@ -351,7 +364,7 @@ config HOLTEK_FF config HID_GOOGLE_HAMMER tristate "Google Hammer Keyboard" - depends on USB_HID && LEDS_CLASS + depends on USB_HID && LEDS_CLASS && MFD_CROS_EC ---help--- Say Y here if you have a Google Hammer device. diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index bd7ac53b75c5..896a51ce7ce0 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_HID_ASUS) += hid-asus.o obj-$(CONFIG_HID_AUREAL) += hid-aureal.o obj-$(CONFIG_HID_BELKIN) += hid-belkin.o obj-$(CONFIG_HID_BETOP_FF) += hid-betopff.o +obj-$(CONFIG_HID_BIGBEN_FF) += hid-bigbenff.o obj-$(CONFIG_HID_CHERRY) += hid-cherry.o obj-$(CONFIG_HID_CHICONY) += hid-chicony.o obj-$(CONFIG_HID_CMEDIA) += hid-cmedia.o diff --git a/drivers/hid/hid-bigbenff.c b/drivers/hid/hid-bigbenff.c new file mode 100644 index 000000000000..3f6abd190df4 --- /dev/null +++ b/drivers/hid/hid-bigbenff.c @@ -0,0 +1,414 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * LED & force feedback support for BigBen Interactive + * + * 0x146b:0x0902 "Bigben Interactive Bigben Game Pad" + * "Kid-friendly Wired Controller" PS3OFMINIPAD SONY + * sold for use with the PS3 + * + * Copyright (c) 2018 Hanno Zulla <kontakt@hanno.de> + */ + +#include <linux/input.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/leds.h> +#include <linux/hid.h> + +#include "hid-ids.h" + + +/* + * The original descriptor for 0x146b:0x0902 + * + * 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + * 0x09, 0x05, // Usage (Game Pad) + * 0xA1, 0x01, // Collection (Application) + * 0x15, 0x00, // Logical Minimum (0) + * 0x25, 0x01, // Logical Maximum (1) + * 0x35, 0x00, // Physical Minimum (0) + * 0x45, 0x01, // Physical Maximum (1) + * 0x75, 0x01, // Report Size (1) + * 0x95, 0x0D, // Report Count (13) + * 0x05, 0x09, // Usage Page (Button) + * 0x19, 0x01, // Usage Minimum (0x01) + * 0x29, 0x0D, // Usage Maximum (0x0D) + * 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + * 0x95, 0x03, // Report Count (3) + * 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + * 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) + * 0x25, 0x07, // Logical Maximum (7) + * 0x46, 0x3B, 0x01, // Physical Maximum (315) + * 0x75, 0x04, // Report Size (4) + * 0x95, 0x01, // Report Count (1) + * 0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter) + * 0x09, 0x39, // Usage (Hat switch) + * 0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State) + * 0x65, 0x00, // Unit (None) + * 0x95, 0x01, // Report Count (1) + * 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + * 0x26, 0xFF, 0x00, // Logical Maximum (255) + * 0x46, 0xFF, 0x00, // Physical Maximum (255) + * 0x09, 0x30, // Usage (X) + * 0x09, 0x31, // Usage (Y) + * 0x09, 0x32, // Usage (Z) + * 0x09, 0x35, // Usage (Rz) + * 0x75, 0x08, // Report Size (8) + * 0x95, 0x04, // Report Count (4) + * 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + * 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) + * 0x09, 0x20, // Usage (0x20) + * 0x09, 0x21, // Usage (0x21) + * 0x09, 0x22, // Usage (0x22) + * 0x09, 0x23, // Usage (0x23) + * 0x09, 0x24, // Usage (0x24) + * 0x09, 0x25, // Usage (0x25) + * 0x09, 0x26, // Usage (0x26) + * 0x09, 0x27, // Usage (0x27) + * 0x09, 0x28, // Usage (0x28) + * 0x09, 0x29, // Usage (0x29) + * 0x09, 0x2A, // Usage (0x2A) + * 0x09, 0x2B, // Usage (0x2B) + * 0x95, 0x0C, // Report Count (12) + * 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + * 0x0A, 0x21, 0x26, // Usage (0x2621) + * 0x95, 0x08, // Report Count (8) + * 0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + * 0x0A, 0x21, 0x26, // Usage (0x2621) + * 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + * 0x26, 0xFF, 0x03, // Logical Maximum (1023) + * 0x46, 0xFF, 0x03, // Physical Maximum (1023) + * 0x09, 0x2C, // Usage (0x2C) + * 0x09, 0x2D, // Usage (0x2D) + * 0x09, 0x2E, // Usage (0x2E) + * 0x09, 0x2F, // Usage (0x2F) + * 0x75, 0x10, // Report Size (16) + * 0x95, 0x04, // Report Count (4) + * 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + * 0xC0, // End Collection + */ + +#define PID0902_RDESC_ORIG_SIZE 137 + +/* + * The fixed descriptor for 0x146b:0x0902 + * + * - map buttons according to gamepad.rst + * - assign right stick from Z/Rz to Rx/Ry + * - map previously unused analog trigger data to Z/RZ + * - simplify feature and output descriptor + */ +static __u8 pid0902_rdesc_fixed[] = { + 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */ + 0x09, 0x05, /* Usage (Game Pad) */ + 0xA1, 0x01, /* Collection (Application) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x25, 0x01, /* Logical Maximum (1) */ + 0x35, 0x00, /* Physical Minimum (0) */ + 0x45, 0x01, /* Physical Maximum (1) */ + 0x75, 0x01, /* Report Size (1) */ + 0x95, 0x0D, /* Report Count (13) */ + 0x05, 0x09, /* Usage Page (Button) */ + 0x09, 0x05, /* Usage (BTN_WEST) */ + 0x09, 0x01, /* Usage (BTN_SOUTH) */ + 0x09, 0x02, /* Usage (BTN_EAST) */ + 0x09, 0x04, /* Usage (BTN_NORTH) */ + 0x09, 0x07, /* Usage (BTN_TL) */ + 0x09, 0x08, /* Usage (BTN_TR) */ + 0x09, 0x09, /* Usage (BTN_TL2) */ + 0x09, 0x0A, /* Usage (BTN_TR2) */ + 0x09, 0x0B, /* Usage (BTN_SELECT) */ + 0x09, 0x0C, /* Usage (BTN_START) */ + 0x09, 0x0E, /* Usage (BTN_THUMBL) */ + 0x09, 0x0F, /* Usage (BTN_THUMBR) */ + 0x09, 0x0D, /* Usage (BTN_MODE) */ + 0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */ + 0x75, 0x01, /* Report Size (1) */ + 0x95, 0x03, /* Report Count (3) */ + 0x81, 0x01, /* Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */ + 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */ + 0x25, 0x07, /* Logical Maximum (7) */ + 0x46, 0x3B, 0x01, /* Physical Maximum (315) */ + 0x75, 0x04, /* Report Size (4) */ + 0x95, 0x01, /* Report Count (1) */ + 0x65, 0x14, /* Unit (System: English Rotation, Length: Centimeter) */ + 0x09, 0x39, /* Usage (Hat switch) */ + 0x81, 0x42, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State) */ + 0x65, 0x00, /* Unit (None) */ + 0x95, 0x01, /* Report Count (1) */ + 0x81, 0x01, /* Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255) */ + 0x46, 0xFF, 0x00, /* Physical Maximum (255) */ + 0x09, 0x30, /* Usage (X) */ + 0x09, 0x31, /* Usage (Y) */ + 0x09, 0x33, /* Usage (Rx) */ + 0x09, 0x34, /* Usage (Ry) */ + 0x75, 0x08, /* Report Size (8) */ + 0x95, 0x04, /* Report Count (4) */ + 0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */ + 0x95, 0x0A, /* Report Count (10) */ + 0x81, 0x01, /* Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */ + 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */ + 0x26, 0xFF, 0x00, /* Logical Maximum (255) */ + 0x46, 0xFF, 0x00, /* Physical Maximum (255) */ + 0x09, 0x32, /* Usage (Z) */ + 0x09, 0x35, /* Usage (Rz) */ + 0x95, 0x02, /* Report Count (2) */ + 0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */ + 0x95, 0x08, /* Report Count (8) */ + 0x81, 0x01, /* Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */ + 0x06, 0x00, 0xFF, /* Usage Page (Vendor Defined 0xFF00) */ + 0xB1, 0x02, /* Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) */ + 0x0A, 0x21, 0x26, /* Usage (0x2621) */ + 0x95, 0x08, /* Report Count (8) */ + 0x91, 0x02, /* Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) */ + 0x0A, 0x21, 0x26, /* Usage (0x2621) */ + 0x95, 0x08, /* Report Count (8) */ + 0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */ + 0xC0, /* End Collection */ +}; + +#define NUM_LEDS 4 + +struct bigben_device { + struct hid_device *hid; + struct hid_report *report; + u8 led_state; /* LED1 = 1 .. LED4 = 8 */ + u8 right_motor_on; /* right motor off/on 0/1 */ + u8 left_motor_force; /* left motor force 0-255 */ + struct led_classdev *leds[NUM_LEDS]; + bool work_led; + bool work_ff; + struct work_struct worker; +}; + + +static void bigben_worker(struct work_struct *work) +{ + struct bigben_device *bigben = container_of(work, + struct bigben_device, worker); + struct hid_field *report_field = bigben->report->field[0]; + + if (bigben->work_led) { + bigben->work_led = false; + report_field->value[0] = 0x01; /* 1 = led message */ + report_field->value[1] = 0x08; /* reserved value, always 8 */ + report_field->value[2] = bigben->led_state; + report_field->value[3] = 0x00; /* padding */ + report_field->value[4] = 0x00; /* padding */ + report_field->value[5] = 0x00; /* padding */ + report_field->value[6] = 0x00; /* padding */ + report_field->value[7] = 0x00; /* padding */ + hid_hw_request(bigben->hid, bigben->report, HID_REQ_SET_REPORT); + } + + if (bigben->work_ff) { + bigben->work_ff = false; + report_field->value[0] = 0x02; /* 2 = rumble effect message */ + report_field->value[1] = 0x08; /* reserved value, always 8 */ + report_field->value[2] = bigben->right_motor_on; + report_field->value[3] = bigben->left_motor_force; + report_field->value[4] = 0xff; /* duration 0-254 (255 = nonstop) */ + report_field->value[5] = 0x00; /* padding */ + report_field->value[6] = 0x00; /* padding */ + report_field->value[7] = 0x00; /* padding */ + hid_hw_request(bigben->hid, bigben->report, HID_REQ_SET_REPORT); + } +} + +static int hid_bigben_play_effect(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct bigben_device *bigben = data; + u8 right_motor_on; + u8 left_motor_force; + + if (effect->type != FF_RUMBLE) + return 0; + + right_motor_on = effect->u.rumble.weak_magnitude ? 1 : 0; + left_motor_force = effect->u.rumble.strong_magnitude / 256; + + if (right_motor_on != bigben->right_motor_on || + left_motor_force != bigben->left_motor_force) { + bigben->right_motor_on = right_motor_on; + bigben->left_motor_force = left_motor_force; + bigben->work_ff = true; + schedule_work(&bigben->worker); + } + + return 0; +} + +static void bigben_set_led(struct led_classdev *led, + enum led_brightness value) +{ + struct device *dev = led->dev->parent; + struct hid_device *hid = to_hid_device(dev); + struct bigben_device *bigben = hid_get_drvdata(hid); + int n; + bool work; + + if (!bigben) { + hid_err(hid, "no device data\n"); + return; + } + + for (n = 0; n < NUM_LEDS; n++) { + if (led == bigben->leds[n]) { + if (value == LED_OFF) { + work = (bigben->led_state & BIT(n)); + bigben->led_state &= ~BIT(n); + } else { + work = !(bigben->led_state & BIT(n)); + bigben->led_state |= BIT(n); + } + + if (work) { + bigben->work_led = true; + schedule_work(&bigben->worker); + } + return; + } + } +} + +static enum led_brightness bigben_get_led(struct led_classdev *led) +{ + struct device *dev = led->dev->parent; + struct hid_device *hid = to_hid_device(dev); + struct bigben_device *bigben = hid_get_drvdata(hid); + int n; + + if (!bigben) { + hid_err(hid, "no device data\n"); + return LED_OFF; + } + + for (n = 0; n < NUM_LEDS; n++) { + if (led == bigben->leds[n]) + return (bigben->led_state & BIT(n)) ? LED_ON : LED_OFF; + } + + return LED_OFF; +} + +static void bigben_remove(struct hid_device *hid) +{ + struct bigben_device *bigben = hid_get_drvdata(hid); + + cancel_work_sync(&bigben->worker); + hid_hw_close(hid); + hid_hw_stop(hid); +} + +static int bigben_probe(struct hid_device *hid, + const struct hid_device_id *id) +{ + struct bigben_device *bigben; + struct hid_input *hidinput; + struct list_head *report_list; + struct led_classdev *led; + char *name; + size_t name_sz; + int n, error; + + bigben = devm_kzalloc(&hid->dev, sizeof(*bigben), GFP_KERNEL); + if (!bigben) + return -ENOMEM; + hid_set_drvdata(hid, bigben); + bigben->hid = hid; + + error = hid_parse(hid); + if (error) { + hid_err(hid, "parse failed\n"); + return error; + } + + error = hid_hw_start(hid, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); + if (error) { + hid_err(hid, "hw start failed\n"); + return error; + } + + report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; + bigben->report = list_entry(report_list->next, + struct hid_report, list); + + hidinput = list_first_entry(&hid->inputs, struct hid_input, list); + set_bit(FF_RUMBLE, hidinput->input->ffbit); + + INIT_WORK(&bigben->worker, bigben_worker); + + error = input_ff_create_memless(hidinput->input, bigben, + hid_bigben_play_effect); + if (error) + return error; + + name_sz = strlen(dev_name(&hid->dev)) + strlen(":red:bigben#") + 1; + + for (n = 0; n < NUM_LEDS; n++) { + led = devm_kzalloc( + &hid->dev, + sizeof(struct led_classdev) + name_sz, + GFP_KERNEL + ); + if (!led) + return -ENOMEM; + name = (void *)(&led[1]); + snprintf(name, name_sz, + "%s:red:bigben%d", + dev_name(&hid->dev), n + 1 + ); + led->name = name; + led->brightness = (n == 0) ? LED_ON : LED_OFF; + led->max_brightness = 1; + led->brightness_get = bigben_get_led; + led->brightness_set = bigben_set_led; + bigben->leds[n] = led; + error = devm_led_classdev_register(&hid->dev, led); + if (error) + return error; + } + + /* initial state: LED1 is on, no rumble effect */ + bigben->led_state = BIT(0); + bigben->right_motor_on = 0; + bigben->left_motor_force = 0; + bigben->work_led = true; + bigben->work_ff = true; + schedule_work(&bigben->worker); + + hid_info(hid, "LED and force feedback support for BigBen gamepad\n"); + + return 0; +} + +static __u8 *bigben_report_fixup(struct hid_device *hid, __u8 *rdesc, + unsigned int *rsize) +{ + if (*rsize == PID0902_RDESC_ORIG_SIZE) { + rdesc = pid0902_rdesc_fixed; + *rsize = sizeof(pid0902_rdesc_fixed); + } else + hid_warn(hid, "unexpected rdesc, please submit for review\n"); + return rdesc; +} + +static const struct hid_device_id bigben_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_BIGBEN, USB_DEVICE_ID_BIGBEN_PS3OFMINIPAD) }, + { } +}; +MODULE_DEVICE_TABLE(hid, bigben_devices); + +static struct hid_driver bigben_driver = { + .name = "bigben", + .id_table = bigben_devices, + .probe = bigben_probe, + .report_fixup = bigben_report_fixup, + .remove = bigben_remove, +}; +module_hid_driver(bigben_driver); + +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 44564f61e9cc..5bec9244c45b 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -406,7 +406,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: parser->global.report_size = item_udata(item); - if (parser->global.report_size > 128) { + if (parser->global.report_size > 256) { hid_err(parser->device, "invalid report_size %d\n", parser->global.report_size); return -1; diff --git a/drivers/hid/hid-cougar.c b/drivers/hid/hid-cougar.c index ad2e87de7dc5..3f0916b64c60 100644 --- a/drivers/hid/hid-cougar.c +++ b/drivers/hid/hid-cougar.c @@ -7,6 +7,7 @@ #include <linux/hid.h> #include <linux/module.h> +#include <linux/printk.h> #include "hid-ids.h" @@ -15,11 +16,9 @@ MODULE_DESCRIPTION("Cougar 500k Gaming Keyboard"); MODULE_LICENSE("GPL"); MODULE_INFO(key_mappings, "G1-G6 are mapped to F13-F18"); -static int cougar_g6_is_space = 1; -module_param_named(g6_is_space, cougar_g6_is_space, int, 0600); +static bool g6_is_space = true; MODULE_PARM_DESC(g6_is_space, - "If set, G6 programmable key sends SPACE instead of F18 (0=off, 1=on) (default=1)"); - + "If true, G6 programmable key sends SPACE instead of F18 (default=true)"); #define COUGAR_VENDOR_USAGE 0xff00ff00 @@ -82,20 +81,23 @@ struct cougar { static LIST_HEAD(cougar_udev_list); static DEFINE_MUTEX(cougar_udev_list_lock); -static void cougar_fix_g6_mapping(struct hid_device *hdev) +/** + * cougar_fix_g6_mapping - configure the mapping for key G6/Spacebar + */ +static void cougar_fix_g6_mapping(void) { int i; for (i = 0; cougar_mapping[i][0]; i++) { if (cougar_mapping[i][0] == COUGAR_KEY_G6) { cougar_mapping[i][1] = - cougar_g6_is_space ? KEY_SPACE : KEY_F18; - hid_info(hdev, "G6 mapped to %s\n", - cougar_g6_is_space ? "space" : "F18"); + g6_is_space ? KEY_SPACE : KEY_F18; + pr_info("cougar: G6 mapped to %s\n", + g6_is_space ? "space" : "F18"); return; } } - hid_warn(hdev, "no mapping defined for G6/spacebar"); + pr_warn("cougar: no mappings defined for G6/spacebar"); } /* @@ -154,7 +156,8 @@ static void cougar_remove_shared_data(void *resource) * Bind the device group's shared data to this cougar struct. * If no shared data exists for this group, create and initialize it. */ -static int cougar_bind_shared_data(struct hid_device *hdev, struct cougar *cougar) +static int cougar_bind_shared_data(struct hid_device *hdev, + struct cougar *cougar) { struct cougar_shared *shared; int error = 0; @@ -228,7 +231,6 @@ static int cougar_probe(struct hid_device *hdev, * to it. */ if (hdev->collection->usage == HID_GD_KEYBOARD) { - cougar_fix_g6_mapping(hdev); list_for_each_entry_safe(hidinput, next, &hdev->inputs, list) { if (hidinput->registered && hidinput->input != NULL) { cougar->shared->input = hidinput->input; @@ -237,6 +239,8 @@ static int cougar_probe(struct hid_device *hdev, } } } else if (hdev->collection->usage == COUGAR_VENDOR_USAGE) { + /* Preinit the mapping table */ + cougar_fix_g6_mapping(); error = hid_hw_open(hdev); if (error) goto fail_stop_and_cleanup; @@ -257,26 +261,32 @@ static int cougar_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) { struct cougar *cougar; + struct cougar_shared *shared; unsigned char code, action; int i; cougar = hid_get_drvdata(hdev); - if (!cougar->special_intf || !cougar->shared || - !cougar->shared->input || !cougar->shared->enabled) + shared = cougar->shared; + if (!cougar->special_intf || !shared) return 0; + if (!shared->enabled || !shared->input) + return -EPERM; + code = data[COUGAR_FIELD_CODE]; action = data[COUGAR_FIELD_ACTION]; for (i = 0; cougar_mapping[i][0]; i++) { if (code == cougar_mapping[i][0]) { - input_event(cougar->shared->input, EV_KEY, + input_event(shared->input, EV_KEY, cougar_mapping[i][1], action); - input_sync(cougar->shared->input); - return 0; + input_sync(shared->input); + return -EPERM; } } - hid_warn(hdev, "unmapped special key code %x: ignoring\n", code); - return 0; + /* Avoid warnings on the same unmapped key twice */ + if (action != 0) + hid_warn(hdev, "unmapped special key code %0x: ignoring\n", code); + return -EPERM; } static void cougar_remove(struct hid_device *hdev) @@ -293,6 +303,26 @@ static void cougar_remove(struct hid_device *hdev) hid_hw_stop(hdev); } +static int cougar_param_set_g6_is_space(const char *val, + const struct kernel_param *kp) +{ + int ret; + + ret = param_set_bool(val, kp); + if (ret) + return ret; + + cougar_fix_g6_mapping(); + + return 0; +} + +static const struct kernel_param_ops cougar_g6_is_space_ops = { + .set = cougar_param_set_g6_is_space, + .get = param_get_bool, +}; +module_param_cb(g6_is_space, &cougar_g6_is_space_ops, &g6_is_space, 0644); + static struct hid_device_id cougar_id_table[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SOLID_YEAR, USB_DEVICE_ID_COUGAR_500K_GAMING_KEYBOARD) }, diff --git a/drivers/hid/hid-elan.c b/drivers/hid/hid-elan.c index 07e26c3567eb..0bfd6d1b44c1 100644 --- a/drivers/hid/hid-elan.c +++ b/drivers/hid/hid-elan.c @@ -497,7 +497,7 @@ static int elan_probe(struct hid_device *hdev, const struct hid_device_id *id) return 0; if (!drvdata->input) { - hid_err(hdev, "Input device is not registred\n"); + hid_err(hdev, "Input device is not registered\n"); ret = -ENAVAIL; goto err; } diff --git a/drivers/hid/hid-google-hammer.c b/drivers/hid/hid-google-hammer.c index 6bf4da7ad63a..ee5e0bdcf078 100644 --- a/drivers/hid/hid-google-hammer.c +++ b/drivers/hid/hid-google-hammer.c @@ -13,16 +13,268 @@ * any later version. */ +#include <linux/acpi.h> #include <linux/hid.h> #include <linux/leds.h> +#include <linux/mfd/cros_ec.h> +#include <linux/mfd/cros_ec_commands.h> #include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_wakeup.h> +#include <asm/unaligned.h> #include "hid-ids.h" -#define MAX_BRIGHTNESS 100 +/* + * C(hrome)B(ase)A(ttached)S(witch) - switch exported by Chrome EC and reporting + * state of the "Whiskers" base - attached or detached. Whiskers USB device also + * reports position of the keyboard - folded or not. Combining base state and + * position allows us to generate proper "Tablet mode" events. + */ +struct cbas_ec { + struct device *dev; /* The platform device (EC) */ + struct input_dev *input; + bool base_present; + struct notifier_block notifier; +}; -/* HID usage for keyboard backlight (Alphanumeric display brightness) */ -#define HID_AD_BRIGHTNESS 0x00140046 +static struct cbas_ec cbas_ec; +static DEFINE_SPINLOCK(cbas_ec_lock); +static DEFINE_MUTEX(cbas_ec_reglock); + +static bool cbas_parse_base_state(const void *data) +{ + u32 switches = get_unaligned_le32(data); + + return !!(switches & BIT(EC_MKBP_BASE_ATTACHED)); +} + +static int cbas_ec_query_base(struct cros_ec_device *ec_dev, bool get_state, + bool *state) +{ + struct ec_params_mkbp_info *params; + struct cros_ec_command *msg; + int ret; + + msg = kzalloc(sizeof(*msg) + max(sizeof(u32), sizeof(*params)), + GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->command = EC_CMD_MKBP_INFO; + msg->version = 1; + msg->outsize = sizeof(*params); + msg->insize = sizeof(u32); + params = (struct ec_params_mkbp_info *)msg->data; + params->info_type = get_state ? + EC_MKBP_INFO_CURRENT : EC_MKBP_INFO_SUPPORTED; + params->event_type = EC_MKBP_EVENT_SWITCH; + + ret = cros_ec_cmd_xfer_status(ec_dev, msg); + if (ret >= 0) { + if (ret != sizeof(u32)) { + dev_warn(ec_dev->dev, "wrong result size: %d != %zu\n", + ret, sizeof(u32)); + ret = -EPROTO; + } else { + *state = cbas_parse_base_state(msg->data); + ret = 0; + } + } + + kfree(msg); + + return ret; +} + +static int cbas_ec_notify(struct notifier_block *nb, + unsigned long queued_during_suspend, + void *_notify) +{ + struct cros_ec_device *ec = _notify; + unsigned long flags; + bool base_present; + + if (ec->event_data.event_type == EC_MKBP_EVENT_SWITCH) { + base_present = cbas_parse_base_state( + &ec->event_data.data.switches); + dev_dbg(cbas_ec.dev, + "%s: base: %d\n", __func__, base_present); + + if (device_may_wakeup(cbas_ec.dev) || + !queued_during_suspend) { + + pm_wakeup_event(cbas_ec.dev, 0); + + spin_lock_irqsave(&cbas_ec_lock, flags); + + /* + * While input layer dedupes the events, we do not want + * to disrupt the state reported by the base by + * overriding it with state reported by the LID. Only + * report changes, as we assume that on attach the base + * is not folded. + */ + if (base_present != cbas_ec.base_present) { + input_report_switch(cbas_ec.input, + SW_TABLET_MODE, + !base_present); + input_sync(cbas_ec.input); + cbas_ec.base_present = base_present; + } + + spin_unlock_irqrestore(&cbas_ec_lock, flags); + } + } + + return NOTIFY_OK; +} + +static __maybe_unused int cbas_ec_resume(struct device *dev) +{ + struct cros_ec_device *ec = dev_get_drvdata(dev->parent); + bool base_present; + int error; + + error = cbas_ec_query_base(ec, true, &base_present); + if (error) { + dev_warn(dev, "failed to fetch base state on resume: %d\n", + error); + } else { + spin_lock_irq(&cbas_ec_lock); + + cbas_ec.base_present = base_present; + + /* + * Only report if base is disconnected. If base is connected, + * it will resend its state on resume, and we'll update it + * in hammer_event(). + */ + if (!cbas_ec.base_present) { + input_report_switch(cbas_ec.input, SW_TABLET_MODE, 1); + input_sync(cbas_ec.input); + } + + spin_unlock_irq(&cbas_ec_lock); + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(cbas_ec_pm_ops, NULL, cbas_ec_resume); + +static void cbas_ec_set_input(struct input_dev *input) +{ + /* Take the lock so hammer_event() does not race with us here */ + spin_lock_irq(&cbas_ec_lock); + cbas_ec.input = input; + spin_unlock_irq(&cbas_ec_lock); +} + +static int __cbas_ec_probe(struct platform_device *pdev) +{ + struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); + struct input_dev *input; + bool base_supported; + int error; + + error = cbas_ec_query_base(ec, false, &base_supported); + if (error) + return error; + + if (!base_supported) + return -ENXIO; + + input = devm_input_allocate_device(&pdev->dev); + if (!input) + return -ENOMEM; + + input->name = "Whiskers Tablet Mode Switch"; + input->id.bustype = BUS_HOST; + + input_set_capability(input, EV_SW, SW_TABLET_MODE); + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, "cannot register input device: %d\n", + error); + return error; + } + + /* Seed the state */ + error = cbas_ec_query_base(ec, true, &cbas_ec.base_present); + if (error) { + dev_err(&pdev->dev, "cannot query base state: %d\n", error); + return error; + } + + input_report_switch(input, SW_TABLET_MODE, !cbas_ec.base_present); + + cbas_ec_set_input(input); + + cbas_ec.dev = &pdev->dev; + cbas_ec.notifier.notifier_call = cbas_ec_notify; + error = blocking_notifier_chain_register(&ec->event_notifier, + &cbas_ec.notifier); + if (error) { + dev_err(&pdev->dev, "cannot register notifier: %d\n", error); + cbas_ec_set_input(NULL); + return error; + } + + device_init_wakeup(&pdev->dev, true); + return 0; +} + +static int cbas_ec_probe(struct platform_device *pdev) +{ + int retval; + + mutex_lock(&cbas_ec_reglock); + + if (cbas_ec.input) { + retval = -EBUSY; + goto out; + } + + retval = __cbas_ec_probe(pdev); + +out: + mutex_unlock(&cbas_ec_reglock); + return retval; +} + +static int cbas_ec_remove(struct platform_device *pdev) +{ + struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); + + mutex_lock(&cbas_ec_reglock); + + blocking_notifier_chain_unregister(&ec->event_notifier, + &cbas_ec.notifier); + cbas_ec_set_input(NULL); + + mutex_unlock(&cbas_ec_reglock); + return 0; +} + +static const struct acpi_device_id cbas_ec_acpi_ids[] = { + { "GOOG000B", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, cbas_ec_acpi_ids); + +static struct platform_driver cbas_ec_driver = { + .probe = cbas_ec_probe, + .remove = cbas_ec_remove, + .driver = { + .name = "cbas_ec", + .acpi_match_table = ACPI_PTR(cbas_ec_acpi_ids), + .pm = &cbas_ec_pm_ops, + }, +}; + +#define MAX_BRIGHTNESS 100 struct hammer_kbd_leds { struct led_classdev cdev; @@ -90,33 +342,130 @@ static int hammer_register_leds(struct hid_device *hdev) return devm_led_classdev_register(&hdev->dev, &kbd_backlight->cdev); } -static int hammer_input_configured(struct hid_device *hdev, - struct hid_input *hi) +#define HID_UP_GOOGLEVENDOR 0xffd10000 +#define HID_VD_KBD_FOLDED 0x00000019 +#define WHISKERS_KBD_FOLDED (HID_UP_GOOGLEVENDOR | HID_VD_KBD_FOLDED) + +/* HID usage for keyboard backlight (Alphanumeric display brightness) */ +#define HID_AD_BRIGHTNESS 0x00140046 + +static int hammer_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, + struct hid_usage *usage, + unsigned long **bit, int *max) { - struct list_head *report_list = - &hdev->report_enum[HID_OUTPUT_REPORT].report_list; + if (hdev->product == USB_DEVICE_ID_GOOGLE_WHISKERS && + usage->hid == WHISKERS_KBD_FOLDED) { + /* + * We do not want to have this usage mapped as it will get + * mixed in with "base attached" signal and delivered over + * separate input device for tablet switch mode. + */ + return -1; + } + + return 0; +} + +static int hammer_event(struct hid_device *hid, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + unsigned long flags; + + if (hid->product == USB_DEVICE_ID_GOOGLE_WHISKERS && + usage->hid == WHISKERS_KBD_FOLDED) { + spin_lock_irqsave(&cbas_ec_lock, flags); + + hid_dbg(hid, "%s: base: %d, folded: %d\n", __func__, + cbas_ec.base_present, value); + + /* + * We should not get event if base is detached, but in case + * we happen to service HID and EC notifications out of order + * let's still check the "base present" flag. + */ + if (cbas_ec.input && cbas_ec.base_present) { + input_report_switch(cbas_ec.input, + SW_TABLET_MODE, value); + input_sync(cbas_ec.input); + } + + spin_unlock_irqrestore(&cbas_ec_lock, flags); + return 1; /* We handled this event */ + } + + return 0; +} + +static bool hammer_is_keyboard_interface(struct hid_device *hdev) +{ + struct hid_report_enum *re = &hdev->report_enum[HID_INPUT_REPORT]; struct hid_report *report; - if (list_empty(report_list)) - return 0; + list_for_each_entry(report, &re->report_list, list) + if (report->application == HID_GD_KEYBOARD) + return true; - report = list_first_entry(report_list, struct hid_report, list); + return false; +} + +static bool hammer_has_backlight_control(struct hid_device *hdev) +{ + struct hid_report_enum *re = &hdev->report_enum[HID_OUTPUT_REPORT]; + struct hid_report *report; + int i, j; - if (report->maxfield == 1 && - report->field[0]->application == HID_GD_KEYBOARD && - report->field[0]->maxusage == 1 && - report->field[0]->usage[0].hid == HID_AD_BRIGHTNESS) { - int err = hammer_register_leds(hdev); + list_for_each_entry(report, &re->report_list, list) { + if (report->application != HID_GD_KEYBOARD) + continue; - if (err) + for (i = 0; i < report->maxfield; i++) { + struct hid_field *field = report->field[i]; + + for (j = 0; j < field->maxusage; j++) + if (field->usage[j].hid == HID_AD_BRIGHTNESS) + return true; + } + } + + return false; +} + +static int hammer_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int error; + + /* + * We always want to poll for, and handle tablet mode events from + * Whiskers, even when nobody has opened the input device. This also + * prevents the hid core from dropping early tablet mode events from + * the device. + */ + if (hdev->product == USB_DEVICE_ID_GOOGLE_WHISKERS && + hammer_is_keyboard_interface(hdev)) + hdev->quirks |= HID_QUIRK_ALWAYS_POLL; + + error = hid_parse(hdev); + if (error) + return error; + + error = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (error) + return error; + + if (hammer_has_backlight_control(hdev)) { + error = hammer_register_leds(hdev); + if (error) hid_warn(hdev, "Failed to register keyboard backlight: %d\n", - err); + error); } return 0; } + static const struct hid_device_id hammer_devices[] = { { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_HAMMER) }, @@ -133,8 +482,34 @@ MODULE_DEVICE_TABLE(hid, hammer_devices); static struct hid_driver hammer_driver = { .name = "hammer", .id_table = hammer_devices, - .input_configured = hammer_input_configured, + .probe = hammer_probe, + .input_mapping = hammer_input_mapping, + .event = hammer_event, }; -module_hid_driver(hammer_driver); + +static int __init hammer_init(void) +{ + int error; + + error = platform_driver_register(&cbas_ec_driver); + if (error) + return error; + + error = hid_register_driver(&hammer_driver); + if (error) { + platform_driver_unregister(&cbas_ec_driver); + return error; + } + + return 0; +} +module_init(hammer_init); + +static void __exit hammer_exit(void) +{ + hid_unregister_driver(&hammer_driver); + platform_driver_unregister(&cbas_ec_driver); +} +module_exit(hammer_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 5146ee029db4..c5233389836b 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -92,6 +92,7 @@ #define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304 #define USB_DEVICE_ID_APPLE_MAGICMOUSE 0x030d #define USB_DEVICE_ID_APPLE_MAGICTRACKPAD 0x030e +#define USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 0x0265 #define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI 0x020e #define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f #define USB_DEVICE_ID_APPLE_GEYSER_ANSI 0x0214 @@ -229,6 +230,9 @@ #define USB_VENDOR_ID_BETOP_2185V2PC 0x8380 #define USB_VENDOR_ID_BETOP_2185V2BFM 0x20bc +#define USB_VENDOR_ID_BIGBEN 0x146b +#define USB_DEVICE_ID_BIGBEN_PS3OFMINIPAD 0x0902 + #define USB_VENDOR_ID_BTC 0x046e #define USB_DEVICE_ID_BTC_EMPREX_REMOTE 0x5578 #define USB_DEVICE_ID_BTC_EMPREX_REMOTE_2 0x5577 @@ -342,6 +346,7 @@ #define USB_DEVICE_ID_DMI_ENC 0x5fab #define USB_VENDOR_ID_DRAGONRISE 0x0079 +#define USB_DEVICE_ID_REDRAGON_SEYMUR2 0x0006 #define USB_DEVICE_ID_DRAGONRISE_WIIU 0x1800 #define USB_DEVICE_ID_DRAGONRISE_PS3 0x1801 #define USB_DEVICE_ID_DRAGONRISE_DOLPHINBAR 0x1803 @@ -976,7 +981,6 @@ #define USB_DEVICE_ID_SIS817_TOUCH 0x0817 #define USB_DEVICE_ID_SIS_TS 0x1013 #define USB_DEVICE_ID_SIS1030_TOUCH 0x1030 -#define USB_DEVICE_ID_SIS10FB_TOUCH 0x10fb #define USB_VENDOR_ID_SKYCABLE 0x1223 #define USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER 0x3F07 diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index a481eaf39e88..ad823a01bd65 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -758,6 +758,11 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel break; case HID_UP_DIGITIZER: + if ((field->application & 0xff) == 0x01) /* Digitizer */ + __set_bit(INPUT_PROP_POINTER, input->propbit); + else if ((field->application & 0xff) == 0x02) /* Pen */ + __set_bit(INPUT_PROP_DIRECT, input->propbit); + switch (usage->hid & 0xff) { case 0x00: /* Undefined */ goto ignore; @@ -1516,6 +1521,7 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid, struct hid_input *hidinput = kzalloc(sizeof(*hidinput), GFP_KERNEL); struct input_dev *input_dev = input_allocate_device(); const char *suffix = NULL; + size_t suffix_len, name_len; if (!hidinput || !input_dev) goto fail; @@ -1559,10 +1565,15 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid, } if (suffix) { - hidinput->name = kasprintf(GFP_KERNEL, "%s %s", - hid->name, suffix); - if (!hidinput->name) - goto fail; + name_len = strlen(hid->name); + suffix_len = strlen(suffix); + if ((name_len < suffix_len) || + strcmp(hid->name + name_len - suffix_len, suffix)) { + hidinput->name = kasprintf(GFP_KERNEL, "%s %s", + hid->name, suffix); + if (!hidinput->name) + goto fail; + } } input_set_drvdata(input_dev, hid); diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index b454c4386157..1d5ea678d268 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -54,6 +54,8 @@ module_param(report_undeciphered, bool, 0644); MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event"); #define TRACKPAD_REPORT_ID 0x28 +#define TRACKPAD2_USB_REPORT_ID 0x02 +#define TRACKPAD2_BT_REPORT_ID 0x31 #define MOUSE_REPORT_ID 0x29 #define DOUBLE_REPORT_ID 0xf7 /* These definitions are not precise, but they're close enough. (Bits @@ -91,6 +93,17 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie #define TRACKPAD_RES_Y \ ((TRACKPAD_MAX_Y - TRACKPAD_MIN_Y) / (TRACKPAD_DIMENSION_Y / 100)) +#define TRACKPAD2_DIMENSION_X (float)16000 +#define TRACKPAD2_MIN_X -3678 +#define TRACKPAD2_MAX_X 3934 +#define TRACKPAD2_RES_X \ + ((TRACKPAD2_MAX_X - TRACKPAD2_MIN_X) / (TRACKPAD2_DIMENSION_X / 100)) +#define TRACKPAD2_DIMENSION_Y (float)11490 +#define TRACKPAD2_MIN_Y -2478 +#define TRACKPAD2_MAX_Y 2587 +#define TRACKPAD2_RES_Y \ + ((TRACKPAD2_MAX_Y - TRACKPAD2_MIN_Y) / (TRACKPAD2_DIMENSION_Y / 100)) + /** * struct magicmouse_sc - Tracks Magic Mouse-specific data. * @input: Input device through which we report events. @@ -183,6 +196,7 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda { struct input_dev *input = msc->input; int id, x, y, size, orientation, touch_major, touch_minor, state, down; + int pressure = 0; if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { id = (tdata[6] << 2 | tdata[5] >> 6) & 0xf; @@ -194,6 +208,17 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda touch_minor = tdata[4]; state = tdata[7] & TOUCH_STATE_MASK; down = state != TOUCH_STATE_NONE; + } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { + id = tdata[8] & 0xf; + x = (tdata[1] << 27 | tdata[0] << 19) >> 19; + y = -((tdata[3] << 30 | tdata[2] << 22 | tdata[1] << 14) >> 19); + size = tdata[6]; + orientation = (tdata[8] >> 5) - 4; + touch_major = tdata[4]; + touch_minor = tdata[5]; + pressure = tdata[7]; + state = tdata[3] & 0xC0; + down = state == 0x80; } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ id = (tdata[7] << 2 | tdata[6] >> 6) & 0xf; x = (tdata[1] << 27 | tdata[0] << 19) >> 19; @@ -215,7 +240,8 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda /* If requested, emulate a scroll wheel by detecting small * vertical touch motions. */ - if (emulate_scroll_wheel) { + if (emulate_scroll_wheel && (input->id.product != + USB_DEVICE_ID_APPLE_MAGICTRACKPAD2)) { unsigned long now = jiffies; int step_x = msc->touches[id].scroll_x - x; int step_y = msc->touches[id].scroll_y - y; @@ -269,10 +295,14 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); + if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) + input_report_abs(input, ABS_MT_PRESSURE, pressure); + if (report_undeciphered) { if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) input_event(input, EV_MSC, MSC_RAW, tdata[7]); - else /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ + else if (input->id.product != + USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) input_event(input, EV_MSC, MSC_RAW, tdata[8]); } } @@ -287,6 +317,7 @@ static int magicmouse_raw_event(struct hid_device *hdev, switch (data[0]) { case TRACKPAD_REPORT_ID: + case TRACKPAD2_BT_REPORT_ID: /* Expect four bytes of prefix, and N*9 bytes of touch data. */ if (size < 4 || ((size - 4) % 9) != 0) return 0; @@ -308,6 +339,22 @@ static int magicmouse_raw_event(struct hid_device *hdev, * ts = data[1] >> 6 | data[2] << 2 | data[3] << 10; */ break; + case TRACKPAD2_USB_REPORT_ID: + /* Expect twelve bytes of prefix and N*9 bytes of touch data. */ + if (size < 12 || ((size - 12) % 9) != 0) + return 0; + npoints = (size - 12) / 9; + if (npoints > 15) { + hid_warn(hdev, "invalid size value (%d) for TRACKPAD2_USB_REPORT_ID\n", + size); + return 0; + } + msc->ntouches = 0; + for (ii = 0; ii < npoints; ii++) + magicmouse_emit_touch(msc, ii, data + ii * 9 + 12); + + clicks = data[1]; + break; case MOUSE_REPORT_ID: /* Expect six bytes of prefix, and N*8 bytes of touch data. */ if (size < 6 || ((size - 6) % 8) != 0) @@ -352,6 +399,9 @@ static int magicmouse_raw_event(struct hid_device *hdev, magicmouse_emit_buttons(msc, clicks & 3); input_report_rel(input, REL_X, x); input_report_rel(input, REL_Y, y); + } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { + input_mt_sync_frame(input); + input_report_key(input, BTN_MOUSE, clicks & 1); } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ input_report_key(input, BTN_MOUSE, clicks & 1); input_mt_report_pointer_emulation(input, true); @@ -364,6 +414,7 @@ static int magicmouse_raw_event(struct hid_device *hdev, static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hdev) { int error; + int mt_flags = 0; __set_bit(EV_KEY, input->evbit); @@ -380,6 +431,22 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd __set_bit(REL_WHEEL, input->relbit); __set_bit(REL_HWHEEL, input->relbit); } + } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { + /* setting the device name to ensure the same driver settings + * get loaded, whether connected through bluetooth or USB + */ + input->name = "Apple Inc. Magic Trackpad 2"; + + __clear_bit(EV_MSC, input->evbit); + __clear_bit(BTN_0, input->keybit); + __clear_bit(BTN_RIGHT, input->keybit); + __clear_bit(BTN_MIDDLE, input->keybit); + __set_bit(BTN_MOUSE, input->keybit); + __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); + __set_bit(BTN_TOOL_FINGER, input->keybit); + + mt_flags = INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | + INPUT_MT_TRACK; } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ /* input->keybit is initialized with incorrect button info * for Magic Trackpad. There really is only one physical @@ -402,14 +469,13 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd __set_bit(EV_ABS, input->evbit); - error = input_mt_init_slots(input, 16, 0); + error = input_mt_init_slots(input, 16, mt_flags); if (error) return error; input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255 << 2, 4, 0); input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255 << 2, 4, 0); - input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0); /* Note: Touch Y position from the device is inverted relative * to how pointer motion is reported (and relative to how USB @@ -418,6 +484,7 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd * inverse of the reported Y. */ if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { + input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0); input_set_abs_params(input, ABS_MT_POSITION_X, MOUSE_MIN_X, MOUSE_MAX_X, 4, 0); input_set_abs_params(input, ABS_MT_POSITION_Y, @@ -427,7 +494,25 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd MOUSE_RES_X); input_abs_set_res(input, ABS_MT_POSITION_Y, MOUSE_RES_Y); + } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { + input_set_abs_params(input, ABS_MT_PRESSURE, 0, 253, 0, 0); + input_set_abs_params(input, ABS_PRESSURE, 0, 253, 0, 0); + input_set_abs_params(input, ABS_MT_ORIENTATION, -3, 4, 0, 0); + input_set_abs_params(input, ABS_X, TRACKPAD2_MIN_X, + TRACKPAD2_MAX_X, 0, 0); + input_set_abs_params(input, ABS_Y, TRACKPAD2_MIN_Y, + TRACKPAD2_MAX_Y, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_X, + TRACKPAD2_MIN_X, TRACKPAD2_MAX_X, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, + TRACKPAD2_MIN_Y, TRACKPAD2_MAX_Y, 0, 0); + + input_abs_set_res(input, ABS_X, TRACKPAD2_RES_X); + input_abs_set_res(input, ABS_Y, TRACKPAD2_RES_Y); + input_abs_set_res(input, ABS_MT_POSITION_X, TRACKPAD2_RES_X); + input_abs_set_res(input, ABS_MT_POSITION_Y, TRACKPAD2_RES_Y); } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ + input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0); input_set_abs_params(input, ABS_X, TRACKPAD_MIN_X, TRACKPAD_MAX_X, 4, 0); input_set_abs_params(input, ABS_Y, TRACKPAD_MIN_Y, @@ -447,7 +532,8 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd input_set_events_per_packet(input, 60); - if (report_undeciphered) { + if (report_undeciphered && + input->id.product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { __set_bit(EV_MSC, input->evbit); __set_bit(MSC_RAW, input->mscbit); } @@ -465,7 +551,8 @@ static int magicmouse_input_mapping(struct hid_device *hdev, msc->input = hi->input; /* Magic Trackpad does not give relative data after switching to MT */ - if (hi->input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD && + if ((hi->input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD || + hi->input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) && field->flags & HID_MAIN_ITEM_RELATIVE) return -1; @@ -494,11 +581,20 @@ static int magicmouse_input_configured(struct hid_device *hdev, static int magicmouse_probe(struct hid_device *hdev, const struct hid_device_id *id) { - const u8 feature[] = { 0xd7, 0x01 }; + const u8 *feature; + const u8 feature_mt[] = { 0xD7, 0x01 }; + const u8 feature_mt_trackpad2_usb[] = { 0x02, 0x01 }; + const u8 feature_mt_trackpad2_bt[] = { 0xF1, 0x02, 0x01 }; u8 *buf; struct magicmouse_sc *msc; struct hid_report *report; int ret; + int feature_size; + + if (id->vendor == USB_VENDOR_ID_APPLE && + id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 && + hdev->type != HID_TYPE_USBMOUSE) + return 0; msc = devm_kzalloc(&hdev->dev, sizeof(*msc), GFP_KERNEL); if (msc == NULL) { @@ -532,7 +628,14 @@ static int magicmouse_probe(struct hid_device *hdev, if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE) report = hid_register_report(hdev, HID_INPUT_REPORT, MOUSE_REPORT_ID, 0); - else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ + else if (id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { + if (id->vendor == BT_VENDOR_ID_APPLE) + report = hid_register_report(hdev, HID_INPUT_REPORT, + TRACKPAD2_BT_REPORT_ID, 0); + else /* USB_VENDOR_ID_APPLE */ + report = hid_register_report(hdev, HID_INPUT_REPORT, + TRACKPAD2_USB_REPORT_ID, 0); + } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ report = hid_register_report(hdev, HID_INPUT_REPORT, TRACKPAD_REPORT_ID, 0); report = hid_register_report(hdev, HID_INPUT_REPORT, @@ -546,7 +649,20 @@ static int magicmouse_probe(struct hid_device *hdev, } report->size = 6; - buf = kmemdup(feature, sizeof(feature), GFP_KERNEL); + if (id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { + if (id->vendor == BT_VENDOR_ID_APPLE) { + feature_size = sizeof(feature_mt_trackpad2_bt); + feature = feature_mt_trackpad2_bt; + } else { /* USB_VENDOR_ID_APPLE */ + feature_size = sizeof(feature_mt_trackpad2_usb); + feature = feature_mt_trackpad2_usb; + } + } else { + feature_size = sizeof(feature_mt); + feature = feature_mt; + } + + buf = kmemdup(feature, feature_size, GFP_KERNEL); if (!buf) { ret = -ENOMEM; goto err_stop_hw; @@ -560,10 +676,10 @@ static int magicmouse_probe(struct hid_device *hdev, * but there seems to be no other way of switching the mode. * Thus the super-ugly hacky success check below. */ - ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(feature), + ret = hid_hw_raw_request(hdev, buf[0], buf, feature_size, HID_FEATURE_REPORT, HID_REQ_SET_REPORT); kfree(buf); - if (ret != -EIO && ret != sizeof(feature)) { + if (ret != -EIO && ret != feature_size) { hid_err(hdev, "unable to request touch data (%d)\n", ret); goto err_stop_hw; } @@ -579,6 +695,10 @@ static const struct hid_device_id magic_mice[] = { USB_DEVICE_ID_APPLE_MAGICMOUSE), .driver_data = 0 }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD), .driver_data = 0 }, + { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_MAGICTRACKPAD2), .driver_data = 0 }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_MAGICTRACKPAD2), .driver_data = 0 }, { } }; MODULE_DEVICE_TABLE(hid, magic_mice); diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index 249d49b6b16c..52c3b01917e7 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -70,6 +70,7 @@ static const struct hid_device_id hid_quirks[] = { { HID_USB_DEVICE(USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC), HID_QUIRK_NOGET }, { HID_USB_DEVICE(USB_VENDOR_ID_DRACAL_RAPHNET, USB_DEVICE_ID_RAPHNET_2NES2SNES), HID_QUIRK_MULTI_INPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_DRACAL_RAPHNET, USB_DEVICE_ID_RAPHNET_4NES4SNES), HID_QUIRK_MULTI_INPUT }, + { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_REDRAGON_SEYMUR2), HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE }, { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_DOLPHINBAR), HID_QUIRK_MULTI_INPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE1), HID_QUIRK_MULTI_INPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_PS3), HID_QUIRK_MULTI_INPUT }, diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c index 823c63ad08b1..4aab96cf0818 100644 --- a/drivers/hid/i2c-hid/i2c-hid-core.c +++ b/drivers/hid/i2c-hid/i2c-hid-core.c @@ -48,7 +48,7 @@ /* quirks to control the device */ #define I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV BIT(0) #define I2C_HID_QUIRK_NO_IRQ_AFTER_RESET BIT(1) -#define I2C_HID_QUIRK_RESEND_REPORT_DESCR BIT(2) +#define I2C_HID_QUIRK_NO_RUNTIME_PM BIT(2) /* flags */ #define I2C_HID_STARTED 0 @@ -170,9 +170,8 @@ static const struct i2c_hid_quirks { { USB_VENDOR_ID_WEIDA, USB_DEVICE_ID_WEIDA_8755, I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV }, { I2C_VENDOR_ID_HANTICK, I2C_PRODUCT_ID_HANTICK_5288, - I2C_HID_QUIRK_NO_IRQ_AFTER_RESET }, - { USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS10FB_TOUCH, - I2C_HID_QUIRK_RESEND_REPORT_DESCR }, + I2C_HID_QUIRK_NO_IRQ_AFTER_RESET | + I2C_HID_QUIRK_NO_RUNTIME_PM }, { 0, 0 } }; @@ -1125,7 +1124,9 @@ static int i2c_hid_probe(struct i2c_client *client, goto err_mem_free; } - pm_runtime_put(&client->dev); + if (!(ihid->quirks & I2C_HID_QUIRK_NO_RUNTIME_PM)) + pm_runtime_put(&client->dev); + return 0; err_mem_free: @@ -1150,7 +1151,8 @@ static int i2c_hid_remove(struct i2c_client *client) struct i2c_hid *ihid = i2c_get_clientdata(client); struct hid_device *hid; - pm_runtime_get_sync(&client->dev); + if (!(ihid->quirks & I2C_HID_QUIRK_NO_RUNTIME_PM)) + pm_runtime_get_sync(&client->dev); pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); pm_runtime_put_noidle(&client->dev); @@ -1256,22 +1258,13 @@ static int i2c_hid_resume(struct device *dev) /* Instead of resetting device, simply powers the device on. This * solves "incomplete reports" on Raydium devices 2386:3118 and - * 2386:4B33 + * 2386:4B33 and fixes various SIS touchscreens no longer sending + * data after a suspend/resume. */ ret = i2c_hid_set_power(client, I2C_HID_PWR_ON); if (ret) return ret; - /* Some devices need to re-send report descr cmd - * after resume, after this it will be back normal. - * otherwise it issues too many incomplete reports. - */ - if (ihid->quirks & I2C_HID_QUIRK_RESEND_REPORT_DESCR) { - ret = i2c_hid_command(client, &hid_report_descr_cmd, NULL, 0); - if (ret) - return ret; - } - if (hid->driver && hid->driver->reset_resume) { ret = hid->driver->reset_resume(hid); return ret; diff --git a/drivers/hid/intel-ish-hid/ipc/hw-ish.h b/drivers/hid/intel-ish-hid/ipc/hw-ish.h index da133716bed0..08a8327dfd22 100644 --- a/drivers/hid/intel-ish-hid/ipc/hw-ish.h +++ b/drivers/hid/intel-ish-hid/ipc/hw-ish.h @@ -29,6 +29,7 @@ #define CNL_Ax_DEVICE_ID 0x9DFC #define GLK_Ax_DEVICE_ID 0x31A2 #define CNL_H_DEVICE_ID 0xA37C +#define ICL_MOBILE_DEVICE_ID 0x34FC #define SPT_H_DEVICE_ID 0xA135 #define REVISION_ID_CHT_A0 0x6 diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c b/drivers/hid/intel-ish-hid/ipc/pci-ish.c index 63c15c917ccb..8793cc49f855 100644 --- a/drivers/hid/intel-ish-hid/ipc/pci-ish.c +++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c @@ -38,6 +38,7 @@ static const struct pci_device_id ish_pci_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_Ax_DEVICE_ID)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, GLK_Ax_DEVICE_ID)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_H_DEVICE_ID)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ICL_MOBILE_DEVICE_ID)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_H_DEVICE_ID)}, {0, } }; diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index e0a06be5ef5c..5dd3a8245f0f 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -3335,6 +3335,7 @@ static void wacom_setup_intuos(struct wacom_wac *wacom_wac) void wacom_setup_device_quirks(struct wacom *wacom) { + struct wacom_wac *wacom_wac = &wacom->wacom_wac; struct wacom_features *features = &wacom->wacom_wac.features; /* The pen and pad share the same interface on most devices */ @@ -3464,6 +3465,24 @@ void wacom_setup_device_quirks(struct wacom *wacom) if (features->type == REMOTE) features->device_type |= WACOM_DEVICETYPE_WL_MONITOR; + + /* HID descriptor for DTK-2451 / DTH-2452 claims to report lots + * of things it shouldn't. Lets fix up the damage... + */ + if (wacom->hdev->product == 0x382 || wacom->hdev->product == 0x37d) { + features->quirks &= ~WACOM_QUIRK_TOOLSERIAL; + __clear_bit(BTN_TOOL_BRUSH, wacom_wac->pen_input->keybit); + __clear_bit(BTN_TOOL_PENCIL, wacom_wac->pen_input->keybit); + __clear_bit(BTN_TOOL_AIRBRUSH, wacom_wac->pen_input->keybit); + __clear_bit(ABS_Z, wacom_wac->pen_input->absbit); + __clear_bit(ABS_DISTANCE, wacom_wac->pen_input->absbit); + __clear_bit(ABS_TILT_X, wacom_wac->pen_input->absbit); + __clear_bit(ABS_TILT_Y, wacom_wac->pen_input->absbit); + __clear_bit(ABS_WHEEL, wacom_wac->pen_input->absbit); + __clear_bit(ABS_MISC, wacom_wac->pen_input->absbit); + __clear_bit(MSC_SERIAL, wacom_wac->pen_input->mscbit); + __clear_bit(EV_MSC, wacom_wac->pen_input->evbit); + } } int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, |