diff options
27 files changed, 1180 insertions, 253 deletions
diff --git a/Documentation/ABI/testing/sysfs-driver-hid-logitech-hidpp b/Documentation/ABI/testing/sysfs-driver-hid-logitech-hidpp new file mode 100644 index 000000000000..d8f831f2d6b5 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-hid-logitech-hidpp @@ -0,0 +1,19 @@ +What: /sys/bus/hid/drivers/logitech-hidpp-device/<dev>/range +Date: Jan, 2016 +KernelVersion: 4.6 +Contact: linux-input@vger.kernel.org +Description: + (RW) This attribute controls the amount of 'turn' permitted in + Logitech G920 wheel. Reading from the file shows the current + range of the steering wheel. Writing a value within the min and + max boundary sets the range of the wheel. + +What: /sys/bus/hid/drivers/logitech-hidpp-device/<dev>/builtin_power_supply +Date: Apr, 2017 +KernelVersion: 4.12 +Contact: linux-input@vger.kernel.org +Description: + Presence of this file indicates that HID++ driver is capable of + handling battery properties in the kernel. This way, upower can + add a udev rule to decide whether or not it should use the + internal unifying support or the generic kernel one. diff --git a/Documentation/ABI/testing/sysfs-driver-hid-ntrig b/Documentation/ABI/testing/sysfs-driver-hid-ntrig new file mode 100644 index 000000000000..e574a5625efe --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-hid-ntrig @@ -0,0 +1,70 @@ +What: /sys/bus/hid/drivers/ntrig/<dev>/activate_slack +Date: May, 2010 +KernelVersion: 2.6.35 +Contact: linux-input@vger.kernel.org +Description: + (RW) Number of contact frames ignored before acknowledging the + start of activity (activating touch). + + +What: /sys/bus/hid/drivers/ntrig/<dev>/decativate_slack +Date: May, 2010 +KernelVersion: 2.6.35 +Contact: linux-input@vger.kernel.org +Description: + (RW) Number of empty (no contact) frames ignored before + acknowledging the end of activity (deactivating touch). + + When the last finger is removed from the device, it sends a + number of empty frames. By holding off on deactivation for a few + frames false erroneous disconnects can be tolerated, where the + sensor may mistakenly not detect a finger that is still present. + + +What: /sys/bus/hid/drivers/ntrig/<dev>/activation_width +What: /sys/bus/hid/drivers/ntrig/<dev>/activation_height +Date: May, 2010 +KernelVersion: 2.6.35 +Contact: linux-input@vger.kernel.org +Description: + Threholds to override activation slack. + + activation_width: (RW) Width threshold to immediately + start processing touch events. + + activation_height: (RW) Height threshold to immediately + start processing touch events. + + +What: /sys/bus/hid/drivers/ntrig/<dev>/min_width +What: /sys/bus/hid/drivers/ntrig/<dev>/min_height +Date: May, 2010 +KernelVersion: 2.6.35 +Contact: linux-input@vger.kernel.org +Description: + Minimum size contact accepted. + + min_width: (RW) Minimum touch contact width to decide + activation and activity. + + min_height: (RW) Minimum touch contact height to decide + activation and activity. + + +What: /sys/bus/hid/drivers/ntrig/<dev>/sensor_physical_width +What: /sys/bus/hid/drivers/ntrig/<dev>/sensor_physical_height +Date: May, 2010 +KernelVersion: 2.6.35 +Contact: linux-input@vger.kernel.org +Description: + (RO) These are internal ranges not used for normal events but + useful for tuning. + + +What: /sys/bus/hid/drivers/ntrig/<dev>/sensor_logical_width +What: /sys/bus/hid/drivers/ntrig/<dev>/sensor_logical_height +Date: May, 2010 +KernelVersion: 2.6.35 +Contact: linux-input@vger.kernel.org +Description: + (RO) The range for positions reported during activity. diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 5ffe4c4121bd..34dac7cef4cf 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -4456,6 +4456,9 @@ usbhid.jspoll= [USBHID] The interval which joysticks are to be polled at. + usbhid.kbpoll= + [USBHID] The interval which keyboards are to be polled at. + usb-storage.delay_use= [UMS] The delay in seconds before a new device is scanned for Logical Units (default 1). diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 19c499f5623d..60252fd796f6 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -274,15 +274,23 @@ config HID_EMS_FF Currently the following devices are known to be supported: - Trio Linker Plus II +config HID_ELAN + tristate "ELAN USB Touchpad Support" + depends on LEDS_CLASS && USB_HID + ---help--- + Say Y to enable support for the USB ELAN touchpad + Currently the following devices are known to be supported: + - HP Pavilion X2 10-p0XX. + config HID_ELECOM tristate "ELECOM HID devices" depends on HID ---help--- Support for ELECOM devices: - BM084 Bluetooth Mouse - - EX-G Trackball (Wired and wireless) - - DEFT Trackball (Wired and wireless) - - HUGE Trackball (Wired and wireless) + - EX-G Trackballs (M-XT3DRBK, M-XT3URBK) + - DEFT Trackballs (M-DT1DRBK, M-DT1URBK, M-DT2DRBK, M-DT2URBK) + - HUGE Trackballs (M-HT1DRBK, M-HT1URBK) config HID_ELO tristate "ELO USB 4000/4500 touchscreen" @@ -331,6 +339,12 @@ config HOLTEK_FF Say Y here if you have a Holtek On Line Grip based game controller and want to have force feedback support for it. +config HID_GOOGLE_HAMMER + tristate "Google Hammer Keyboard" + depends on USB_HID && LEDS_CLASS + ---help--- + Say Y here if you have a Google Hammer device. + config HID_GT683R tristate "MSI GT68xR LED support" depends on LEDS_CLASS && USB_HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index eb13b9e92d85..17a8bd97da9d 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -39,11 +39,13 @@ obj-$(CONFIG_HID_CP2112) += hid-cp2112.o obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o obj-$(CONFIG_HID_DRAGONRISE) += hid-dr.o obj-$(CONFIG_HID_EMS_FF) += hid-emsff.o +obj-$(CONFIG_HID_ELAN) += hid-elan.o obj-$(CONFIG_HID_ELECOM) += hid-elecom.o obj-$(CONFIG_HID_ELO) += hid-elo.o obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o obj-$(CONFIG_HID_GEMBIRD) += hid-gembird.o obj-$(CONFIG_HID_GFRM) += hid-gfrm.o +obj-$(CONFIG_HID_GOOGLE_HAMMER) += hid-google-hammer.o obj-$(CONFIG_HID_GT683R) += hid-gt683r.o obj-$(CONFIG_HID_GYRATION) += hid-gyration.o obj-$(CONFIG_HID_HOLTEK) += hid-holtek-kbd.o diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index 88b9703318e4..88a5672f42cd 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -570,7 +570,9 @@ static int asus_input_mapping(struct hid_device *hdev, static int asus_start_multitouch(struct hid_device *hdev) { int ret; - const unsigned char buf[] = { FEATURE_REPORT_ID, 0x00, 0x03, 0x01, 0x00 }; + static const unsigned char buf[] = { + FEATURE_REPORT_ID, 0x00, 0x03, 0x01, 0x00 + }; unsigned char *dmabuf = kmemdup(buf, sizeof(buf), GFP_KERNEL); if (!dmabuf) { @@ -644,8 +646,7 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id) * All functionality is on a single HID interface and for * userspace the touchpad must be a separate input_dev. */ - hdev->quirks |= HID_QUIRK_MULTI_INPUT | - HID_QUIRK_NO_EMPTY_INPUT; + hdev->quirks |= HID_QUIRK_MULTI_INPUT; drvdata->tp = &asus_t100chi_tp; } diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index c2560aae5542..5d7cc6bbbac6 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1365,7 +1365,7 @@ u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags) * of implement() working on 8 byte chunks */ - int len = hid_report_len(report) + 7; + u32 len = hid_report_len(report) + 7; return kmalloc(len, flags); } @@ -1430,7 +1430,7 @@ void __hid_request(struct hid_device *hid, struct hid_report *report, { char *buf; int ret; - int len; + u32 len; buf = hid_alloc_report_buf(report, GFP_KERNEL); if (!buf) @@ -1456,14 +1456,14 @@ out: } EXPORT_SYMBOL_GPL(__hid_request); -int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, +int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size, int interrupt) { struct hid_report_enum *report_enum = hid->report_enum + type; struct hid_report *report; struct hid_driver *hdrv; unsigned int a; - int rsize, csize = size; + u32 rsize, csize = size; u8 *cdata = data; int ret = 0; @@ -1521,7 +1521,7 @@ EXPORT_SYMBOL_GPL(hid_report_raw_event); * * This is data entry for lower layers. */ -int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int interrupt) +int hid_input_report(struct hid_device *hid, int type, u8 *data, u32 size, int interrupt) { struct hid_report_enum *report_enum; struct hid_driver *hdrv; @@ -1966,6 +1966,8 @@ static int hid_device_probe(struct device *dev) } } + /* reset the quirks that has been previously set */ + hdev->quirks = hid_lookup_quirk(hdev); hdev->driver = hdrv; if (hdrv->probe) { ret = hdrv->probe(hdev, id); @@ -2197,31 +2199,40 @@ void hid_destroy_device(struct hid_device *hdev) EXPORT_SYMBOL_GPL(hid_destroy_device); -static int __bus_add_driver(struct device_driver *drv, void *data) +static int __hid_bus_reprobe_drivers(struct device *dev, void *data) { - struct hid_driver *added_hdrv = data; - struct hid_driver *hdrv = to_hid_driver(drv); + struct hid_driver *hdrv = data; + struct hid_device *hdev = to_hid_device(dev); - if (hdrv->bus_add_driver) - hdrv->bus_add_driver(added_hdrv); + if (hdev->driver == hdrv && + !hdrv->match(hdev, hid_ignore_special_drivers)) + return device_reprobe(dev); return 0; } -static int __bus_removed_driver(struct device_driver *drv, void *data) +static int __hid_bus_driver_added(struct device_driver *drv, void *data) { - struct hid_driver *removed_hdrv = data; struct hid_driver *hdrv = to_hid_driver(drv); - if (hdrv->bus_removed_driver) - hdrv->bus_removed_driver(removed_hdrv); + if (hdrv->match) { + bus_for_each_dev(&hid_bus_type, NULL, hdrv, + __hid_bus_reprobe_drivers); + } return 0; } +static int __bus_removed_driver(struct device_driver *drv, void *data) +{ + return bus_rescan_devices(&hid_bus_type); +} + int __hid_register_driver(struct hid_driver *hdrv, struct module *owner, const char *mod_name) { + int ret; + hdrv->driver.name = hdrv->name; hdrv->driver.bus = &hid_bus_type; hdrv->driver.owner = owner; @@ -2230,9 +2241,13 @@ int __hid_register_driver(struct hid_driver *hdrv, struct module *owner, INIT_LIST_HEAD(&hdrv->dyn_list); spin_lock_init(&hdrv->dyn_lock); - bus_for_each_drv(&hid_bus_type, NULL, hdrv, __bus_add_driver); + ret = driver_register(&hdrv->driver); + + if (ret == 0) + bus_for_each_drv(&hid_bus_type, NULL, NULL, + __hid_bus_driver_added); - return driver_register(&hdrv->driver); + return ret; } EXPORT_SYMBOL_GPL(__hid_register_driver); diff --git a/drivers/hid/hid-corsair.c b/drivers/hid/hid-corsair.c index 9ba5d98a1180..ec9e060ec46c 100644 --- a/drivers/hid/hid-corsair.c +++ b/drivers/hid/hid-corsair.c @@ -2,11 +2,14 @@ * HID driver for Corsair devices * * Supported devices: + * - Vengeance K70 Keyboard + * - K70 RAPIDFIRE Keyboard * - Vengeance K90 Keyboard * - Scimitar PRO RGB Gaming Mouse * * Copyright (c) 2015 Clement Vuchener * Copyright (c) 2017 Oscar Campos + * Copyright (c) 2017 Aaron Bottegal */ /* @@ -673,7 +676,7 @@ static int corsair_input_mapping(struct hid_device *dev, } /* - * The report descriptor of Corsair Scimitar RGB Pro gaming mouse is + * The report descriptor of some of the Corsair gaming mice is * non parseable as they define two consecutive Logical Minimum for * the Usage Page (Consumer) in rdescs bytes 75 and 77 being 77 0x16 * that should be obviousy 0x26 for Logical Magimum of 16 bits. This @@ -681,7 +684,8 @@ static int corsair_input_mapping(struct hid_device *dev, * Minimum being larger than Logical Maximum. * * This driver fixes the report descriptor for: - * - USB ID b1c:1b3e, sold as Scimitar RGB Pro Gaming mouse + * - USB ID 1b1c:1b34, sold as GLAIVE RGB Gaming mouse + * - USB ID 1b1c:1b3e, sold as Scimitar RGB Pro Gaming mouse */ static __u8 *corsair_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc, @@ -691,13 +695,14 @@ static __u8 *corsair_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc, if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { /* - * Corsair Scimitar RGB Pro report descriptor is broken and - * defines two different Logical Minimum for the Consumer - * Application. The byte 77 should be a 0x26 defining a 16 - * bits integer for the Logical Maximum but it is a 0x16 + * Corsair GLAIVE RGB and Scimitar RGB Pro report descriptor is + * broken and defines two different Logical Minimum for the + * Consumer Application. The byte 77 should be a 0x26 defining + * a 16 bits integer for the Logical Maximum but it is a 0x16 * instead (Logical Minimum) */ switch (hdev->product) { + case USB_DEVICE_ID_CORSAIR_GLAIVE_RGB: case USB_DEVICE_ID_CORSAIR_SCIMITAR_PRO_RGB: if (*rsize >= 172 && rdesc[75] == 0x15 && rdesc[77] == 0x16 && rdesc[78] == 0xff && rdesc[79] == 0x0f) { @@ -716,7 +721,14 @@ static const struct hid_device_id corsair_devices[] = { .driver_data = CORSAIR_USE_K90_MACRO | CORSAIR_USE_K90_BACKLIGHT }, { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, + USB_DEVICE_ID_CORSAIR_GLAIVE_RGB) }, + { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_SCIMITAR_PRO_RGB) }, + /* + * Vengeance K70 and K70 RAPIDFIRE share product IDs. + */ + { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, + USB_DEVICE_ID_CORSAIR_K70R) }, {} }; diff --git a/drivers/hid/hid-elan.c b/drivers/hid/hid-elan.c new file mode 100644 index 000000000000..803a725785cf --- /dev/null +++ b/drivers/hid/hid-elan.c @@ -0,0 +1,421 @@ +/* + * HID Driver for ELAN Touchpad + * + * Currently only supports touchpad found on HP Pavilion X2 10 + * + * Copyright (c) 2016 Alexandrov Stanislav <neko@nya.ai> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/hid.h> +#include <linux/input/mt.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/usb.h> + +#include "hid-ids.h" + +#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_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; +}; + +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; +}; + +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); + + return (intf->altsetting->desc.bInterfaceNumber != drvdata->settings->usb_bInterfaceNumber); +} + +static int elan_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if (is_not_elan_touchpad(hdev)) + return 0; + + if (field->report->id == ELAN_SINGLE_FINGER || + field->report->id == ELAN_MT_FIRST_FINGER || + field->report->id == ELAN_MT_SECOND_FINGER) + return -1; + + return 0; +} + +static int elan_input_configured(struct hid_device *hdev, struct hid_input *hi) +{ + int ret; + struct input_dev *input; + struct elan_drvdata *drvdata = hid_get_drvdata(hdev); + + if (is_not_elan_touchpad(hdev)) + return 0; + + input = devm_input_allocate_device(&hdev->dev); + if (!input) + return -ENOMEM; + + input->name = "Elan Touchpad"; + input->phys = hdev->phys; + input->uniq = hdev->uniq; + input->id.bustype = hdev->bus; + input->id.vendor = hdev->vendor; + input->id.product = hdev->product; + 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); + + __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); + if (ret) { + hid_err(hdev, "Failed to init elan MT slots: %d\n", ret); + return ret; + } + + ret = input_register_device(input); + if (ret) { + hid_err(hdev, "Failed to register elan input device: %d\n", + ret); + input_free_device(input); + return ret; + } + + drvdata->input = input; + + return 0; +} + +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; + + bool active = !!data; + + input_mt_slot(input, slot_num); + input_mt_report_slot_state(input, MT_TOOL_FINGER, active); + if (active) { + x = ((data[0] & 0xF0) << 4) | data[1]; + y = drvdata->settings->max_y - + (((data[0] & 0x07) << 8) | data[2]); + w = 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); + } +} + +static void elan_report_input(struct elan_drvdata *drvdata, u8 *data) +{ + int i; + struct input_dev *input = drvdata->input; + + /* + * There is 3 types of reports: for single touch, + * for multitouch - first finger and for multitouch - second finger + * + * packet structure for ELAN_SINGLE_FINGER and ELAN_MT_FIRST_FINGER: + * + * byte 1: 1 0 0 0 0 0 0 1 // 0x81 or 0x82 + * byte 2: 0 0 0 0 0 0 0 0 // looks like unused + * byte 3: f5 f4 f3 f2 f1 0 0 L + * byte 4: x12 x11 x10 x9 0? y11 y10 y9 + * 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 + * + * packet structure for ELAN_MT_SECOND_FINGER: + * + * byte 1: 1 0 0 0 0 0 1 1 // 0x83 + * byte 2: x12 x11 x10 x9 0 y11 y10 y9 + * 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 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 + */ + + if (data[0] == ELAN_SINGLE_FINGER) { + for (i = 0; i < drvdata->settings->max_fingers; i++) { + if (data[2] & BIT(i + 3)) + elan_report_mt_slot(drvdata, data + 3, i); + else + elan_report_mt_slot(drvdata, NULL, i); + } + input_report_key(input, BTN_LEFT, data[2] & 0x01); + } + /* + * When touched with two fingers Elan touchpad will emit two HID reports + * first is ELAN_MT_FIRST_FINGER and second is ELAN_MT_SECOND_FINGER + * we will save ELAN_MT_FIRST_FINGER report and wait for + * ELAN_MT_SECOND_FINGER to finish multitouch + */ + if (data[0] == ELAN_MT_FIRST_FINGER) { + memcpy(drvdata->prev_report, data, + sizeof(drvdata->prev_report)); + return; + } + + if (data[0] == ELAN_MT_SECOND_FINGER) { + int first = 0; + u8 *prev_report = drvdata->prev_report; + + if (prev_report[0] != ELAN_MT_FIRST_FINGER) + return; + + for (i = 0; i < drvdata->settings->max_fingers; i++) { + if (prev_report[2] & BIT(i + 3)) { + if (!first) { + first = 1; + elan_report_mt_slot(drvdata, prev_report + 3, i); + } else { + elan_report_mt_slot(drvdata, data + 1, i); + } + } else { + elan_report_mt_slot(drvdata, NULL, i); + } + } + input_report_key(input, BTN_LEFT, prev_report[2] & 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) +{ + struct elan_drvdata *drvdata = hid_get_drvdata(hdev); + + if (is_not_elan_touchpad(hdev)) + return 0; + + if (data[0] == ELAN_SINGLE_FINGER || + data[0] == ELAN_MT_FIRST_FINGER || + data[0] == ELAN_MT_SECOND_FINGER) { + if (size == ELAN_INPUT_REPORT_SIZE) { + elan_report_input(drvdata, data); + return 1; + } + } + + return 0; +} + +static int elan_start_multitouch(struct hid_device *hdev) +{ + int ret; + + /* + * This byte sequence will enable multitouch mode and disable + * mouse emulation + */ + const unsigned char buf[] = { 0x0D, 0x00, 0x03, 0x21, 0x00 }; + unsigned char *dmabuf = kmemdup(buf, sizeof(buf), GFP_KERNEL); + + if (!dmabuf) + return -ENOMEM; + + ret = hid_hw_raw_request(hdev, dmabuf[0], dmabuf, sizeof(buf), + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + + kfree(dmabuf); + + if (ret != sizeof(buf)) { + hid_err(hdev, "Failed to start multitouch: %d\n", ret); + return ret; + } + + return 0; +} + +static enum led_brightness elan_mute_led_get_brigtness(struct led_classdev *led_cdev) +{ + struct device *dev = led_cdev->dev->parent; + struct hid_device *hdev = to_hid_device(dev); + struct elan_drvdata *drvdata = hid_get_drvdata(hdev); + + return drvdata->mute_led_state; +} + +static int elan_mute_led_set_brigtness(struct led_classdev *led_cdev, + enum led_brightness value) +{ + int ret; + u8 led_state; + struct device *dev = led_cdev->dev->parent; + struct hid_device *hdev = to_hid_device(dev); + struct elan_drvdata *drvdata = hid_get_drvdata(hdev); + + unsigned char *dmabuf = kzalloc(ELAN_LED_REPORT_SIZE, GFP_KERNEL); + + if (!dmabuf) + return -ENOMEM; + + led_state = !!value; + + dmabuf[0] = ELAN_MUTE_LED_REPORT; + dmabuf[1] = 0x02; + dmabuf[2] = led_state; + + ret = hid_hw_raw_request(hdev, dmabuf[0], dmabuf, ELAN_LED_REPORT_SIZE, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); + + kfree(dmabuf); + + if (ret != ELAN_LED_REPORT_SIZE) { + hid_err(hdev, "Failed to set mute led brightness: %d\n", ret); + return ret; + } + + drvdata->mute_led_state = led_state; + return 0; +} + +static int elan_init_mute_led(struct hid_device *hdev) +{ + struct elan_drvdata *drvdata = hid_get_drvdata(hdev); + struct led_classdev *mute_led = &drvdata->mute_led; + + mute_led->name = "elan:red:mute"; + mute_led->brightness_get = elan_mute_led_get_brigtness; + mute_led->brightness_set_blocking = elan_mute_led_set_brigtness; + mute_led->max_brightness = LED_ON; + mute_led->dev = &hdev->dev; + + return devm_led_classdev_register(&hdev->dev, mute_led); +} + +static int elan_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + struct elan_drvdata *drvdata; + + drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL); + + if (!drvdata) + return -ENOMEM; + + drvdata->settings = (struct elan_touchpad_settings *)id->driver_data; + hid_set_drvdata(hdev, drvdata); + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "Hid Parse failed\n"); + return ret; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + hid_err(hdev, "Hid hw start failed\n"); + return ret; + } + + if (is_not_elan_touchpad(hdev)) + return 0; + + if (!drvdata->input) { + hid_err(hdev, "Input device is not registred\n"); + ret = -ENAVAIL; + goto err; + } + + ret = elan_start_multitouch(hdev); + if (ret) + goto err; + + ret = elan_init_mute_led(hdev); + if (ret) + goto err; + + return 0; +err: + hid_hw_stop(hdev); + return ret; +} + +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_10_COVER), + (kernel_ulong_t)&hp_x2_10_touchpad_data}, + { } +}; + +MODULE_DEVICE_TABLE(hid, elan_devices); + +static struct hid_driver elan_driver = { + .name = "elan", + .id_table = elan_devices, + .input_mapping = elan_input_mapping, + .input_configured = elan_input_configured, + .raw_event = elan_raw_event, + .probe = elan_probe, + .remove = elan_remove, +}; + +module_hid_driver(elan_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alexandrov Stanislav"); +MODULE_DESCRIPTION("Driver for HID ELAN Touchpads"); diff --git a/drivers/hid/hid-elecom.c b/drivers/hid/hid-elecom.c index 1a1ecc491c02..ae8e9413c79d 100644 --- a/drivers/hid/hid-elecom.c +++ b/drivers/hid/hid-elecom.c @@ -1,9 +1,9 @@ /* * HID driver for ELECOM devices: * - BM084 Bluetooth Mouse - * - EX-G Trackball (Wired and wireless) - * - DEFT Trackball (Wired and wireless) - * - HUGE Trackball (Wired and wireless) + * - EX-G Trackballs (M-XT3DRBK, M-XT3URBK, M-XT4DRBK) + * - DEFT Trackballs (M-DT1DRBK, M-DT1URBK, M-DT2DRBK, M-DT2URBK) + * - HUGE Trackballs (M-HT1DRBK, M-HT1URBK) * * Copyright (c) 2010 Richard Nauber <Richard.Nauber@gmail.com> * Copyright (c) 2016 Yuxuan Shui <yshuiv7@gmail.com> @@ -65,14 +65,15 @@ static __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc, rdesc[47] = 0x00; } break; - case USB_DEVICE_ID_ELECOM_EX_G_WIRED: - case USB_DEVICE_ID_ELECOM_EX_G_WIRELESS: + case USB_DEVICE_ID_ELECOM_M_XT3URBK: + case USB_DEVICE_ID_ELECOM_M_XT3DRBK: + case USB_DEVICE_ID_ELECOM_M_XT4DRBK: mouse_button_fixup(hdev, rdesc, *rsize, 6); break; - case USB_DEVICE_ID_ELECOM_DEFT_WIRED: - case USB_DEVICE_ID_ELECOM_DEFT_WIRELESS: - case USB_DEVICE_ID_ELECOM_HUGE_WIRED: - case USB_DEVICE_ID_ELECOM_HUGE_WIRELESS: + case USB_DEVICE_ID_ELECOM_M_DT1URBK: + case USB_DEVICE_ID_ELECOM_M_DT1DRBK: + case USB_DEVICE_ID_ELECOM_M_HT1URBK: + case USB_DEVICE_ID_ELECOM_M_HT1DRBK: mouse_button_fixup(hdev, rdesc, *rsize, 8); break; } @@ -81,12 +82,13 @@ static __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc, static const struct hid_device_id elecom_devices[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, - { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_EX_G_WIRED) }, - { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_EX_G_WIRELESS) }, - { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_DEFT_WIRED) }, - { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_DEFT_WIRELESS) }, - { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_HUGE_WIRED) }, - { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_HUGE_WIRELESS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1URBK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1DRBK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1URBK) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK) }, { } }; MODULE_DEVICE_TABLE(hid, elecom_devices); diff --git a/drivers/hid/hid-generic.c b/drivers/hid/hid-generic.c index 3c0a1bf433d7..c25b4718de44 100644 --- a/drivers/hid/hid-generic.c +++ b/drivers/hid/hid-generic.c @@ -26,37 +26,6 @@ static struct hid_driver hid_generic; -static int __unmap_hid_generic(struct device *dev, void *data) -{ - struct hid_driver *hdrv = data; - struct hid_device *hdev = to_hid_device(dev); - - /* only unbind matching devices already bound to hid-generic */ - if (hdev->driver != &hid_generic || - hid_match_device(hdev, hdrv) == NULL) - return 0; - - if (dev->parent) /* Needed for USB */ - device_lock(dev->parent); - device_release_driver(dev); - if (dev->parent) - device_unlock(dev->parent); - - return 0; |