diff options
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/Kconfig | 13 | ||||
-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-ids.h | 5 | ||||
-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/wacom_wac.c | 19 |
11 files changed, 649 insertions, 35 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 68d2f773b522..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 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-ids.h b/drivers/hid/hid-ids.h index bc49909aba8e..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 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/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, |