diff options
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/Kconfig | 10 | ||||
-rw-r--r-- | drivers/hid/Makefile | 1 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 37 | ||||
-rw-r--r-- | drivers/hid/hid-cougar.c | 312 | ||||
-rw-r--r-- | drivers/hid/hid-debug.c | 14 | ||||
-rw-r--r-- | drivers/hid/hid-elan.c | 235 | ||||
-rw-r--r-- | drivers/hid/hid-google-hammer.c | 2 | ||||
-rw-r--r-- | drivers/hid/hid-ids.h | 6 | ||||
-rw-r--r-- | drivers/hid/hid-picolcd_fb.c | 3 | ||||
-rw-r--r-- | drivers/hid/hid-sensor-hub.c | 3 | ||||
-rw-r--r-- | drivers/hid/hid-steam.c | 10 | ||||
-rw-r--r-- | drivers/hid/hidraw.c | 2 | ||||
-rw-r--r-- | drivers/hid/i2c-hid/i2c-hid.c | 2 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ipc/pci-ish.c | 22 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ishtp-hid-client.c | 4 | ||||
-rw-r--r-- | drivers/hid/usbhid/Kconfig | 2 | ||||
-rw-r--r-- | drivers/hid/usbhid/hiddev.c | 11 | ||||
-rw-r--r-- | drivers/hid/wacom_sys.c | 30 | ||||
-rw-r--r-- | drivers/hid/wacom_wac.c | 10 |
19 files changed, 609 insertions, 107 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index a49a10437c40..61e1953ff921 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -207,6 +207,16 @@ config HID_CORSAIR - Vengeance K90 - Scimitar PRO RGB +config HID_COUGAR + tristate "Cougar devices" + depends on HID + help + Support for Cougar devices that are not fully compliant with the + HID standard. + + Supported devices: + - Cougar 500k Gaming Keyboard + config HID_PRODIKEYS tristate "Prodikeys PC-MIDI Keyboard support" depends on HID && SND diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 511e1cbff768..bd7ac53b75c5 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_HID_CHERRY) += hid-cherry.o obj-$(CONFIG_HID_CHICONY) += hid-chicony.o obj-$(CONFIG_HID_CMEDIA) += hid-cmedia.o obj-$(CONFIG_HID_CORSAIR) += hid-corsair.o +obj-$(CONFIG_HID_COUGAR) += hid-cougar.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-core.c b/drivers/hid/hid-core.c index 355dc7e49562..402ad974b31c 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -134,8 +134,11 @@ static int open_collection(struct hid_parser *parser, unsigned type) } if (parser->device->maxcollection == parser->device->collection_size) { - collection = kmalloc(sizeof(struct hid_collection) * - parser->device->collection_size * 2, GFP_KERNEL); + collection = kmalloc( + array3_size(sizeof(struct hid_collection), + parser->device->collection_size, + 2), + GFP_KERNEL); if (collection == NULL) { hid_err(parser->device, "failed to reallocate collection array\n"); return -ENOMEM; @@ -1278,7 +1281,7 @@ static void hid_input_field(struct hid_device *hid, struct hid_field *field, __s32 max = field->logical_maximum; __s32 *value; - value = kmalloc(sizeof(__s32) * count, GFP_ATOMIC); + value = kmalloc_array(count, sizeof(__s32), GFP_ATOMIC); if (!value) return; @@ -1936,6 +1939,29 @@ static int hid_bus_match(struct device *dev, struct device_driver *drv) return hid_match_device(hdev, hdrv) != NULL; } +/** + * hid_compare_device_paths - check if both devices share the same path + * @hdev_a: hid device + * @hdev_b: hid device + * @separator: char to use as separator + * + * Check if two devices share the same path up to the last occurrence of + * the separator char. Both paths must exist (i.e., zero-length paths + * don't match). + */ +bool hid_compare_device_paths(struct hid_device *hdev_a, + struct hid_device *hdev_b, char separator) +{ + int n1 = strrchr(hdev_a->phys, separator) - hdev_a->phys; + int n2 = strrchr(hdev_b->phys, separator) - hdev_b->phys; + + if (n1 != n2 || n1 <= 0 || n2 <= 0) + return false; + + return !strncmp(hdev_a->phys, hdev_b->phys, n1); +} +EXPORT_SYMBOL_GPL(hid_compare_device_paths); + static int hid_device_probe(struct device *dev) { struct hid_driver *hdrv = to_hid_driver(dev->driver); @@ -1949,6 +1975,8 @@ static int hid_device_probe(struct device *dev) } hdev->io_started = false; + clear_bit(ffs(HID_STAT_REPROBED), &hdev->status); + if (!hdev->driver) { id = hid_match_device(hdev, hdrv); if (id == NULL) { @@ -2212,7 +2240,8 @@ static int __hid_bus_reprobe_drivers(struct device *dev, void *data) struct hid_device *hdev = to_hid_device(dev); if (hdev->driver == hdrv && - !hdrv->match(hdev, hid_ignore_special_drivers)) + !hdrv->match(hdev, hid_ignore_special_drivers) && + !test_and_set_bit(ffs(HID_STAT_REPROBED), &hdev->status)) return device_reprobe(dev); return 0; diff --git a/drivers/hid/hid-cougar.c b/drivers/hid/hid-cougar.c new file mode 100644 index 000000000000..ad2e87de7dc5 --- /dev/null +++ b/drivers/hid/hid-cougar.c @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * HID driver for Cougar 500k Gaming Keyboard + * + * Copyright (c) 2018 Daniel M. Lambea <dmlambea@gmail.com> + */ + +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +MODULE_AUTHOR("Daniel M. Lambea <dmlambea@gmail.com>"); +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); +MODULE_PARM_DESC(g6_is_space, + "If set, G6 programmable key sends SPACE instead of F18 (0=off, 1=on) (default=1)"); + + +#define COUGAR_VENDOR_USAGE 0xff00ff00 + +#define COUGAR_FIELD_CODE 1 +#define COUGAR_FIELD_ACTION 2 + +#define COUGAR_KEY_G1 0x83 +#define COUGAR_KEY_G2 0x84 +#define COUGAR_KEY_G3 0x85 +#define COUGAR_KEY_G4 0x86 +#define COUGAR_KEY_G5 0x87 +#define COUGAR_KEY_G6 0x78 +#define COUGAR_KEY_FN 0x0d +#define COUGAR_KEY_MR 0x6f +#define COUGAR_KEY_M1 0x80 +#define COUGAR_KEY_M2 0x81 +#define COUGAR_KEY_M3 0x82 +#define COUGAR_KEY_LEDS 0x67 +#define COUGAR_KEY_LOCK 0x6e + + +/* Default key mappings. The special key COUGAR_KEY_G6 is defined first + * because it is more frequent to use the spacebar rather than any other + * special keys. Depending on the value of the parameter 'g6_is_space', + * the mapping will be updated in the probe function. + */ +static unsigned char cougar_mapping[][2] = { + { COUGAR_KEY_G6, KEY_SPACE }, + { COUGAR_KEY_G1, KEY_F13 }, + { COUGAR_KEY_G2, KEY_F14 }, + { COUGAR_KEY_G3, KEY_F15 }, + { COUGAR_KEY_G4, KEY_F16 }, + { COUGAR_KEY_G5, KEY_F17 }, + { COUGAR_KEY_LOCK, KEY_SCREENLOCK }, +/* The following keys are handled by the hardware itself, so no special + * treatment is required: + { COUGAR_KEY_FN, KEY_RESERVED }, + { COUGAR_KEY_MR, KEY_RESERVED }, + { COUGAR_KEY_M1, KEY_RESERVED }, + { COUGAR_KEY_M2, KEY_RESERVED }, + { COUGAR_KEY_M3, KEY_RESERVED }, + { COUGAR_KEY_LEDS, KEY_RESERVED }, +*/ + { 0, 0 }, +}; + +struct cougar_shared { + struct list_head list; + struct kref kref; + bool enabled; + struct hid_device *dev; + struct input_dev *input; +}; + +struct cougar { + bool special_intf; + struct cougar_shared *shared; +}; + +static LIST_HEAD(cougar_udev_list); +static DEFINE_MUTEX(cougar_udev_list_lock); + +static void cougar_fix_g6_mapping(struct hid_device *hdev) +{ + 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"); + return; + } + } + hid_warn(hdev, "no mapping defined for G6/spacebar"); +} + +/* + * Constant-friendly rdesc fixup for mouse interface + */ +static __u8 *cougar_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + if (rdesc[2] == 0x09 && rdesc[3] == 0x02 && + (rdesc[115] | rdesc[116] << 8) >= HID_MAX_USAGES) { + hid_info(hdev, + "usage count exceeds max: fixing up report descriptor\n"); + rdesc[115] = ((HID_MAX_USAGES-1) & 0xff); + rdesc[116] = ((HID_MAX_USAGES-1) >> 8); + } + return rdesc; +} + +static struct cougar_shared *cougar_get_shared_data(struct hid_device *hdev) +{ + struct cougar_shared *shared; + + /* Try to find an already-probed interface from the same device */ + list_for_each_entry(shared, &cougar_udev_list, list) { + if (hid_compare_device_paths(hdev, shared->dev, '/')) { + kref_get(&shared->kref); + return shared; + } + } + return NULL; +} + +static void cougar_release_shared_data(struct kref *kref) +{ + struct cougar_shared *shared = container_of(kref, + struct cougar_shared, kref); + + mutex_lock(&cougar_udev_list_lock); + list_del(&shared->list); + mutex_unlock(&cougar_udev_list_lock); + + kfree(shared); +} + +static void cougar_remove_shared_data(void *resource) +{ + struct cougar *cougar = resource; + + if (cougar->shared) { + kref_put(&cougar->shared->kref, cougar_release_shared_data); + cougar->shared = NULL; + } +} + +/* + * 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) +{ + struct cougar_shared *shared; + int error = 0; + + mutex_lock(&cougar_udev_list_lock); + + shared = cougar_get_shared_data(hdev); + if (!shared) { + shared = kzalloc(sizeof(*shared), GFP_KERNEL); + if (!shared) { + error = -ENOMEM; + goto out; + } + + kref_init(&shared->kref); + shared->dev = hdev; + list_add_tail(&shared->list, &cougar_udev_list); + } + + cougar->shared = shared; + + error = devm_add_action(&hdev->dev, cougar_remove_shared_data, cougar); + if (error) { + mutex_unlock(&cougar_udev_list_lock); + cougar_remove_shared_data(cougar); + return error; + } + +out: + mutex_unlock(&cougar_udev_list_lock); + return error; +} + +static int cougar_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + struct cougar *cougar; + struct hid_input *next, *hidinput = NULL; + unsigned int connect_mask; + int error; + + cougar = devm_kzalloc(&hdev->dev, sizeof(*cougar), GFP_KERNEL); + if (!cougar) + return -ENOMEM; + hid_set_drvdata(hdev, cougar); + + error = hid_parse(hdev); + if (error) { + hid_err(hdev, "parse failed\n"); + goto fail; + } + + if (hdev->collection->usage == COUGAR_VENDOR_USAGE) { + cougar->special_intf = true; + connect_mask = HID_CONNECT_HIDRAW; + } else + connect_mask = HID_CONNECT_DEFAULT; + + error = hid_hw_start(hdev, connect_mask); + if (error) { + hid_err(hdev, "hw start failed\n"); + goto fail; + } + + error = cougar_bind_shared_data(hdev, cougar); + if (error) + goto fail_stop_and_cleanup; + + /* The custom vendor interface will use the hid_input registered + * for the keyboard interface, in order to send translated key codes + * 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; + cougar->shared->enabled = true; + break; + } + } + } else if (hdev->collection->usage == COUGAR_VENDOR_USAGE) { + error = hid_hw_open(hdev); + if (error) + goto fail_stop_and_cleanup; + } + return 0; + +fail_stop_and_cleanup: + hid_hw_stop(hdev); +fail: + hid_set_drvdata(hdev, NULL); + return error; +} + +/* + * Convert events from vendor intf to input key events + */ +static int cougar_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *data, int size) +{ + struct cougar *cougar; + unsigned char code, action; + int i; + + cougar = hid_get_drvdata(hdev); + if (!cougar->special_intf || !cougar->shared || + !cougar->shared->input || !cougar->shared->enabled) + return 0; + + 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, + cougar_mapping[i][1], action); + input_sync(cougar->shared->input); + return 0; + } + } + hid_warn(hdev, "unmapped special key code %x: ignoring\n", code); + return 0; +} + +static void cougar_remove(struct hid_device *hdev) +{ + struct cougar *cougar = hid_get_drvdata(hdev); + + if (cougar) { + /* Stop the vendor intf to process more events */ + if (cougar->shared) + cougar->shared->enabled = false; + if (cougar->special_intf) + hid_hw_close(hdev); + } + hid_hw_stop(hdev); +} + +static struct hid_device_id cougar_id_table[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_SOLID_YEAR, + USB_DEVICE_ID_COUGAR_500K_GAMING_KEYBOARD) }, + {} +}; +MODULE_DEVICE_TABLE(hid, cougar_id_table); + +static struct hid_driver cougar_driver = { + .name = "cougar", + .id_table = cougar_id_table, + .report_fixup = cougar_report_fixup, + .probe = cougar_probe, + .remove = cougar_remove, + .raw_event = cougar_raw_event, +}; + +module_hid_driver(cougar_driver); diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 4f4e7a08a07b..b48100236df8 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -457,7 +457,7 @@ static char *resolv_usage_page(unsigned page, struct seq_file *f) { char *buf = NULL; if (!f) { - buf = kzalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_ATOMIC); + buf = kzalloc(HID_DEBUG_BUFSIZE, GFP_ATOMIC); if (!buf) return ERR_PTR(-ENOMEM); } @@ -685,7 +685,7 @@ void hid_dump_report(struct hid_device *hid, int type, u8 *data, char *buf; unsigned int i; - buf = kmalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_ATOMIC); + buf = kmalloc(HID_DEBUG_BUFSIZE, GFP_ATOMIC); if (!buf) return; @@ -1088,7 +1088,7 @@ static int hid_debug_events_open(struct inode *inode, struct file *file) goto out; } - if (!(list->hid_debug_buf = kzalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_KERNEL))) { + if (!(list->hid_debug_buf = kzalloc(HID_DEBUG_BUFSIZE, GFP_KERNEL))) { err = -ENOMEM; kfree(list); goto out; @@ -1154,6 +1154,8 @@ copy_rest: goto out; if (list->tail > list->head) { len = list->tail - list->head; + if (len > count) + len = count; if (copy_to_user(buffer + ret, &list->hid_debug_buf[list->head], len)) { ret = -EFAULT; @@ -1163,6 +1165,8 @@ copy_rest: list->head += len; } else { len = HID_DEBUG_BUFSIZE - list->head; + if (len > count) + len = count; if (copy_to_user(buffer, &list->hid_debug_buf[list->head], len)) { ret = -EFAULT; @@ -1170,7 +1174,9 @@ copy_rest: } list->head = 0; ret += len; - goto copy_rest; + count -= len; + if (count > 0) + goto copy_rest; } } diff --git a/drivers/hid/hid-elan.c b/drivers/hid/hid-elan.c index 803a725785cf..07e26c3567eb 100644 --- a/drivers/hid/hid-elan.c +++ b/drivers/hid/hid-elan.c @@ -19,38 +19,49 @@ #include "hid-ids.h" +#define ELAN_MT_I2C 0x5d #define ELAN_SINGLE_FINGER 0x81 #define ELAN_MT_FIRST_FINGER 0x82 #define ELAN_MT_SECOND_FINGER 0x83 #define ELAN_INPUT_REPORT_SIZE 8 +#define ELAN_I2C_REPORT_SIZE 32 +#define ELAN_FINGER_DATA_LEN 5 +#define ELAN_MAX_FINGERS 5 +#define ELAN_MAX_PRESSURE 255 +#define ELAN_TP_USB_INTF 1 + +#define ELAN_FEATURE_REPORT 0x0d +#define ELAN_FEATURE_SIZE 5 +#define ELAN_PARAM_MAX_X 6 +#define ELAN_PARAM_MAX_Y 7 +#define ELAN_PARAM_RES 8 #define ELAN_MUTE_LED_REPORT 0xBC #define ELAN_LED_REPORT_SIZE 8 -struct elan_touchpad_settings { - u8 max_fingers; - u16 max_x; - u16 max_y; - u8 max_area_x; - u8 max_area_y; - u8 max_w; - int usb_bInterfaceNumber; -}; +#define ELAN_HAS_LED BIT(0) struct elan_drvdata { struct input_dev *input; u8 prev_report[ELAN_INPUT_REPORT_SIZE]; struct led_classdev mute_led; u8 mute_led_state; - struct elan_touchpad_settings *settings; + u16 max_x; + u16 max_y; + u16 res_x; + u16 res_y; }; static int is_not_elan_touchpad(struct hid_device *hdev) { - struct usb_interface *intf = to_usb_interface(hdev->dev.parent); - struct elan_drvdata *drvdata = hid_get_drvdata(hdev); + if (hdev->bus == BUS_USB) { + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + + return (intf->altsetting->desc.bInterfaceNumber != + ELAN_TP_USB_INTF); + } - return (intf->altsetting->desc.bInterfaceNumber != drvdata->settings->usb_bInterfaceNumber); + return 0; } static int elan_input_mapping(struct hid_device *hdev, struct hid_input *hi, @@ -62,12 +73,86 @@ static int elan_input_mapping(struct hid_device *hdev, struct hid_input *hi, if (field->report->id == ELAN_SINGLE_FINGER || field->report->id == ELAN_MT_FIRST_FINGER || - field->report->id == ELAN_MT_SECOND_FINGER) + field->report->id == ELAN_MT_SECOND_FINGER || + field->report->id == ELAN_MT_I2C) return -1; return 0; } +static int elan_get_device_param(struct hid_device *hdev, + unsigned char *dmabuf, unsigned char param) +{ + int ret; + + dmabuf[0] = ELAN_FEATURE_REPORT; + dmabuf[1] = 0x05; + dmabuf[2] = 0x03; + dmabuf[3] = param; + dmabuf[4] = 0x01; + + ret = hid_hw_raw_request(hdev, ELAN_FEATURE_REPORT, dmabuf, + ELAN_FEATURE_SIZE, HID_FEATURE_REPORT, + HID_REQ_SET_REPORT); + if (ret != ELAN_FEATURE_SIZE) { + hid_err(hdev, "Set report error for parm %d: %d\n", param, ret); + return ret; + } + + ret = hid_hw_raw_request(hdev, ELAN_FEATURE_REPORT, dmabuf, + ELAN_FEATURE_SIZE, HID_FEATURE_REPORT, + HID_REQ_GET_REPORT); + if (ret != ELAN_FEATURE_SIZE) { + hid_err(hdev, "Get report error for parm %d: %d\n", param, ret); + return ret; + } + + return 0; +} + +static unsigned int elan_convert_res(char val) +{ + /* + * (value from firmware) * 10 + 790 = dpi + * dpi * 10 / 254 = dots/mm + */ + return (val * 10 + 790) * 10 / 254; +} + +static int elan_get_device_params(struct hid_device *hdev) +{ + struct elan_drvdata *drvdata = hid_get_drvdata(hdev); + unsigned char *dmabuf; + int ret; + + dmabuf = kmalloc(ELAN_FEATURE_SIZE, GFP_KERNEL); + if (!dmabuf) + return -ENOMEM; + + ret = elan_get_device_param(hdev, dmabuf, ELAN_PARAM_MAX_X); + if (ret) + goto err; + + drvdata->max_x = (dmabuf[4] << 8) | dmabuf[3]; + + ret = elan_get_device_param(hdev, dmabuf, ELAN_PARAM_MAX_Y); + if (ret) + goto err; + + drvdata->max_y = (dmabuf[4] << 8) | dmabuf[3]; + + ret = elan_get_device_param(hdev, dmabuf, ELAN_PARAM_RES); + if (ret) + goto err; + + drvdata->res_x = elan_convert_res(dmabuf[3]); + drvdata->res_y = elan_convert_res(dmabuf[4]); + +err: + kfree(dmabuf); + return ret; +} + static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi) { int ret; @@ -77,6 +162,10 @@ static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi) if (is_not_elan_touchpad(hdev)) return 0; + ret = elan_get_device_params(hdev); + if (ret) + return ret; + input = devm_input_allocate_device(&hdev->dev); if (!input) return -ENOMEM; @@ -90,25 +179,25 @@ static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi) input->id.version = hdev->version; input->dev.parent = &hdev->dev; - input_set_abs_params(input, ABS_MT_POSITION_X, 0, - drvdata->settings->max_x, 0, 0); - input_set_abs_params(input, ABS_MT_POSITION_Y, 0, - drvdata->settings->max_y, 0, 0); - input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, - drvdata->settings->max_fingers, 0, 0); - input_set_abs_params(input, ABS_TOOL_WIDTH, 0, - drvdata->settings->max_w, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_X, 0, drvdata->max_x, + 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, drvdata->max_y, + 0, 0); + input_set_abs_params(input, ABS_MT_PRESSURE, 0, ELAN_MAX_PRESSURE, + 0, 0); __set_bit(BTN_LEFT, input->keybit); __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); - ret = input_mt_init_slots(input, drvdata->settings->max_fingers, - INPUT_MT_POINTER); + ret = input_mt_init_slots(input, ELAN_MAX_FINGERS, INPUT_MT_POINTER); if (ret) { hid_err(hdev, "Failed to init elan MT slots: %d\n", ret); return ret; } + input_abs_set_res(input, ABS_X, drvdata->res_x); + input_abs_set_res(input, ABS_Y, drvdata->res_y); + ret = input_register_device(input); if (ret) { hid_err(hdev, "Failed to register elan input device: %d\n", @@ -126,7 +215,7 @@ static void elan_report_mt_slot(struct elan_drvdata *drvdata, u8 *data, unsigned int slot_num) { struct input_dev *input = drvdata->input; - int x, y, w; + int x, y, p; bool active = !!data; @@ -134,17 +223,17 @@ static void elan_report_mt_slot(struct elan_drvdata *drvdata, u8 *data, input_mt_report_slot_state(input, MT_TOOL_FINGER, active); if (active) { x = ((data[0] & 0xF0) << 4) | data[1]; - y = drvdata->settings->max_y - + y = drvdata->max_y - (((data[0] & 0x07) << 8) | data[2]); - w = data[4]; + p = data[4]; input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); - input_report_abs(input, ABS_TOOL_WIDTH, w); + input_report_abs(input, ABS_MT_PRESSURE, p); } } -static void elan_report_input(struct elan_drvdata *drvdata, u8 *data) +static void elan_usb_report_input(struct elan_drvdata *drvdata, u8 *data) { int i; struct input_dev *input = drvdata->input; @@ -162,7 +251,7 @@ static void elan_report_input(struct elan_drvdata *drvdata, u8 *data) * byte 5: x8 x7 x6 x5 x4 x3 x2 x1 * byte 6: y8 y7 y6 y5 y4 y3 y2 y1 * byte 7: sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1 - * byte 8: w8 w7 w6 w5 w4 w3 w2 w1 + * byte 8: p8 p7 p6 p5 p4 p3 p2 p1 * * packet structure for ELAN_MT_SECOND_FINGER: * @@ -171,19 +260,21 @@ static void elan_report_input(struct elan_drvdata *drvdata, u8 *data) * byte 3: x8 x7 x6 x5 x4 x3 x2 x1 * byte 4: y8 y7 y6 y5 y4 y3 y2 y1 * byte 5: sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1 - * byte 6: w8 w7 w6 w5 w4 w3 w2 w1 + * byte 6: p8 p7 p6 p5 p4 p3 p2 p1 * byte 7: 0 0 0 0 0 0 0 0 * byte 8: 0 0 0 0 0 0 0 0 * * f5-f1: finger touch bits * L: clickpad button - * sy / sx: not sure yet, but this looks like rectangular - * area for finger - * w: looks like finger width + * sy / sx: finger width / height expressed in traces, the total number + * of traces can be queried by doing a HID_REQ_SET_REPORT + * { 0x0d, 0x05, 0x03, 0x05, 0x01 } followed by a GET, in the + * returned buf, buf[3]=no-x-traces, buf[4]=no-y-traces. + * p: pressure */ if (data[0] == ELAN_SINGLE_FINGER) { - for (i = 0; i < drvdata->settings->max_fingers; i++) { + for (i = 0; i < ELAN_MAX_FINGERS; i++) { if (data[2] & BIT(i + 3)) elan_report_mt_slot(drvdata, data + 3, i); else @@ -210,7 +301,7 @@ static void elan_report_input(struct elan_drvdata *drvdata, u8 *data) if (prev_report[0] != ELAN_MT_FIRST_FINGER) return; - for (i = 0; i < drvdata->settings->max_fingers; i++) { + for (i = 0; i < ELAN_MAX_FINGERS; i++) { if (prev_report[2] & BIT(i + 3)) { if (!first) { first = 1; @@ -229,6 +320,46 @@ static void elan_report_input(struct elan_drvdata *drvdata, u8 *data) input_sync(input); } +static void elan_i2c_report_input(struct elan_drvdata *drvdata, u8 *data) +{ + struct input_dev *input = drvdata->input; + u8 *finger_data; + int i; + + /* + * Elan MT touchpads in i2c mode send finger data in the same format + * as in USB mode, but then with all fingers in a single packet. + * + * packet structure for ELAN_MT_I2C: + * + * byte 1: 1 0 0 1 1 1 0 1 // 0x5d + * byte 2: f5 f4 f3 f2 f1 0 0 L + * byte 3: x12 x11 x10 x9 0? y11 y10 y9 + * byte 4: x8 x7 x6 x5 x4 x3 x2 x1 + * byte 5: y8 y7 y6 y5 y4 y3 y2 y1 + * byte 6: sy4 sy3 sy2 sy1 sx4 sx3 sx2 sx1 + * byte 7: p8 p7 p6 p5 p4 p3 p2 p1 + * byte 8-12: Same as byte 3-7 for second finger down + * byte 13-17: Same as byte 3-7 for third finger down + * byte 18-22: Same as byte 3-7 for fourth finger down + * byte 23-27: Same as byte 3-7 for fifth finger down + */ + + finger_data = data + 2; + for (i = 0; i < ELAN_MAX_FINGERS; i++) { + if (data[1] & BIT(i + 3)) { + elan_report_mt_slot(drvdata, finger_data, i); + finger_data += ELAN_FINGER_DATA_LEN; + } else { + elan_report_mt_slot(drvdata, NULL, i); + } + } + + input_report_key(input, BTN_LEFT, data[1] & 0x01); + input_mt_sync_frame(input); + input_sync(input); +} + static int elan_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) { @@ -241,11 +372,16 @@ static int elan_raw_event(struct hid_device *hdev, data[0] == ELAN_MT_FIRST_FINGER || data[0] == ELAN_MT_SECOND_FINGER) { if (size == ELAN_INPUT_REPORT_SIZE) { - elan_report_input(drvdata, data); + elan_usb_report_input(drvdata, data); return 1; } } + if (data[0] == ELAN_MT_I2C && size == ELAN_I2C_REPORT_SIZE) { + elan_i2c_report_input(drvdata, data); + return 1; + } + return 0; } @@ -343,7 +479,6 @@ static int elan_probe(struct hid_device *hdev, const struct hid_device_id *id) if (!drvdata) return -ENOMEM; - drvdata->settings = (struct elan_touchpad_settings *)id->driver_data; hid_set_drvdata(hdev, drvdata); ret = hid_parse(hdev); @@ -371,9 +506,11 @@ static int elan_probe(struct hid_device *hdev, const struct hid_device_id *id) if (ret) goto err; - ret = elan_init_mute_led(hdev); - if (ret) - goto err; + if (id->driver_data & ELAN_HAS_LED) { + ret = elan_init_mute_led(hdev); + if (ret) + goto err; + } return 0; err: @@ -386,22 +523,14 @@ static void elan_remove(struct hid_device *hdev) hid_hw_stop(hdev); } -static const struct elan_touchpad_settings hp_x2_10_touchpad_data = { - .max_fingers = 5, - .max_x = 2930, - .max_y = 1250, - .max_area_x = 15, - .max_area_y = 15, - .max_w = 255, - .usb_bInterfaceNumber = 1, -}; - static const struct hid_device_id elan_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_HP_X2), + .driver_data = ELAN_HAS_LED }, { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_HP_X2_10_COVER), - (kernel_ulong_t)&hp_x2_10_touchpad_data}, + .driver_data = ELAN_HAS_LED }, + { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_TOSHIBA_CLICK_L9W) }, { } }; - MODULE_DEVICE_TABLE(hid, elan_devices); static struct hid_driver elan_driver = { diff --git a/drivers/hid/hid-google-hammer.c b/drivers/hid/hid-google-hammer.c index 7b8e17b03cb8..6bf4da7ad63a 100644 --- a/drivers/hid/hid-google-hammer.c +++ b/drivers/hid/hid-google-hammer.c @@ -124,6 +124,8 @@ static const struct hid_device_id hammer_devices[] = { USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_STAFF) }, { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_WAND) }, + { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, + USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_WHISKERS) }, { } }; MODULE_DEVICE_TABLE(hid, hammer_devices); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index a85634fe033f..79bdf0c7e351 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -369,6 +369,8 @@ #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001 #define USB_VENDOR_ID_ELAN 0x04f3 +#define USB_DEVICE_ID_TOSHIBA_CLICK_L9W 0x0401 +#define USB_DEVICE_ID_HP_X2 0x074d #define USB_DEVICE_ID_HP_X2_10_COVER 0x0755 #define USB_VENDOR_ID_ELECOM 0x056e @@ -452,6 +454,7 @@ #define USB_DEVICE_ID_GOOGLE_TOUCH_ROSE 0x5028 #define USB_DEVICE_ID_GOOGLE_STAFF 0x502b #define USB_DEVICE_ID_GOOGLE_WAND 0x502d +#define USB_DEVICE_ID_GOOGLE_WHISKERS 0x5030 #define USB_VENDOR_ID_GOTOP 0x08f2 #define USB_DEVICE_ID_SUPER_Q2 0x007f @@ -1000,6 +1003,9 @@ #define USB_VENDOR_ID_SINO_LITE 0x1345 #define USB_DEVICE_ID_SINO_LITE_CONTROLLER 0x3008 +#define USB_VENDOR_ID_SOLID_YEAR 0x060b +#define USB_DEVICE_ID_COUGAR_500K_GAMING_KEYBOARD 0x500a + #define USB_VENDOR_ID_SOUNDGRAPH 0x15c2 #define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST 0x0034 #define USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST 0x0046 diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c index 7f965e231433..864a084b6cba 100644 --- a/drivers/hid/hid-picolcd_fb.c +++ b/drivers/hid/hid-picolcd_fb.c @@ -394,7 +394,8 @@ static int picolcd_set_par(struct fb_info *info) return -EINVAL; o_fb = fbdata->bitmap; - tmp_fb = kmalloc(PICOLCDFB_SIZE*info->var.bits_per_pixel, GFP_KERNEL); + tmp_fb = kmalloc_array(PICOLCDFB_SIZE, info->var.bits_per_pixel, + GFP_KERNEL); if (!tmp_fb) return -ENOMEM; diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index 25363fc571bc..50af72baa5ca 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -624,7 +624,8 @@ static int sensor_hub_probe(struct hid_device *hdev, ret = -EINVAL; goto err_stop_hw; } - sd->hid_sensor_hub_client_devs = devm_kzalloc(&hdev->dev, dev_cnt * + sd->hid_sensor_hub_client_devs = devm_kcalloc(&hdev->dev, + dev_cnt, sizeof(struct mfd_cell), GFP_KERNEL); if (sd->hid_sensor_hub_client_devs == NULL) { diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c index cb86cc834201..0422ec2b13d2 100644 --- a/drivers/hid/hid-steam.c +++ b/drivers/hid/hid-steam.c @@ -573,7 +573,7 @@ static bool steam_is_valve_interface(struct hid_device *hdev) static int steam_client_ll_parse(struct hid_device *hdev) { - struct steam_device *steam = hid_get_drvdata(hdev); + struct steam_device *steam = hdev->driver_data; return hid_parse_report(hdev, steam->hdev->dev_rdesc, steam->hdev->dev_rsize); @@ -590,7 +590,7 @@ static void steam_client_ll_stop(struct hid_device *hdev) static int steam_client_ll_open(struct hid_device *hdev) { - struct steam_device *steam = hid_get_drvdata(hdev); + struct steam_device *steam = hdev->driver_data; int ret; ret = hid_hw_open(steam->hdev); @@ -605,7 +605,7 @@ static int steam_client_ll_open(struct hid_device *hdev) static void steam_client_ll_close(struct hid_device *hdev) { - struct steam_device *steam = hid_get_drvdata(hdev); + struct steam_device *steam = hdev->driver_data; mutex_lock(&steam->mutex); steam->client_opened = false; @@ -623,7 +623,7 @@ static int steam_client_ll_raw_request(struct hid_device *hdev, size_t count, unsigned char report_type, int reqtype) { - struct steam_device *steam = hid_get_drvdata(hdev); + struct steam_device *steam = hdev->driver_data; return hid_hw_raw_request(steam->hdev, reportnum, buf, count, report_type, reqtype); @@ -710,7 +710,7 @@ static int steam_probe(struct hid_device *hdev, ret = PTR_ERR(steam->client_hdev); goto client_hdev_fail; } - hid_set_drvdata(steam->client_hdev, steam); + steam->client_hdev->driver_data = steam; /* * With the real steam controller interface, do not connect hidraw. diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index b39844adea47..4a44e48e08b2 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -218,7 +218,7 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t goto out; } - buf = kmalloc(count * sizeof(__u8), GFP_KERNEL); + buf = kmalloc(count, GFP_KERNEL); if (!buf) { ret = -ENOMEM; goto out; diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 4f532d9238fb..2ce194a84868 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -484,7 +484,7 @@ static void i2c_hid_get_input(struct i2c_hid *ihid) return; } - if ((ret_size > size) || (ret_size <= 2)) { + if ((ret_size > size) || (ret_size < 2)) { dev_err(&ihid->client->dev, "%s: incomplete report (%d/%d)\n", __func__, size, ret_size); return; diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c b/drivers/hid/intel-ish-hid/ipc/pci-ish.c index 582e449be9fe..a2c53ea3b5ed 100644 --- a/drivers/hid/intel-ish-hid/ipc/pci-ish.c +++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c @@ -205,8 +205,7 @@ static void ish_remove(struct pci_dev *pdev) kfree(ishtp_dev); } -#ifdef CONFIG_PM -static struct device *ish_resume_device; +static struct device __maybe_unused *ish_resume_device; /* 50ms to get resume response */ #define WAIT_FOR_RESUME_ACK_MS 50 @@ -220,7 +219,7 @@ static struct device *ish_resume_device; * in that case a simple resume message is enough, others we need * a reset sequence. */ -static void ish_resume_handler(struct work_struct *work) +static void __maybe_unused ish_resume_handler(struct work_struct *work) { struct pci_dev *pdev = to_pci_dev(ish_resume_device); struct ishtp_device *dev = pci_get_drvdata(pdev); @@ -262,7 +261,7 @@ static void ish_resume_handler(struct work_struct *work) * * Return: 0 to the pm core */ -static int ish_suspend(struct device *device) +static int __maybe_unused ish_suspend(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); struct ishtp_device *dev = pci_get_drvdata(pdev); @@ -288,7 +287,7 @@ static int ish_suspend(struct device *device) return 0; } -static DECLARE_WORK(resume_work, ish_resume_handler); +static __maybe_unused DECLARE_WORK(resume_work, ish_resume_handler); /** * ish_resume() - ISH resume callback * @device: device pointer @@ -297,7 +296,7 @@ static DECLARE_WORK(resume_work, ish_resume_handler); * * Return: 0 to the pm core */ -static int ish_resume(struct device *device) +static int __maybe_unused ish_resume(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); struct ishtp_device *dev = pci_get_drvdata(pdev); @@ -311,21 +310,14 @@ static int ish_resume(struct device *device) return 0; } -static const struct dev_pm_ops ish_pm_ops = { - .suspend = ish_suspend, - .resume = ish_resume, -}; -#define ISHTP_ISH_PM_OPS (&ish_pm_ops) -#else -#define ISHTP_ISH_PM_OPS NULL -#endif /* CONFIG_PM */ +static SIMPLE_DEV_PM_OPS(ish_pm_ops, ish_suspend, ish_resume); static struct pci_driver ish_driver = { .name = KBUILD_MODNAME, .id_table = ish_pci_tbl, .probe = ish_probe, .remove = ish_remove, - .driver.pm = ISHTP_ISH_PM_OPS, + .driver.pm = &ish_pm_ops, }; module_pci_driver(ish_driver); diff --git a/drivers/hid/intel-ish-hid/ishtp-hid-client.c b/drivers/hid/intel-ish-hid/ishtp-hid-client.c index acc2536c8094..2d28cffc1404 100644 --- a/drivers/hid/intel-ish-hid/ishtp-hid-client.c +++ b/drivers/hid/intel-ish-hid/ishtp-hid-client.c @@ -121,9 +121,9 @@ static void process_recv(struct ishtp_cl *hid_ishtp_cl, void *recv_buf, } client_data->hid_dev_count = (unsigned int)*payload; if (!client_data->hid_devices) - client_data->hid_devices = devm_kzalloc( + client_data->hid_devices = devm_kcalloc( &client_data->cl_device->dev, - client_data->hid_dev_count * + client_data->hid_dev_count, sizeof(struct device_info), GFP_KERNEL); if (!client_data->hid_devices) { diff --git a/drivers/hid/usbhid/Kconfig b/drivers/hid/usbhid/Kconfig index 0108c5991a04..e50d8fe4d36c 100644 --- a/drivers/hid/usbhid/Kconfig +++ b/drivers/hid/usbhid/Kconfig @@ -14,7 +14,7 @@ config USB_HID You can't use this driver and the HIDBP (Boot Protocol) keyboard and mouse drivers at the same time. More information is available: - <file:Documentation/input/input.txt>. + <file:Documentation/input/input.rst>. If unsure, say Y. diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index e3ce233f8bdc..23872d08308c 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -36,6 +36,7 @@ #include <linux/hiddev.h> #include <linux/compat.h> #include <linux/vmalloc.h> +#include <linux/nospec.h> #include "usbhid.h" #ifdef CONFIG_USB_DYNAMIC_MINORS @@ -469,10 +470,14 @@ static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd, if (uref->field_index >= report->maxfield) goto inval; + uref->field_index = array_index_nospec(uref->field_index, + report->maxfield); field = report->field[uref->field_index]; if (uref->usage_index >= field->maxusage) goto inval; + uref->usage_index = array_index_nospec(uref->usage_index, + field->maxusage); uref->usage_code = field->usage[uref->usage_index].hid; @@ -499,6 +504,8 @@ static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd, if (uref->field_index >= report->maxfield) goto inval; + uref->field_index = array_index_nospec(uref->field_index, + report->maxfield); field = report->field[uref->field_index]; @@ -753,6 +760,8 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (finfo.field_index >= report->maxfield) break; + finfo.field_index = array_index_nospec(finfo.field_index, + report->maxfield); field = report->field[finfo.field_index]; memset(&finfo, 0, sizeof(finfo)); @@ -797,6 +806,8 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (cinfo.index >= hid->maxcollection) break; + cinfo.index = array_index_nospec(cinfo.index, + hid->maxcollection); cinfo.type = hid->collection[cinfo.index].type; cinfo.usage = hid->collection[cinfo.index].usage; diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index ee7a37eb159a..ffe59a19b3a3 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -395,6 +395,14 @@ static void wacom_usage_mapping(struct hid_device *hdev, } } + /* 2nd-generation Intuos Pro Large has incorrect Y maximum */ + if (hdev->vendor == USB_VENDOR_ID_WACOM && + hdev->product == 0x0358 && + WACOM_PEN_FIELD(field) && + wacom_equivalent_usage(usage->hid) == HID_GD_Y) { + field->logical_maximum = 43200; + } + switch (usage->hid) { case HID_GD_X: features->x_max = field->logical_maximum; @@ -695,18 +703,6 @@ struct wacom_hdev_data { static LIST_HEAD(wacom_udev_list); static DEFINE_MUTEX(wacom_udev_list_lock); -static bool compare_device_paths(struct hid_device *hdev_a, - struct hid_device *hdev_b, char separator) -{ - int n1 = strrchr(hdev_a->phys, separator) - hdev_a->phys; - int n2 = strrchr(hdev_b->phys, separator) - hdev_b->phys; - - if (n1 != n2 || n1 <= 0 || n2 <= 0) - return false; - - return !strncmp(hdev_a->phys, hdev_b->phys, n1); -} - static bool wacom_are_sibling(struct hid_device *hdev, struct hid_device *sibling) { @@ -729,10 +725,10 @@ static bool wacom_are_sibling(struct hid_device *hdev, * the same physical parent device path. */ if (hdev->vendor == sibling->vendor && hdev->product == sibling->product) { - if (!compare_device_paths(hdev, sibling, '/')) + if (!hid_compare_device_paths(hdev, sibling, '/')) return false; } else { - if (!compare_device_paths(hdev, sibling, '.')) + if (!hid_compare_device_paths(hdev, sibling, '.')) return false; } @@ -779,7 +775,7 @@ static struct wacom_hdev_data *wacom_get_hdev_data(struct hid_device *hdev) /* Try to find an already-probed interface from the same device */ list_for_each_entry(data, &wacom_udev_list, list) { - if (compare_device_paths(hdev, data->dev, '/')) { + if (hid_compare_device_paths(hdev, data->dev, '/')) { kref_get(&data->kref); return data; } @@ -1363,7 +1359,7 @@ static int wacom_led_groups_alloc_and_register_one(struct device *dev, if (!devres_open_group(dev, &wacom->led.groups[group_id], GFP_KERNEL)) return -ENOMEM; - leds = devm_kzalloc(dev, sizeof(struct wacom_led) * count, GFP_KERNEL); + leds = devm_kcalloc(dev, count, sizeof(struct wacom_led), GFP_KERNEL); if (!leds) { error = -ENOMEM; goto err; @@ -1463,7 +1459,7 @@ static int wacom_led_groups_allocate(struct wacom *wacom, int count) struct wacom_group_leds *groups; int error; - groups = devm_kzalloc(dev, sizeof(struct wacom_group_leds) * count, + groups = devm_kcalloc(dev, count, sizeof(struct wacom_group_leds), GFP_KERNEL); if (!groups) return -ENOMEM; diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 0bb44d0088ed..ad7afa74d365 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -3365,8 +3365,14 @@ void wacom_setup_device_quirks(struct wacom *wacom) if (features->type >= INTUOSHT && features->type <= BAMBOO_PT) features->device_type |= WACOM_DEVICETYPE_PAD; - features->x_max = 4096; - features->y_max = 4096; + if (features->type == INTUOSHT2) { + features->x_max = features->x_max / 10; + features->y_max = features->y_max / 10; + } + else { + features->x_max = 4096; + features->y_max = 4096; + } } else if (features->pktlen == WACOM_PKGLEN_BBTOUCH) { features->device_type |= WACOM_DEVICETYPE_PAD; |