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 | 45 | ||||
-rw-r--r-- | drivers/hid/hid-cougar.c | 312 | ||||
-rw-r--r-- | drivers/hid/hid-debug.c | 8 | ||||
-rw-r--r-- | drivers/hid/hid-elan.c | 235 | ||||
-rw-r--r-- | drivers/hid/hid-ids.h | 5 | ||||
-rw-r--r-- | drivers/hid/hid-input.c | 3 | ||||
-rw-r--r-- | drivers/hid/hid-microsoft.c | 49 | ||||
-rw-r--r-- | drivers/hid/hid-multitouch.c | 989 | ||||
-rw-r--r-- | drivers/hid/hid-ntrig.c | 2 | ||||
-rw-r--r-- | drivers/hid/hid-redragon.c | 26 | ||||
-rw-r--r-- | drivers/hid/hid-sony.c | 164 | ||||
-rw-r--r-- | drivers/hid/i2c-hid/i2c-hid.c | 59 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ipc/ipc.c | 9 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ipc/pci-ish.c | 13 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ishtp/hbm.c | 2 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-core.c | 7 | ||||
-rw-r--r-- | drivers/hid/usbhid/hiddev.c | 11 | ||||
-rw-r--r-- | drivers/hid/wacom_sys.c | 18 | ||||
-rw-r--r-- | drivers/hid/wacom_wac.c | 10 |
21 files changed, 1303 insertions, 675 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 f858cc72011d..3da354af7a0a 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -128,9 +128,19 @@ static int open_collection(struct hid_parser *parser, unsigned type) usage = parser->local.usage[0]; - if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) { - hid_err(parser->device, "collection stack overflow\n"); - return -EINVAL; + if (parser->collection_stack_ptr == parser->collection_stack_size) { + unsigned int *collection_stack; + unsigned int new_size = parser->collection_stack_size + + HID_COLLECTION_STACK_SIZE; + + collection_stack = krealloc(parser->collection_stack, + new_size * sizeof(unsigned int), + GFP_KERNEL); + if (!collection_stack) + return -ENOMEM; + + parser->collection_stack = collection_stack; + parser->collection_stack_size = new_size; } if (parser->device->maxcollection == parser->device->collection_size) { @@ -840,6 +850,7 @@ static int hid_scan_report(struct hid_device *hid) break; } + kfree(parser->collection_stack); vfree(parser); return 0; } @@ -1939,6 +1950,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); @@ -1952,6 +1986,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) { @@ -2215,7 +2251,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 8469b6964ff6..b48100236df8 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -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-ids.h b/drivers/hid/hid-ids.h index c7981ddd8776..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 @@ -1001,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-input.c b/drivers/hid/hid-input.c index ab93dd5927c3..4e94ea3e280a 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1550,6 +1550,9 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid, case HID_GD_WIRELESS_RADIO_CTLS: suffix = "Wireless Radio Control"; break; + case HID_GD_SYSTEM_MULTIAXIS: + suffix = "System Multi Axis"; + break; default: break; } diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index 96e7d3231d2f..72d983626afd 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -22,12 +22,13 @@ #include "hid-ids.h" -#define MS_HIDINPUT 0x01 -#define MS_ERGONOMY 0x02 -#define MS_PRESENTER 0x04 -#define MS_RDESC 0x08 -#define MS_NOGET 0x10 -#define MS_DUPLICATE_USAGES 0x20 +#define MS_HIDINPUT BIT(0) +#define MS_ERGONOMY BIT(1) +#define MS_PRESENTER BIT(2) +#define MS_RDESC BIT(3) +#define MS_NOGET BIT(4) +#define MS_DUPLICATE_USAGES BIT(5) +#define MS_SURFACE_DIAL BIT(6) static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) @@ -130,6 +131,30 @@ static int ms_presenter_8k_quirk(struct hid_input *hi, struct hid_usage *usage, return 1; } +static int ms_surface_dial_quirk(struct hid_input *hi, struct hid_field *field, + struct hid_usage *usage, unsigned long **bit, int *max) +{ + switch (usage->hid & HID_USAGE_PAGE) { + case 0xff070000: + /* fall-through */ + case HID_UP_DIGITIZER: + /* ignore those axis */ + return -1; + case HID_UP_GENDESK: + switch (usage->hid) { + case HID_GD_X: + /* fall-through */ + case HID_GD_Y: + /* fall-through */ + case HID_GD_RFKILL_BTN: + /* ignore those axis */ + return -1; + } + } + + return 0; +} + static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) @@ -146,6 +171,13 @@ static int ms_input_mapping(struct hid_device *hdev, struct hid_input *hi, ms_presenter_8k_quirk(hi, usage, bit, max)) return 1; + if (quirks & MS_SURFACE_DIAL) { + int ret = ms_surface_dial_quirk(hi, field, usage, bit, max); + + if (ret) + return ret; + } + return 0; } |