diff options
-rw-r--r-- | Documentation/devicetree/bindings/input/hid-over-i2c.txt | 3 | ||||
-rw-r--r-- | drivers/hid/Kconfig | 10 | ||||
-rw-r--r-- | drivers/hid/Makefile | 1 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 23 | ||||
-rw-r--r-- | drivers/hid/hid-cougar.c | 312 | ||||
-rw-r--r-- | drivers/hid/hid-elan.c | 235 | ||||
-rw-r--r-- | drivers/hid/hid-ids.h | 5 | ||||
-rw-r--r-- | drivers/hid/i2c-hid/i2c-hid.c | 57 | ||||
-rw-r--r-- | drivers/hid/wacom_sys.c | 18 | ||||
-rw-r--r-- | include/linux/hid.h | 2 | ||||
-rw-r--r-- | include/linux/platform_data/i2c-hid.h | 7 |
11 files changed, 570 insertions, 103 deletions
diff --git a/Documentation/devicetree/bindings/input/hid-over-i2c.txt b/Documentation/devicetree/bindings/input/hid-over-i2c.txt index 4d3da9d91de4..89e6ab89ba38 100644 --- a/Documentation/devicetree/bindings/input/hid-over-i2c.txt +++ b/Documentation/devicetree/bindings/input/hid-over-i2c.txt @@ -26,7 +26,8 @@ device-specific compatible properties, which should be used in addition to the - compatible: * "wacom,w9013" (Wacom W9013 digitizer). Supports: - - vdd-supply + - vdd-supply (3.3V) + - vddl-supply (1.8V) - post-power-on-delay-ms - vdd-supply: phandle of the regulator that provides the supply voltage. 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 3942ee61bd1c..402ad974b31c 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1939,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); 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-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/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index eae0cb3ddec6..2ce194a84868 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -1002,18 +1002,18 @@ static int i2c_hid_probe(struct i2c_client *client, return client->irq; } - ihid = kzalloc(sizeof(struct i2c_hid), GFP_KERNEL); + ihid = devm_kzalloc(&client->dev, sizeof(*ihid), GFP_KERNEL); if (!ihid) return -ENOMEM; if (client->dev.of_node) { ret = i2c_hid_of_probe(client, &ihid->pdata); if (ret) - goto err; + return ret; } else if (!platform_data) { ret = i2c_hid_acpi_pdata(client, &ihid->pdata); if (ret) - goto err; + return ret; } else { ihid->pdata = *platform_data; } @@ -1021,21 +1021,20 @@ static int i2c_hid_probe(struct i2c_client *client, /* Parse platform agnostic common properties from ACPI / device tree */ i2c_hid_fwnode_probe(client, &ihid->pdata); - ihid->pdata.supply = devm_regulator_get(&client->dev, "vdd"); - if (IS_ERR(ihid->pdata.supply)) { - ret = PTR_ERR(ihid->pdata.supply); - if (ret != -EPROBE_DEFER) - dev_err(&client->dev, "Failed to get regulator: %d\n", - ret); - goto err; - } + ihid->pdata.supplies[0].supply = "vdd"; + ihid->pdata.supplies[1].supply = "vddl"; + + ret = devm_regulator_bulk_get(&client->dev, + ARRAY_SIZE(ihid->pdata.supplies), + ihid->pdata.supplies); + if (ret) + return ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ihid->pdata.supplies), + ihid->pdata.supplies); + if (ret < 0) + return ret; - ret = regulator_enable(ihid->pdata.supply); - if (ret < 0) { - dev_err(&client->dev, "Failed to enable regulator: %d\n", - ret); - goto err; - } if (ihid->pdata.post_power_delay_ms) msleep(ihid->pdata.post_power_delay_ms); @@ -1122,11 +1121,9 @@ err_pm: pm_runtime_disable(&client->dev); err_regulator: - regulator_disable(ihid->pdata.supply); - -err: + regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies), + ihid->pdata.supplies); i2c_hid_free_buffers(ihid); - kfree(ihid); return ret; } @@ -1148,9 +1145,8 @@ static int i2c_hid_remove(struct i2c_client *client) if (ihid->bufsize) i2c_hid_free_buffers(ihid); - regulator_disable(ihid->pdata.supply); - - kfree(ihid); + regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies), + ihid->pdata.supplies); return 0; } @@ -1201,9 +1197,8 @@ static int i2c_hid_suspend(struct device *dev) hid_warn(hid, "Failed to enable irq wake: %d\n", wake_status); } else { - ret = regulator_disable(ihid->pdata.supply); - if (ret < 0) - hid_warn(hid, "Failed to disable supply: %d\n", ret); + regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies), + ihid->pdata.supplies); } return 0; @@ -1218,9 +1213,11 @@ static int i2c_hid_resume(struct device *dev) int wake_status; if (!device_may_wakeup(&client->dev)) { - ret = regulator_enable(ihid->pdata.supply); - if (ret < 0) - hid_warn(hid, "Failed to enable supply: %d\n", ret); + ret = regulator_bulk_enable(ARRAY_SIZE(ihid->pdata.supplies), + ihid->pdata.supplies); + if (ret) + hid_warn(hid, "Failed to enable supplies: %d\n", ret); + if (ihid->pdata.post_power_delay_ms) msleep(ihid->pdata.post_power_delay_ms); } else if (ihid->irq_wake_enabled) { diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index d6797535fff9..ffe59a19b3a3 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -703,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) { @@ -737,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; } @@ -787,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; } diff --git a/include/linux/hid.h b/include/linux/hid.h index 773bcb1d4044..938d9ba6d7cd 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -894,6 +894,8 @@ const struct hid_device_id *hid_match_id(const struct hid_device *hdev, const struct hid_device_id *id); const struct hid_device_id *hid_match_device(struct hid_device *hdev, struct hid_driver *hdrv); +bool hid_compare_device_paths(struct hid_device *hdev_a, + struct hid_device *hdev_b, char separator); s32 hid_snto32(__u32 value, unsigned n); __u32 hid_field_extract(const struct hid_device *hid, __u8 *report, unsigned offset, unsigned n); diff --git a/include/linux/platform_data/i2c-hid.h b/include/linux/platform_data/i2c-hid.h index 1fb088239d12..c628bb5e1061 100644 --- a/include/linux/platform_data/i2c-hid.h +++ b/include/linux/platform_data/i2c-hid.h @@ -12,14 +12,13 @@ #ifndef __LINUX_I2C_HID_H #define __LINUX_I2C_HID_H +#include <linux/regulator/consumer.h> #include <linux/types.h> -struct regulator; - /** * struct i2chid_platform_data - used by hid over i2c implementation. * @hid_descriptor_address: i2c register where the HID descriptor is stored. - * @supply: regulator for powering on the device. + * @supplies: regulators for powering on the device. * @post_power_delay_ms: delay after powering on before device is usable. * * Note that it is the responsibility of the platform driver (or the acpi 5.0 @@ -35,7 +34,7 @@ struct regulator; */ struct i2c_hid_platform_data { u16 hid_descriptor_address; - struct regulator *supply; + struct regulator_bulk_data supplies[2]; int post_power_delay_ms; }; |