Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid

Pull HID updates from Jiri Kosina:

 - remove hid_have_special_driver[] entry hard requirement for any newly
   supported VID/PID by a specific non-core hid driver, and general
   related cleanup of HID matching core, from Benjamin Tissoires

 - support for new Wacom devices and a few small fixups for already
   supported ones in Wacom driver, from Aaron Armstrong Skomra and Jason
   Gerecke

 - sysfs interface fix for roccat driver from Dan Carpenter

 - support for new Asus HW (T100TAF, T100HA, T200TA) from Hans de Goede

 - improved support for Jabra devices, from Niels Skou Olsen

 - other assorted small fixes and new device IDs

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (30 commits)
  HID: quirks: Fix keyboard + touchpad on Toshiba Click Mini not working
  HID: roccat: prevent an out of bounds read in kovaplus_profile_activated()
  HID: asus: Fix special function keys on T200TA
  HID: asus: Add touchpad max x/y and resolution info for the T200TA
  HID: wacom: Add support for One by Wacom (CTL-472 / CTL-672)
  HID: wacom: Fix reporting of touch toggle (WACOM_HID_WD_MUTE_DEVICE) events
  HID: intel-ish-hid: Enable Cannon Lake and Coffee Lake laptop/desktop
  HID: elecom: rewrite report fixup for EX-G and future mice
  HID: sony: Report DS4 version info through sysfs
  HID: sony: Print reversed MAC address via %pMR
  HID: wacom: EKR: ensure devres groups at higher indexes are released
  HID: rmi: Support the Fujitsu R726 Pad dock using hid-rmi
  HID: add quirk for another PIXART OEM mouse used by HP
  HID: quirks: make array hid_quirks static
  HID: hid-multitouch: support fine-grain orientation reporting
  HID: asus: Add product-id for the T100TAF and T100HA keyboard docks
  HID: elo: clear BTN_LEFT mapping
  HID: multitouch: Combine all left-button events in a frame
  HID: multitouch: Only look at non touch fields in first packet of a frame
  HID: multitouch: Properly deal with Win8 PTP reports with 0 touches
  ...
This commit is contained in:
Linus Torvalds 2018-01-31 13:00:01 -08:00
commit 183b6366cf
27 changed files with 1996 additions and 1397 deletions

View File

@ -31,7 +31,7 @@ device-specific compatible properties, which should be used in addition to the
- vdd-supply: phandle of the regulator that provides the supply voltage.
- post-power-on-delay-ms: time required by the device after enabling its regulators
before it is ready for communication. Must be used with 'vdd-supply'.
or powering it on, before it is ready for communication.
Example:

View File

@ -269,10 +269,11 @@ ABS_MT_ORIENTATION
The orientation of the touching ellipse. The value should describe a signed
quarter of a revolution clockwise around the touch center. The signed value
range is arbitrary, but zero should be returned for an ellipse aligned with
the Y axis of the surface, a negative value when the ellipse is turned to
the left, and a positive value when the ellipse is turned to the
right. When completely aligned with the X axis, the range max should be
returned.
the Y axis (north) of the surface, a negative value when the ellipse is
turned to the left, and a positive value when the ellipse is turned to the
right. When aligned with the X axis in the positive direction, the range
max should be returned; when aligned with the X axis in the negative
direction, the range -max should be returned.
Touch ellipsis are symmetrical by default. For devices capable of true 360
degree orientation, the reported orientation must exceed the range max to

View File

@ -280,6 +280,7 @@ config HID_ELECOM
---help---
Support for ELECOM devices:
- BM084 Bluetooth Mouse
- EX-G Trackball (Wired and wireless)
- DEFT Trackball (Wired and wireless)
- HUGE Trackball (Wired and wireless)
@ -396,6 +397,17 @@ config HID_ITE
---help---
Support for ITE devices not fully compliant with HID standard.
config HID_JABRA
tristate "Jabra USB HID Driver"
depends on HID
---help---
Support for Jabra USB HID devices.
Prevents mapping of vendor defined HID usages to input events. Without
this driver HID reports from Jabra devices may incorrectly be seen as
mouse button events.
Say M here if you may ever plug in a Jabra USB device.
config HID_TWINHAN
tristate "Twinhan IR remote control"
depends on HID

View File

@ -2,7 +2,7 @@
#
# Makefile for the HID driver
#
hid-y := hid-core.o hid-input.o
hid-y := hid-core.o hid-input.o hid-quirks.o
hid-$(CONFIG_DEBUG_FS) += hid-debug.o
obj-$(CONFIG_HID) += hid.o
@ -52,6 +52,7 @@ obj-$(CONFIG_HID_HOLTEK) += hid-holtekff.o
obj-$(CONFIG_HID_HYPERV_MOUSE) += hid-hyperv.o
obj-$(CONFIG_HID_ICADE) += hid-icade.o
obj-$(CONFIG_HID_ITE) += hid-ite.o
obj-$(CONFIG_HID_JABRA) += hid-jabra.o
obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o
obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o
obj-$(CONFIG_HID_KYE) += hid-kye.o

View File

@ -26,6 +26,7 @@
* any later version.
*/
#include <linux/dmi.h>
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/input/mt.h>
@ -119,6 +120,24 @@ static const struct asus_touchpad_info asus_t100ta_tp = {
.max_contacts = 5,
};
static const struct asus_touchpad_info asus_t100ha_tp = {
.max_x = 2640,
.max_y = 1320,
.res_x = 30, /* units/mm */
.res_y = 29, /* units/mm */
.contact_size = 5,
.max_contacts = 5,
};
static const struct asus_touchpad_info asus_t200ta_tp = {
.max_x = 3120,
.max_y = 1716,
.res_x = 30, /* units/mm */
.res_y = 28, /* units/mm */
.contact_size = 5,
.max_contacts = 5,
};
static const struct asus_touchpad_info asus_t100chi_tp = {
.max_x = 2640,
.max_y = 1320,
@ -606,7 +625,17 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (intf->altsetting->desc.bInterfaceNumber == T100_TPAD_INTF) {
drvdata->quirks = QUIRK_SKIP_INPUT_MAPPING;
drvdata->tp = &asus_t100ta_tp;
/*
* The T100HA uses the same USB-ids as the T100TAF and
* the T200TA uses the same USB-ids as the T100TA, while
* both have different max x/y values as the T100TA[F].
*/
if (dmi_match(DMI_PRODUCT_NAME, "T100HAN"))
drvdata->tp = &asus_t100ha_tp;
else if (dmi_match(DMI_PRODUCT_NAME, "T200TA"))
drvdata->tp = &asus_t200ta_tp;
else
drvdata->tp = &asus_t100ta_tp;
}
}
@ -686,9 +715,10 @@ static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc,
hid_info(hdev, "Fixing up Asus notebook report descriptor\n");
rdesc[55] = 0xdd;
}
/* For the T100TA keyboard dock */
/* For the T100TA/T200TA keyboard dock */
if (drvdata->quirks & QUIRK_T100_KEYBOARD &&
*rsize == 76 && rdesc[73] == 0x81 && rdesc[74] == 0x01) {
(*rsize == 76 || *rsize == 101) &&
rdesc[73] == 0x81 && rdesc[74] == 0x01) {
hid_info(hdev, "Fixing up Asus T100 keyb report descriptor\n");
rdesc[74] &= ~HID_MAIN_ITEM_CONSTANT;
}
@ -751,7 +781,10 @@ static const struct hid_device_id asus_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3), QUIRK_G752_KEYBOARD },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_T100_KEYBOARD),
USB_DEVICE_ID_ASUSTEK_T100TA_KEYBOARD),
QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_T100TAF_KEYBOARD),
QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES },
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_ASUS_AK1D) },
{ HID_USB_DEVICE(USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_ASUS_MD_5110) },

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,15 @@
/*
* HID driver for ELECOM devices.
* HID driver for ELECOM devices:
* - BM084 Bluetooth Mouse
* - EX-G Trackball (Wired and wireless)
* - DEFT Trackball (Wired and wireless)
* - HUGE Trackball (Wired and wireless)
*
* Copyright (c) 2010 Richard Nauber <Richard.Nauber@gmail.com>
* Copyright (c) 2016 Yuxuan Shui <yshuiv7@gmail.com>
* Copyright (c) 2017 Diego Elio Pettenò <flameeyes@flameeyes.eu>
* Copyright (c) 2017 Alex Manoussakis <amanou@gnu.org>
* Copyright (c) 2017 Tomasz Kramkowski <tk@the-tk.com>
*/
/*
@ -19,6 +25,34 @@
#include "hid-ids.h"
/*
* Certain ELECOM mice misreport their button count meaning that they only work
* correctly with the ELECOM mouse assistant software which is unavailable for
* Linux. A four extra INPUT reports and a FEATURE report are described by the
* report descriptor but it does not appear that these enable software to
* control what the extra buttons map to. The only simple and straightforward
* solution seems to involve fixing up the report descriptor.
*
* Report descriptor format:
* Positions 13, 15, 21 and 31 store the button bit count, button usage minimum,
* button usage maximum and padding bit count respectively.
*/
#define MOUSE_BUTTONS_MAX 8
static void mouse_button_fixup(struct hid_device *hdev,
__u8 *rdesc, unsigned int rsize,
int nbuttons)
{
if (rsize < 32 || rdesc[12] != 0x95 ||
rdesc[14] != 0x75 || rdesc[15] != 0x01 ||
rdesc[20] != 0x29 || rdesc[30] != 0x75)
return;
hid_info(hdev, "Fixing up Elecom mouse button count\n");
nbuttons = clamp(nbuttons, 0, MOUSE_BUTTONS_MAX);
rdesc[13] = nbuttons;
rdesc[21] = nbuttons;
rdesc[31] = MOUSE_BUTTONS_MAX - nbuttons;
}
static __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
@ -31,45 +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:
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:
/* The DEFT/HUGE trackball has eight buttons, but its descriptor
* only reports five, disabling the three Fn buttons on the top
* of the mouse.
*
* Apply the following diff to the descriptor:
*
* Collection (Physical), Collection (Physical),
* Report ID (1), Report ID (1),
* Report Count (5), -> Report Count (8),
* Report Size (1), Report Size (1),
* Usage Page (Button), Usage Page (Button),
* Usage Minimum (01h), Usage Minimum (01h),
* Usage Maximum (05h), -> Usage Maximum (08h),
* Logical Minimum (0), Logical Minimum (0),
* Logical Maximum (1), Logical Maximum (1),
* Input (Variable), Input (Variable),
* Report Count (1), -> Report Count (0),
* Report Size (3), Report Size (3),
* Input (Constant), Input (Constant),
* Report Size (16), Report Size (16),
* Report Count (2), Report Count (2),
* Usage Page (Desktop), Usage Page (Desktop),
* Usage (X), Usage (X),
* Usage (Y), Usage (Y),
* Logical Minimum (-32768), Logical Minimum (-32768),
* Logical Maximum (32767), Logical Maximum (32767),
* Input (Variable, Relative), Input (Variable, Relative),
* End Collection, End Collection,
*/
if (*rsize == 213 && rdesc[13] == 5 && rdesc[21] == 5) {
hid_info(hdev, "Fixing up Elecom DEFT/HUGE Fn buttons\n");
rdesc[13] = 8; /* Button/Variable Report Count */
rdesc[21] = 8; /* Button/Variable Usage Maximum */
rdesc[29] = 0; /* Button/Constant Report Count */
}
mouse_button_fixup(hdev, rdesc, *rsize, 8);
break;
}
return rdesc;
@ -77,6 +81,8 @@ 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) },

View File

@ -42,6 +42,12 @@ static int elo_input_configured(struct hid_device *hdev,
{
struct input_dev *input = hidinput->input;
/*
* ELO devices have one Button usage in GenDesk field, which makes
* hid-input map it to BTN_LEFT; that confuses userspace, which then
* considers the device to be a mouse/touchpad instead of touchscreen.
*/
clear_bit(BTN_LEFT, input->keybit);
set_bit(BTN_TOUCH, input->keybit);
set_bit(ABS_PRESSURE, input->absbit);
input_set_abs_params(input, ABS_PRESSURE, 0, 256, 0, 0);

View File

@ -24,8 +24,71 @@
#include <linux/hid.h>
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;
}
static void hid_generic_add_driver(struct hid_driver *hdrv)
{
bus_for_each_dev(&hid_bus_type, NULL, hdrv, __unmap_hid_generic);
}
static void hid_generic_removed_driver(struct hid_driver *hdrv)
{
int ret;
ret = driver_attach(&hid_generic.driver);
}
static int __check_hid_generic(struct device_driver *drv, void *data)
{
struct hid_driver *hdrv = to_hid_driver(drv);
struct hid_device *hdev = data;
if (hdrv == &hid_generic)
return 0;
return hid_match_device(hdev, hdrv) != NULL;
}
static bool hid_generic_match(struct hid_device *hdev,
bool ignore_special_driver)
{
if (ignore_special_driver)
return true;
if (hdev->quirks & HID_QUIRK_HAVE_SPECIAL_DRIVER)
return false;
/*
* If any other driver wants the device, leave the device to this other
* driver.
*/
if (bus_for_each_drv(&hid_bus_type, NULL, hdev, __check_hid_generic))
return false;
return true;
}
static const struct hid_device_id hid_table[] = {
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_GENERIC, HID_ANY_ID, HID_ANY_ID) },
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, HID_ANY_ID, HID_ANY_ID) },
{ }
};
MODULE_DEVICE_TABLE(hid, hid_table);
@ -33,6 +96,9 @@ MODULE_DEVICE_TABLE(hid, hid_table);
static struct hid_driver hid_generic = {
.name = "hid-generic",
.id_table = hid_table,
.match = hid_generic_match,
.bus_add_driver = hid_generic_add_driver,
.bus_removed_driver = hid_generic_removed_driver,
};
module_hid_driver(hid_generic);

View File

@ -178,7 +178,8 @@
#define USB_VENDOR_ID_ASUSTEK 0x0b05
#define USB_DEVICE_ID_ASUSTEK_LCM 0x1726
#define USB_DEVICE_ID_ASUSTEK_LCM2 0x175b
#define USB_DEVICE_ID_ASUSTEK_T100_KEYBOARD 0x17e0
#define USB_DEVICE_ID_ASUSTEK_T100TA_KEYBOARD 0x17e0
#define USB_DEVICE_ID_ASUSTEK_T100TAF_KEYBOARD 0x1807
#define USB_DEVICE_ID_ASUSTEK_T100CHI_KEYBOARD 0x8502
#define USB_DEVICE_ID_ASUSTEK_T304_KEYBOARD 0x184a
#define USB_DEVICE_ID_ASUSTEK_I2C_KEYBOARD 0x8585
@ -370,6 +371,8 @@
#define USB_VENDOR_ID_ELECOM 0x056e
#define USB_DEVICE_ID_ELECOM_BM084 0x0061
#define USB_DEVICE_ID_ELECOM_EX_G_WIRED 0x00fb
#define USB_DEVICE_ID_ELECOM_EX_G_WIRELESS 0x00fc
#define USB_DEVICE_ID_ELECOM_DEFT_WIRED 0x00fe
#define USB_DEVICE_ID_ELECOM_DEFT_WIRELESS 0x00ff
#define USB_DEVICE_ID_ELECOM_HUGE_WIRED 0x010c
@ -535,6 +538,7 @@
#define USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0A4A 0x0a4a
#define USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0B4A 0x0b4a
#define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE 0x134a
#define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_094A 0x094a
#define USB_VENDOR_ID_HUION 0x256c
#define USB_DEVICE_ID_HUION_TABLET 0x006e
@ -1156,6 +1160,7 @@
#define USB_VENDOR_ID_PRIMAX 0x0461
#define USB_DEVICE_ID_PRIMAX_MOUSE_4D22 0x4d22
#define USB_DEVICE_ID_PRIMAX_KEYBOARD 0x4e05
#define USB_DEVICE_ID_PRIMAX_REZEL 0x4e72
#define USB_VENDOR_ID_RISO_KAGAKU 0x1294 /* Riso Kagaku Corp. */

58
drivers/hid/hid-jabra.c Normal file
View File

@ -0,0 +1,58 @@
/*
* Jabra USB HID Driver
*
* Copyright (c) 2017 Niels Skou Olsen <nolsen@jabra.com>
*/
/*
* 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/module.h>
#include "hid-ids.h"
#define HID_UP_VENDOR_DEFINED_MIN 0xff000000
#define HID_UP_VENDOR_DEFINED_MAX 0xffff0000
static int jabra_input_mapping(struct hid_device *hdev,
struct hid_input *hi,
struct hid_field *field,
struct hid_usage *usage,
unsigned long **bit, int *max)
{
int is_vendor_defined =
((usage->hid & HID_USAGE_PAGE) >= HID_UP_VENDOR_DEFINED_MIN &&
(usage->hid & HID_USAGE_PAGE) <= HID_UP_VENDOR_DEFINED_MAX);
dbg_hid("hid=0x%08x appl=0x%08x coll_idx=0x%02x usage_idx=0x%02x: %s\n",
usage->hid,
field->application,
usage->collection_index,
usage->usage_index,
is_vendor_defined ? "ignored" : "defaulted");
/* Ignore vendor defined usages, default map standard usages */
return is_vendor_defined ? -1 : 0;
}
static const struct hid_device_id jabra_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_JABRA, HID_ANY_ID) },
{ }
};
MODULE_DEVICE_TABLE(hid, jabra_devices);
static struct hid_driver jabra_driver = {
.name = "jabra",
.id_table = jabra_devices,
.input_mapping = jabra_input_mapping,
};
module_hid_driver(jabra_driver);
MODULE_AUTHOR("Niels Skou Olsen <nolsen@jabra.com>");
MODULE_DESCRIPTION("Jabra USB HID Driver");
MODULE_LICENSE("GPL");

View File

@ -85,11 +85,12 @@ MODULE_LICENSE("GPL");
#define MT_IO_FLAGS_PENDING_SLOTS 2
struct mt_slot {
__s32 x, y, cx, cy, p, w, h;
__s32 x, y, cx, cy, p, w, h, a;
__s32 contactid; /* the device ContactID assigned to this slot */
bool touch_state; /* is the touch valid? */
bool inrange_state; /* is the finger in proximity of the sensor? */
bool confidence_state; /* is the touch made by a finger? */
bool has_azimuth; /* the contact reports azimuth */
};
struct mt_class {
@ -119,6 +120,10 @@ struct mt_device {
unsigned long mt_io_flags; /* mt flags (MT_IO_FLAGS_*) */
int cc_index; /* contact count field index in the report */
int cc_value_index; /* contact count value index in the field */
int scantime_index; /* scantime field index in the report */
int scantime_val_index; /* scantime value index in the field */
int prev_scantime; /* scantime reported in the previous packet */
int left_button_state; /* left button state */
unsigned last_slot_field; /* the last field of a slot */
unsigned mt_report_id; /* the report ID of the multitouch device */
unsigned long initial_quirks; /* initial quirks state */
@ -582,8 +587,15 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
if (!(cls->quirks & MT_QUIRK_NO_AREA)) {
set_abs(hi->input, ABS_MT_TOUCH_MINOR, field,
cls->sn_height);
input_set_abs_params(hi->input,
ABS_MT_ORIENTATION, 0, 1, 0, 0);
/*
* Only set ABS_MT_ORIENTATION if it is not
* already set by the HID_DG_AZIMUTH usage.
*/
if (!test_bit(ABS_MT_ORIENTATION,
hi->input->absbit))
input_set_abs_params(hi->input,
ABS_MT_ORIENTATION, 0, 1, 0, 0);
}
mt_store_field(usage, td, hi);
return 1;
@ -599,6 +611,12 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
EV_MSC, MSC_TIMESTAMP);
input_set_capability(hi->input, EV_MSC, MSC_TIMESTAMP);
mt_store_field(usage, td, hi);
/* Ignore if indexes are out of bounds. */
if (field->index >= field->report->maxfield ||
usage->usage_index >= field->report_count)
return 1;
td->scantime_index = field->index;
td->scantime_val_index = usage->usage_index;
return 1;
case HID_DG_CONTACTCOUNT:
/* Ignore if indexes are out of bounds. */
@ -608,6 +626,21 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
td->cc_index = field->index;
td->cc_value_index = usage->usage_index;
return 1;
case HID_DG_AZIMUTH:
hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_ORIENTATION);
/*
* Azimuth has the range of [0, MAX) representing a full
* revolution. Set ABS_MT_ORIENTATION to a quarter of
* MAX according the definition of ABS_MT_ORIENTATION
*/
input_set_abs_params(hi->input, ABS_MT_ORIENTATION,
-field->logical_maximum / 4,
field->logical_maximum / 4,
cls->sn_move ?
field->logical_maximum / cls->sn_move : 0, 0);
mt_store_field(usage, td, hi);
return 1;
case HID_DG_CONTACTMAX:
/* we don't set td->last_slot_field as contactcount and
* contact max are global to the report */
@ -700,6 +733,10 @@ static void mt_complete_slot(struct mt_device *td, struct input_dev *input)
int wide = (s->w > s->h);
int major = max(s->w, s->h);
int minor = min(s->w, s->h);
int orientation = wide;
if (s->has_azimuth)
orientation = s->a;
/*
* divided by two to match visual scale of touch
@ -716,7 +753,8 @@ static void mt_complete_slot(struct mt_device *td, struct input_dev *input)
input_event(input, EV_ABS, ABS_MT_TOOL_Y, s->cy);
input_event(input, EV_ABS, ABS_MT_DISTANCE,
!s->touch_state);
input_event(input, EV_ABS, ABS_MT_ORIENTATION, wide);
input_event(input, EV_ABS, ABS_MT_ORIENTATION,
orientation);
input_event(input, EV_ABS, ABS_MT_PRESSURE, s->p);
input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
@ -734,10 +772,16 @@ static void mt_complete_slot(struct mt_device *td, struct input_dev *input)
*/
static void mt_sync_frame(struct mt_device *td, struct input_dev *input)
{
__s32 cls = td->mtclass.name;
if (cls == MT_CLS_WIN_8 || cls == MT_CLS_WIN_8_DUAL)
input_event(input, EV_KEY, BTN_LEFT, td->left_button_state);
input_mt_sync_frame(input);
input_event(input, EV_MSC, MSC_TIMESTAMP, td->timestamp);
input_sync(input);
td->num_received = 0;
td->left_button_state = 0;
if (test_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags))
set_bit(MT_IO_FLAGS_PENDING_SLOTS, &td->mt_io_flags);
else
@ -778,9 +822,11 @@ static int mt_touch_event(struct hid_device *hid, struct hid_field *field,
}
static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field,
struct hid_usage *usage, __s32 value)
struct hid_usage *usage, __s32 value,
bool first_packet)
{
struct mt_device *td = hid_get_drvdata(hid);
__s32 cls = td->mtclass.name;
__s32 quirks = td->mtclass.quirks;
struct input_dev *input = field->hidinput->input;
@ -832,11 +878,49 @@ static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field,
break;
case HID_DG_CONTACTCOUNT:
break;
case HID_DG_AZIMUTH:
/*
* Azimuth is counter-clockwise and ranges from [0, MAX)
* (a full revolution). Convert it to clockwise ranging
* [-MAX/2, MAX/2].
*
* Note that ABS_MT_ORIENTATION require us to report
* the limit of [-MAX/4, MAX/4], but the value can go
* out of range to [-MAX/2, MAX/2] to report an upside
* down ellipsis.
*/
if (value > field->logical_maximum / 2)
value -= field->logical_maximum;
td->curdata.a = -value;
td->curdata.has_azimuth = true;
break;
case HID_DG_TOUCH:
/* do nothing */
break;
default:
/*
* For Win8 PTP touchpads we should only look at
* non finger/touch events in the first_packet of
* a (possible) multi-packet frame.
*/
if ((cls == MT_CLS_WIN_8 || cls == MT_CLS_WIN_8_DUAL) &&
!first_packet)
return;
/*
* For Win8 PTP touchpads we map both the clickpad click
* and any "external" left buttons to BTN_LEFT if a
* device claims to have both we need to report 1 for
* BTN_LEFT if either is pressed, so we or all values
* together and report the result in mt_sync_frame().
*/
if ((cls == MT_CLS_WIN_8 || cls == MT_CLS_WIN_8_DUAL) &&
usage->type == EV_KEY && usage->code == BTN_LEFT) {
td->left_button_state |= value;
return;
}
if (usage->type)
input_event(input, usage->type, usage->code,
value);
@ -855,9 +939,11 @@ static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field,
static void mt_touch_report(struct hid_device *hid, struct hid_report *report)
{
struct mt_device *td = hid_get_drvdata(hid);
__s32 cls = td->mtclass.name;
struct hid_field *field;
bool first_packet;
unsigned count;
int r, n;
int r, n, scantime = 0;
/* sticky fingers release in progress, abort */
if (test_and_set_bit(MT_IO_FLAGS_RUNNING, &td->mt_io_flags))
@ -867,13 +953,31 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report)
* Includes multi-packet support where subsequent
* packets are sent with zero contactcount.
*/
if (td->scantime_index >= 0) {
field = report->field[td->scantime_index];
scantime = field->value[td->scantime_val_index];
}
if (td->cc_index >= 0) {
struct hid_field *field = report->field[td->cc_index];
int value = field->value[td->cc_value_index];
if (value)
/*
* For Win8 PTPs the first packet (td->num_received == 0) may
* have a contactcount of 0 if there only is a button event.
* We double check that this is not a continuation packet
* of a possible multi-packet frame be checking that the
* timestamp has changed.
*/
if ((cls == MT_CLS_WIN_8 || cls == MT_CLS_WIN_8_DUAL) &&
td->num_received == 0 && td->prev_scantime != scantime)
td->num_expected = value;
/* A non 0 contact count always indicates a first packet */
else if (value)
td->num_expected = value;
}
td->prev_scantime = scantime;
first_packet = td->num_received == 0;
for (r = 0; r < report->maxfield; r++) {
field = report->field[r];
count = field->report_count;
@ -883,7 +987,7 @@ static void mt_touch_report(struct hid_device *hid, struct hid_report *report)
for (n = 0; n < count; n++)
mt_process_mt_event(hid, field, &field->usage[n],
field->value[n]);
field->value[n], first_packet);
}
if (td->num_received >= td->num_expected)
@ -1329,6 +1433,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
td->maxcontact_report_id = -1;
td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN;
td->cc_index = -1;
td->scantime_index = -1;
td->mt_report_id = -1;
hid_set_drvdata(hdev, td);
@ -1649,14 +1754,6 @@ static const struct hid_device_id mt_devices[] = {
MT_USB_DEVICE(USB_VENDOR_ID_TURBOX,
USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART) },
/* Panasonic panels */
{ .driver_data = MT_CLS_PANASONIC,
MT_USB_DEVICE(USB_VENDOR_ID_PANASONIC,
USB_DEVICE_ID_PANABOARD_UBT780) },
{ .driver_data = MT_CLS_PANASONIC,
MT_USB_DEVICE(USB_VENDOR_ID_PANASONIC,
USB_DEVICE_ID_PANABOARD_UBT880) },
/* Novatek Panel */
{ .driver_data = MT_CLS_NSMU,
MT_USB_DEVICE(USB_VENDOR_ID_NOVATEK,
@ -1667,6 +1764,14 @@ static const struct hid_device_id mt_devices[] = {
HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8,
USB_VENDOR_ID_NTRIG, 0x1b05) },
/* Panasonic panels */
{ .driver_data = MT_CLS_PANASONIC,
MT_USB_DEVICE(USB_VENDOR_ID_PANASONIC,
USB_DEVICE_ID_PANABOARD_UBT780) },
{ .driver_data = MT_CLS_PANASONIC,
MT_USB_DEVICE(USB_VENDOR_ID_PANASONIC,
USB_DEVICE_ID_PANABOARD_UBT880) },
/* PixArt optical touch screen */
{ .driver_data = MT_CLS_INRANGE_CONTACTNUMBER,
MT_USB_DEVICE(USB_VENDOR_ID_PIXART,

1276
drivers/hid/hid-quirks.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -731,6 +731,7 @@ static const struct hid_device_id rmi_id[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_RAZER, USB_DEVICE_ID_RAZER_BLADE_14),
.driver_data = RMI_DEVICE_HAS_PHYS_BUTTONS },
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X1_COVER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_REZEL) },
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_RMI, HID_ANY_ID, HID_ANY_ID) },
{ }
};

View File

@ -37,6 +37,8 @@ static uint kovaplus_convert_event_cpi(uint value)
static void kovaplus_profile_activated(struct kovaplus_device *kovaplus,
uint new_profile_index)
{
if (new_profile_index >= ARRAY_SIZE(kovaplus->profile_settings))
return;
kovaplus->actual_profile = new_profile_index;
kovaplus->actual_cpi = kovaplus->profile_settings[new_profile_index].cpi_startup_level;
kovaplus->actual_x_sensitivity = kovaplus->profile_settings[new_profile_index].sensitivity_x;

View File

@ -473,6 +473,7 @@ struct motion_output_report_02 {
#define DS4_FEATURE_REPORT_0x02_SIZE 37
#define DS4_FEATURE_REPORT_0x05_SIZE 41
#define DS4_FEATURE_REPORT_0x81_SIZE 7
#define DS4_FEATURE_REPORT_0xA3_SIZE 49
#define DS4_INPUT_REPORT_0x11_SIZE 78
#define DS4_OUTPUT_REPORT_0x05_SIZE 32
#define DS4_OUTPUT_REPORT_0x11_SIZE 78
@ -544,6 +545,8 @@ struct sony_sc {
struct power_supply *battery;
struct power_supply_desc battery_desc;
int device_id;
unsigned fw_version;
unsigned hw_version;
u8 *output_report_dmabuf;
#ifdef CONFIG_SONY_FF
@ -627,6 +630,29 @@ static ssize_t ds4_store_poll_interval(struct device *dev,
static DEVICE_ATTR(bt_poll_interval, 0644, ds4_show_poll_interval,
ds4_store_poll_interval);
static ssize_t sony_show_firmware_version(struct device *dev,
struct device_attribute
*attr, char *buf)
{
struct hid_device *hdev = to_hid_device(dev);
struct sony_sc *sc = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "0x%04x\n", sc->fw_version);
}
static DEVICE_ATTR(firmware_version, 0444, sony_show_firmware_version, NULL);
static ssize_t sony_show_hardware_version(struct device *dev,
struct device_attribute
*attr, char *buf)
{
struct hid_device *hdev = to_hid_device(dev);
struct sony_sc *sc = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "0x%04x\n", sc->hw_version);
}
static DEVICE_ATTR(hardware_version, 0444, sony_show_hardware_version, NULL);
static u8 *motion_fixup(struct hid_device *hdev, u8 *rdesc,
unsigned int *rsize)
@ -1646,6 +1672,31 @@ static void dualshock4_calibration_work(struct work_struct *work)
spin_unlock_irqrestore(&sc->lock, flags);
}
static int dualshock4_get_version_info(struct sony_sc *sc)
{
u8 *buf;
int ret;
buf = kmalloc(DS4_FEATURE_REPORT_0xA3_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
ret = hid_hw_raw_request(sc->hdev, 0xA3, buf,
DS4_FEATURE_REPORT_0xA3_SIZE,
HID_FEATURE_REPORT,
HID_REQ_GET_REPORT);
if (ret < 0) {
kfree(buf);
return ret;
}
sc->hw_version = get_unaligned_le16(&buf[35]);
sc->fw_version = get_unaligned_le16(&buf[41]);
kfree(buf);
return 0;
}
static void sixaxis_set_leds_from_id(struct sony_sc *sc)
{
static const u8 sixaxis_leds[10][4] = {
@ -2399,10 +2450,7 @@ static int sony_check_add(struct sony_sc *sc)
memcpy(sc->mac_address, &buf[1], sizeof(sc->mac_address));
snprintf(sc->hdev->uniq, sizeof(sc->hdev->uniq),
"%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
sc->mac_address[5], sc->mac_address[4],
sc->mac_address[3], sc->mac_address[2],
sc->mac_address[1], sc->mac_address[0]);
"%pMR", sc->mac_address);
} else if ((sc->quirks & SIXAXIS_CONTROLLER_USB) ||
(sc->quirks & NAVIGATION_CONTROLLER_USB)) {
buf = kmalloc(SIXAXIS_REPORT_0xF2_SIZE, GFP_KERNEL);
@ -2432,10 +2480,7 @@ static int sony_check_add(struct sony_sc *sc)
sc->mac_address[5-n] = buf[4+n];
snprintf(sc->hdev->uniq, sizeof(sc->hdev->uniq),
"%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
sc->mac_address[5], sc->mac_address[4],
sc->mac_address[3], sc->mac_address[2],
sc->mac_address[1], sc->mac_address[0]);
"%pMR", sc->mac_address);
} else {
return 0;
}
@ -2619,6 +2664,28 @@ static int sony_input_configured(struct hid_device *hdev,
goto err_stop;
}
ret = dualshock4_get_version_info(sc);
if (ret < 0) {
hid_err(sc->hdev, "Failed to get version data from Dualshock 4\n");
goto err_stop;
}
ret = device_create_file(&sc->hdev->dev, &dev_attr_firmware_version);
if (ret) {
/* Make zero for cleanup reasons of sysfs entries. */
sc->fw_version = 0;
sc->hw_version = 0;
hid_err(sc->hdev, "can't create sysfs firmware_version attribute err: %d\n", ret);
goto err_stop;
}
ret = device_create_file(&sc->hdev->dev, &dev_attr_hardware_version);
if (ret) {
sc->hw_version = 0;
hid_err(sc->hdev, "can't create sysfs hardware_version attribute err: %d\n", ret);
goto err_stop;
}
/*
* The Dualshock 4 touchpad supports 2 touches and has a
* resolution of 1920x942 (44.86 dots/mm).
@ -2695,6 +2762,10 @@ err_stop:
*/
if (sc->ds4_bt_poll_interval)
device_remove_file(&sc->hdev->dev, &dev_attr_bt_poll_interval);
if (sc->fw_version)
device_remove_file(&sc->hdev->dev, &dev_attr_firmware_version);
if (sc->hw_version)
device_remove_file(&sc->hdev->dev, &dev_attr_hardware_version);
if (sc->quirks & SONY_LED_SUPPORT)
sony_leds_remove(sc);
if (sc->quirks & SONY_BATTERY_SUPPORT)
@ -2796,6 +2867,12 @@ static void sony_remove(struct hid_device *hdev)
if (sc->quirks & DUALSHOCK4_CONTROLLER_BT)
device_remove_file(&sc->hdev->dev, &dev_attr_bt_poll_interval);
if (sc->fw_version)
device_remove_file(&sc->hdev->dev, &dev_attr_firmware_version);
if (sc->hw_version)
device_remove_file(&sc->hdev->dev, &dev_attr_hardware_version);
sony_cancel_work_sync(sc);
kfree(sc->output_report_dmabuf);

View File

@ -934,11 +934,6 @@ static int i2c_hid_of_probe(struct i2c_client *client,
}
pdata->hid_descriptor_address = val;
ret = of_property_read_u32(dev->of_node, "post-power-on-delay-ms",
&val);
if (!ret)
pdata->post_power_delay_ms = val;
return 0;
}
@ -955,6 +950,16 @@ static inline int i2c_hid_of_probe(struct i2c_client *client,
}
#endif
static void i2c_hid_fwnode_probe(struct i2c_client *client,
struct i2c_hid_platform_data *pdata)
{
u32 val;
if (!device_property_read_u32(&client->dev, "post-power-on-delay-ms",
&val))
pdata->post_power_delay_ms = val;
}
static int i2c_hid_probe(struct i2c_client *client,
const struct i2c_device_id *dev_id)
{
@ -998,6 +1003,9 @@ static int i2c_hid_probe(struct i2c_client *client,
ihid->pdata = *platform_data;
}
/* 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);

View File

@ -28,6 +28,7 @@
#define SPT_Ax_DEVICE_ID 0x9D35
#define CNL_Ax_DEVICE_ID 0x9DFC
#define GLK_Ax_DEVICE_ID 0x31A2
#define CNL_H_DEVICE_ID 0xA37C
#define REVISION_ID_CHT_A0 0x6
#define REVISION_ID_CHT_Ax_SI 0x0

View File

@ -37,6 +37,7 @@ static const struct pci_device_id ish_pci_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_Ax_DEVICE_ID)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_Ax_DEVICE_ID)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, GLK_Ax_DEVICE_ID)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_H_DEVICE_ID)},
{0, }
};
MODULE_DEVICE_TABLE(pci, ish_pci_tbl);

View File

@ -3,7 +3,7 @@
# Makefile for the USB input drivers
#
usbhid-y := hid-core.o hid-quirks.o
usbhid-y := hid-core.o
usbhid-$(CONFIG_USB_HIDDEV) += hiddev.o
usbhid-$(CONFIG_HID_PID) += hid-pidff.o

View File

@ -978,8 +978,7 @@ static int usbhid_parse(struct hid_device *hid)
int num_descriptors;
size_t offset = offsetof(struct hid_descriptor, desc);
quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
quirks = hid_lookup_quirk(hid);
if (quirks & HID_QUIRK_IGNORE)
return -ENODEV;
@ -1328,8 +1327,8 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *
hid->bus = BUS_USB;
hid->vendor = le16_to_cpu(dev->descriptor.idVendor);
hid->product = le16_to_cpu(dev->descriptor.idProduct);
hid->version = le16_to_cpu(dev->descriptor.bcdDevice);
hid->name[0] = 0;
hid->quirks = usbhid_lookup_quirk(hid->vendor, hid->product);
if (intf->cur_altsetting->desc.bInterfaceProtocol ==
USB_INTERFACE_PROTOCOL_MOUSE)
hid->type = HID_TYPE_USBMOUSE;
@ -1641,7 +1640,7 @@ static int __init hid_init(void)
{
int retval = -ENOMEM;
retval = usbhid_quirks_init(quirks_param);
retval = hid_quirks_init(quirks_param, BUS_USB, MAX_USBHID_BOOT_QUIRKS);
if (retval)
goto usbhid_quirks_init_fail;
retval = usb_register(&hid_driver);
@ -1651,7 +1650,7 @@ static int __init hid_init(void)
return 0;
usb_register_fail:
usbhid_quirks_exit();
hid_quirks_exit(BUS_USB);
usbhid_quirks_init_fail:
return retval;
}
@ -1659,7 +1658,7 @@ usbhid_quirks_init_fail:
static void __exit hid_exit(void)
{
usb_deregister(&hid_driver);
usbhid_quirks_exit();
hid_quirks_exit(BUS_USB);
}
module_init(hid_init);

View File

@ -1,402 +0,0 @@
/*
* USB HID quirks support for Linux
*
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
* Copyright (c) 2007 Paul Walmsley
*/
/*
* 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/export.h>
#include <linux/slab.h>
#include "../hid-ids.h"
/*
* Alphabetically sorted blacklist by quirk type.
*/
static const struct hid_blacklist {
__u16 idVendor;
__u16 idProduct;
__u32 quirks;
} hid_blacklist[] = {
{ USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD, HID_QUIRK_BADPAD },
{ USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD },
{ USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD },
{ USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD },
{ USB_VENDOR_ID_DWAV, USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER, HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET },
{ USB_VENDOR_ID_MOJO, USB_DEVICE_ID_RETRO_ADAPTER, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_NATSU, USB_DEVICE_ID_NATSU_GAMEPAD, HID_QUIRK_BADPAD },
{ USB_VENDOR_ID_NEC, USB_DEVICE_ID_NEC_USB_GAME_PAD, HID_QUIRK_BADPAD },
{ USB_VENDOR_ID_NEXTWINDOW, USB_DEVICE_ID_NEXTWINDOW_TOUCHSCREEN, HID_QUIRK_MULTI_INPUT},
{ USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
{ USB_VENDOR_ID_AFATECH, USB_DEVICE_ID_AFATECH_AF9016, HID_QUIRK_FULLSPEED_INTERVAL },
{ USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_ETURBOTOUCH, USB_DEVICE_ID_ETURBOTOUCH, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_ETURBOTOUCH, USB_DEVICE_ID_ETURBOTOUCH_2968, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_GREENASIA, USB_DEVICE_ID_GREENASIA_DUAL_USB_JOYPAD, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS },
{ USB_VENDOR_ID_TOUCHPACK, USB_DEVICE_ID_TOUCHPACK_RTS, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_AIREN, USB_DEVICE_ID_AIREN_SLIMPLUS, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_AKAI, USB_DEVICE_ID_AKAI_MPKMINI2, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_AKAI_09E8, USB_DEVICE_ID_AKAI_09E8_MIDIMIX, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_AMI, USB_DEVICE_ID_AMI_VIRT_KEYBOARD_AND_MOUSE, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS682, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS692, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS1758, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FIGHTERSTICK, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_COMBATSTICK, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FLIGHT_SIM_ECLIPSE_YOKE, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FLIGHT_SIM_YOKE, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_PRO_THROTTLE, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_PRO_PEDALS, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_3AXIS_5BUTTON_STICK, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K70R, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_M65RGB, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K95RGB, HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K70RGB, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K65RGB, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_STRAFE, HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K70RGB_RAPIDFIRE, HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K65RGB_RAPIDFIRE, HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_SCIMITAR_PRO_RGB, HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB_OMNI_SURROUND_51, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_PIXART_USB_OPTICAL_MOUSE, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_WIIU, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_PS3, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_DOLPHINBAR, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE1, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_ELAN, HID_ANY_ID, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_FUTABA, USB_DEVICE_ID_LED_DISPLAY, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0A4A, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0B4A, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_IDEACOM, USB_DEVICE_ID_IDEACOM_IDC6680, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_C007, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_C077, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KEYBOARD_G710_PLUS, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_C01A, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_C05A, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_C06A, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_PRO_2, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_2, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TOUCH_COVER_2, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_1610, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_PENMOUNT, USB_DEVICE_ID_PENMOUNT_1640, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE_ID2, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_MOUSE_4D22, HID_QUIRK_ALWAYS_POLL },
{ USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3003, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_REALTEK, USB_DEVICE_ID_REALTEK_READER, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_SENNHEISER, USB_DEVICE_ID_SENNHEISER_BTD500USB, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_SIGMATEL, USB_DEVICE_ID_SIGMATEL_STMP3780, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS9200_TOUCH, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS817_TOUCH, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS_TS, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_SIS_TOUCH, USB_DEVICE_ID_SIS1030_TOUCH, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_SYMBOL, USB_DEVICE_ID_SYMBOL_SCANNER_1, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_SYMBOL, USB_DEVICE_ID_SYMBOL_SCANNER_2, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_TPV, USB_DEVICE_ID_TPV_OPTICAL_TOUCHSCREEN_8882, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_TPV, USB_DEVICE_ID_TPV_OPTICAL_TOUCHSCREEN_8883, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_KNA5, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWA60, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_WISEGROUP_LTD2, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_PI_ENGINEERING, USB_DEVICE_ID_PI_ENGINEERING_VEC_USB_FOOTPEDAL, HID_QUIRK_HIDINPUT_FORCE },
{ USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_MULTI_TOUCH, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_SIGMA_MICRO, USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_M912, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD2, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS1, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_LTS2, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_HD, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_QUAD_HD, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP_V103, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD_A096, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_MULTIPLE_1781, USB_DEVICE_ID_RAPHNET_4NES4SNES_OLD, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_DRACAL_RAPHNET, USB_DEVICE_ID_RAPHNET_2NES2SNES, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_DRACAL_RAPHNET, USB_DEVICE_ID_RAPHNET_4NES4SNES, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_INNOMEDIA, USB_DEVICE_ID_INNEX_GENESIS_ATARI, HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_MCS, USB_DEVICE_ID_MCS_GAMEPADBLOCK, HID_QUIRK_MULTI_INPUT },
{ 0, 0 }
};
/* Dynamic HID quirks list - specified at runtime */
struct quirks_list_struct {
struct hid_blacklist hid_bl_item;
struct list_head node;
};
static LIST_HEAD(dquirks_list);
static DECLARE_RWSEM(dquirks_rwsem);
/* Runtime ("dynamic") quirks manipulation functions */
/**
* usbhid_exists_dquirk: find any dynamic quirks for a USB HID device
* @idVendor: the 16-bit USB vendor ID, in native byteorder
* @idProduct: the 16-bit USB product ID, in native byteorder
*
* Description:
* Scans dquirks_list for a matching dynamic quirk and returns
* the pointer to the relevant struct hid_blacklist if found.
* Must be called with a read lock held on dquirks_rwsem.
*
* Returns: NULL if no quirk found, struct hid_blacklist * if found.
*/
static struct hid_blacklist *usbhid_exists_dquirk(const u16 idVendor,
const u16 idProduct)
{
struct quirks_list_struct *q;
struct hid_blacklist *bl_entry = NULL;
list_for_each_entry(q, &dquirks_list, node) {
if (q->hid_bl_item.idVendor == idVendor &&
q->hid_bl_item.idProduct == idProduct) {
bl_entry = &q->hid_bl_item;
break;
}
}
if (bl_entry != NULL)
dbg_hid("Found dynamic quirk 0x%x for USB HID vendor 0x%hx prod 0x%hx\n",
bl_entry->quirks, bl_entry->idVendor,
bl_entry->idProduct);
return bl_entry;
}
/**
* usbhid_modify_dquirk: add/replace a HID quirk
* @idVendor: the 16-bit USB vendor ID, in native byteorder
* @idProduct: the 16-bit USB product ID, in native byteorder
* @quirks: the u32 quirks value to add/replace
*
* Description:
* If an dynamic quirk exists in memory for this (idVendor,
* idProduct) pair, replace its quirks value with what was
* provided. Otherwise, add the quirk to the dynamic quirks list.
*
* Returns: 0 OK, -error on failure.
*/
static int usbhid_modify_dquirk(const u16 idVendor, const u16 idProduct,
const u32 quirks)
{
struct quirks_list_struct *q_new, *q;
int list_edited = 0;
if (!idVendor) {
dbg_hid("Cannot add a quirk with idVendor = 0\n");
return -EINVAL;
}
q_new = kmalloc(sizeof(struct quirks_list_struct), GFP_KERNEL);
if (!q_new)
return -ENOMEM;
q_new->hid_bl_item.idVendor = idVendor;
q_new->hid_bl_item.idProduct = idProduct;
q_new->hid_bl_item.quirks = quirks;
down_write(&dquirks_rwsem);
list_for_each_entry(q, &dquirks_list, node) {
if (q->hid_bl_item.idVendor == idVendor &&
q->hid_bl_item.idProduct == idProduct) {
list_replace(&q->node, &q_new->node);
kfree(q);
list_edited = 1;
break;
}
}
if (!list_edited)
list_add_tail(&q_new->node, &dquirks_list);
up_write(&dquirks_rwsem);
return 0;
}
/**
* usbhid_remove_all_dquirks: remove all runtime HID quirks from memory
*
* Description:
* Free all memory associated with dynamic quirks - called before
* module unload.
*
*/
static void usbhid_remove_all_dquirks(void)
{
struct quirks_list_struct *q, *temp;
down_write(&dquirks_rwsem);
list_for_each_entry_safe(q, temp, &dquirks_list, node) {
list_del(&q->node);
kfree(q);
}
up_write(&dquirks_rwsem);
}
/**
* usbhid_quirks_init: apply USB HID quirks specified at module load time
*/
int usbhid_quirks_init(char **quirks_param)
{
u16 idVendor, idProduct;
u32 quirks;
int n = 0, m;
for (; n < MAX_USBHID_BOOT_QUIRKS && quirks_param[n]; n++) {
m = sscanf(quirks_param[n], "0x%hx:0x%hx:0x%x",
&idVendor, &idProduct, &quirks);
if (m != 3 ||
usbhid_modify_dquirk(idVendor, idProduct, quirks) != 0) {
pr_warn("Could not parse HID quirk module param %s\n",
quirks_param[n]);
}
}
return 0;
}
/**
* usbhid_quirks_exit: release memory associated with dynamic_quirks
*
* Description:
* Release all memory associated with dynamic quirks. Called upon
* module unload.
*
* Returns: nothing
*/
void usbhid_quirks_exit(void)
{
usbhid_remove_all_dquirks();
}
/**
* usbhid_exists_squirk: return any static quirks for a USB HID device
* @idVendor: the 16-bit USB vendor ID, in native byteorder
* @idProduct: the 16-bit USB product ID, in native byteorder
*
* Description:
* Given a USB vendor ID and product ID, return a pointer to
* the hid_blacklist entry associated with that device.
*
* Returns: pointer if quirk found, or NULL if no quirks found.
*/
static const struct hid_blacklist *usbhid_exists_squirk(const u16 idVendor,
const u16 idProduct)
{
const struct hid_blacklist *bl_entry = NULL;
int n = 0;
for (; hid_blacklist[n].idVendor; n++)
if (hid_blacklist[n].idVendor == idVendor &&
(hid_blacklist[n].idProduct == (__u16) HID_ANY_ID ||
hid_blacklist[n].idProduct == idProduct))
bl_entry = &hid_blacklist[n];
if (bl_entry != NULL)
dbg_hid("Found squirk 0x%x for USB HID vendor 0x%hx prod 0x%hx\n",
bl_entry->quirks, bl_entry->idVendor,
bl_entry->idProduct);
return bl_entry;
}
/**
* usbhid_lookup_quirk: return any quirks associated with a USB HID device
* @idVendor: the 16-bit USB vendor ID, in native byteorder
* @idProduct: the 16-bit USB product ID, in native byteorder
*
* Description:
* Given a USB vendor ID and product ID, return any quirks associated
* with that device.
*
* Returns: a u32 quirks value.
*/
u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct)
{
u32 quirks = 0;
const struct hid_blacklist *bl_entry = NULL;
/* NCR devices must not be queried for reports */
if (idVendor == USB_VENDOR_ID_NCR &&
idProduct >= USB_DEVICE_ID_NCR_FIRST &&
idProduct <= USB_DEVICE_ID_NCR_LAST)
return HID_QUIRK_NO_INIT_REPORTS;
down_read(&dquirks_rwsem);
bl_entry = usbhid_exists_dquirk(idVendor, idProduct);
if (!bl_entry)
bl_entry = usbhid_exists_squirk(idVendor, idProduct);
if (bl_entry)
quirks = bl_entry->quirks;
up_read(&dquirks_rwsem);
return quirks;
}
EXPORT_SYMBOL_GPL(usbhid_lookup_quirk);

View File

@ -56,6 +56,107 @@ static int wacom_set_report(struct hid_device *hdev, u8 type, u8 *buf,
return retval;
}
static void wacom_wac_queue_insert(struct hid_device *hdev,
struct kfifo_rec_ptr_2 *fifo,
u8 *raw_data, int size)
{
bool warned = false;
while (kfifo_avail(fifo) < size) {
if (!warned)
hid_warn(hdev, "%s: kfifo has filled, starting to drop events\n", __func__);
warned = true;
kfifo_skip(fifo);
}
kfifo_in(fifo, raw_data, size);
}
static void wacom_wac_queue_flush(struct hid_device *hdev,
struct kfifo_rec_ptr_2 *fifo)
{
while (!kfifo_is_empty(fifo)) {
u8 buf[WACOM_PKGLEN_MAX];
int size;
int err;
size = kfifo_out(fifo, buf, sizeof(buf));
err = hid_report_raw_event(hdev, HID_INPUT_REPORT, buf, size, false);
if (err) {
hid_warn(hdev, "%s: unable to flush event due to error %d\n",
__func__, err);
}
}
}
static int wacom_wac_pen_serial_enforce(struct hid_device *hdev,
struct hid_report *report, u8 *raw_data, int size)
{
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
struct wacom_features *features = &wacom_wac->features;
bool flush = false;
bool insert = false;
int i, j;
if (wacom_wac->serial[0] || !(features->quirks & WACOM_QUIRK_TOOLSERIAL))
return 0;
/* Queue events which have invalid tool type or serial number */
for (i = 0; i < report->maxfield; i++) {
for (j = 0; j < report->field[i]->maxusage; j++) {
struct hid_field *field = report->field[i];
struct hid_usage *usage = &field->usage[j];
unsigned int equivalent_usage = wacom_equivalent_usage(usage->hid);
unsigned int offset;
unsigned int size;
unsigned int value;
if (equivalent_usage != HID_DG_INRANGE &&
equivalent_usage != HID_DG_TOOLSERIALNUMBER &&
equivalent_usage != WACOM_HID_WD_SERIALHI &&
equivalent_usage != WACOM_HID_WD_TOOLTYPE)
continue;
offset = field->report_offset;
size = field->report_size;
value = hid_field_extract(hdev, raw_data+1, offset + j * size, size);
/* If we go out of range, we need to flush the queue ASAP */
if (equivalent_usage == HID_DG_INRANGE)
value = !value;
if (value) {
flush = true;
switch (equivalent_usage) {
case HID_DG_TOOLSERIALNUMBER:
wacom_wac->serial[0] = value;
break;
case WACOM_HID_WD_SERIALHI:
wacom_wac->serial[0] |= ((__u64)value) << 32;
break;
case WACOM_HID_WD_TOOLTYPE:
wacom_wac->id[0] = value;
break;
}
}
else {
insert = true;
}
}
}
if (flush)
wacom_wac_queue_flush(hdev, &wacom_wac->pen_fifo);
else if (insert)
wacom_wac_queue_insert(hdev, &wacom_wac->pen_fifo, raw_data, size);
return insert && !flush;
}
static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
u8 *raw_data, int size)
{
@ -64,6 +165,9 @@ static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
if (size > WACOM_PKGLEN_MAX)
return 1;
if (wacom_wac_pen_serial_enforce(hdev, report, raw_data, size))
return -1;
memcpy(wacom->wacom_wac.data, raw_data, size);
wacom_wac_irq(&wacom->wacom_wac, size);
@ -2347,23 +2451,23 @@ static void wacom_remote_destroy_one(struct wacom *wacom, unsigned int index)
int i;
unsigned long flags;
spin_lock_irqsave(&remote->remote_lock, flags);
remote->remotes[index].registered = false;
spin_unlock_irqrestore(&remote->remote_lock, flags);
if (remote->remotes[index].battery.battery)
devres_release_group(&wacom->hdev->dev,
&remote->remotes[index].battery.bat_desc);
if (remote->remotes[index].group.name)
devres_release_group(&wacom->hdev->dev,
&remote->remotes[index]);
for (i = 0; i < WACOM_MAX_REMOTES; i++) {
if (remote->remotes[i].serial == serial) {
spin_lock_irqsave(&remote->remote_lock, flags);
remote->remotes[i].registered = false;
spin_unlock_irqrestore(&remote->remote_lock, flags);
if (remote->remotes[i].battery.battery)
devres_release_group(&wacom->hdev->dev,
&remote->remotes[i].battery.bat_desc);
if (remote->remotes[i].group.name)
devres_release_group(&wacom->hdev->dev,
&remote->remotes[i]);
remote->remotes[i].serial = 0;
remote->remotes[i].group.name = NULL;
remote->remotes[i].registered = false;
remote->remotes[i].battery.battery = NULL;
wacom->led.groups[i].select = WACOM_STATUS_UNKNOWN;
}
@ -2580,6 +2684,10 @@ static int wacom_probe(struct hid_device *hdev,
goto fail;
}
error = kfifo_alloc(&wacom_wac->pen_fifo, WACOM_PKGLEN_MAX, GFP_KERNEL);
if (error)
goto fail;
wacom_wac->hid_data.inputmode = -1;
wacom_wac->mode_report = -1;
@ -2643,6 +2751,8 @@ static void wacom_remove(struct hid_device *hdev)
if (wacom->wacom_wac.features.type != REMOTE)
wacom_release_resources(wacom);
kfifo_free(&wacom_wac->pen_fifo);
hid_set_drvdata(hdev, NULL);
}

View File

@ -1924,7 +1924,6 @@ static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field
struct wacom_features *features = &wacom_wac->features;
unsigned equivalent_usage = wacom_equivalent_usage(usage->hid);
int i;
bool is_touch_on = value;
bool do_report = false;
/*
@ -1969,16 +1968,17 @@ static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field
break;
case WACOM_HID_WD_MUTE_DEVICE:
if (wacom_wac->shared->touch_input && value) {
wacom_wac->shared->is_touch_on = !wacom_wac->shared->is_touch_on;
is_touch_on = wacom_wac->shared->is_touch_on;
}
/* fall through*/
case WACOM_HID_WD_TOUCHONOFF:
if (wacom_wac->shared->touch_input) {
bool *is_touch_on = &wacom_wac->shared->is_touch_on;
if (equivalent_usage == WACOM_HID_WD_MUTE_DEVICE && value)
*is_touch_on = !(*is_touch_on);
else if (equivalent_usage == WACOM_HID_WD_TOUCHONOFF)
*is_touch_on = value;
input_report_switch(wacom_wac->shared->touch_input,
SW_MUTE_DEVICE, !is_touch_on);
SW_MUTE_DEVICE, !(*is_touch_on));
input_sync(wacom_wac->shared->touch_input);
}
break;
@ -2085,7 +2085,29 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev,
wacom_map_usage(input, usage, field, EV_KEY, BTN_STYLUS2, 0);
break;
case HID_DG_TOOLSERIALNUMBER:
features->quirks |= WACOM_QUIRK_TOOLSERIAL;
wacom_map_usage(input, usage, field, EV_MSC, MSC_SERIAL, 0);
/* Adjust AES usages to match modern convention */
if (usage->hid == WACOM_HID_WT_SERIALNUMBER && field->report_size == 16) {
if (field->index + 2 < field->report->maxfield) {
struct hid_field *a = field->report->field[field->index + 1];
struct hid_field *b = field->report->field[field->index + 2];
if (a->maxusage > 0 && a->usage[0].hid == HID_DG_TOOLSERIALNUMBER && a->report_size == 32 &&
b->maxusage > 0 && b->usage[0].hid == 0xFF000000 && b->report_size == 8) {
features->quirks |= WACOM_QUIRK_AESPEN;
usage->hid = WACOM_HID_WD_TOOLTYPE;
field->logical_minimum = S16_MIN;
field->logical_maximum = S16_MAX;
a->logical_minimum = S32_MIN;
a->logical_maximum = S32_MAX;
b->usage[0].hid = WACOM_HID_WD_SERIALHI;
b->logical_minimum = 0;
b->logical_maximum = U8_MAX;
}
}
}
break;
case WACOM_HID_WD_SENSE:
features->quirks |= WACOM_QUIRK_SENSE;
@ -2093,15 +2115,18 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev,
break;
case WACOM_HID_WD_SERIALHI:
wacom_map_usage(input, usage, field, EV_ABS, ABS_MISC, 0);
set_bit(EV_KEY, input->evbit);
input_set_capability(input, EV_KEY, BTN_TOOL_PEN);
input_set_capability(input, EV_KEY, BTN_TOOL_RUBBER);
input_set_capability(input, EV_KEY, BTN_TOOL_BRUSH);
input_set_capability(input, EV_KEY, BTN_TOOL_PENCIL);
input_set_capability(input, EV_KEY, BTN_TOOL_AIRBRUSH);
if (!(features->device_type & WACOM_DEVICETYPE_DIRECT)) {
input_set_capability(input, EV_KEY, BTN_TOOL_MOUSE);
input_set_capability(input, EV_KEY, BTN_TOOL_LENS);
if (!(features->quirks & WACOM_QUIRK_AESPEN)) {
set_bit(EV_KEY, input->evbit);
input_set_capability(input, EV_KEY, BTN_TOOL_PEN);
input_set_capability(input, EV_KEY, BTN_TOOL_RUBBER);
input_set_capability(input, EV_KEY, BTN_TOOL_BRUSH);
input_set_capability(input, EV_KEY, BTN_TOOL_PENCIL);
input_set_capability(input, EV_KEY, BTN_TOOL_AIRBRUSH);
if (!(features->device_type & WACOM_DEVICETYPE_DIRECT)) {
input_set_capability(input, EV_KEY, BTN_TOOL_MOUSE);
input_set_capability(input, EV_KEY, BTN_TOOL_LENS);
}
}
break;
case WACOM_HID_WD_FINGERWHEEL:
@ -4390,6 +4415,12 @@ static const struct wacom_features wacom_features_0x360 =
static const struct wacom_features wacom_features_0x361 =
{ "Wacom Intuos Pro L", 62200, 43200, 8191, 63,
INTUOSP2_BT, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, 9, .touch_max = 10 };
static const struct wacom_features wacom_features_0x37A =
{ "Wacom One by Wacom S", 15200, 9500, 2047, 63,
BAMBOO_PEN, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0x37B =
{ "Wacom One by Wacom M", 21600, 13500, 2047, 63,
BAMBOO_PEN, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_HID_ANY_ID =
{ "Wacom HID", .type = HID_GENERIC, .oVid = HID_ANY_ID, .oPid = HID_ANY_ID };
@ -4558,6 +4589,8 @@ const struct hid_device_id wacom_ids[] = {
{ USB_DEVICE_WACOM(0x343) },
{ BT_DEVICE_WACOM(0x360) },
{ BT_DEVICE_WACOM(0x361) },
{ USB_DEVICE_WACOM(0x37A) },
{ USB_DEVICE_WACOM(0x37B) },
{ USB_DEVICE_WACOM(0x4001) },
{ USB_DEVICE_WACOM(0x4004) },
{ USB_DEVICE_WACOM(0x5000) },

View File

@ -11,6 +11,7 @@
#include <linux/types.h>
#include <linux/hid.h>
#include <linux/kfifo.h>
/* maximum packet length for USB/BT devices */
#define WACOM_PKGLEN_MAX 361
@ -86,7 +87,9 @@
/* device quirks */
#define WACOM_QUIRK_BBTOUCH_LOWRES 0x0001
#define WACOM_QUIRK_SENSE 0x0002
#define WACOM_QUIRK_AESPEN 0x0004
#define WACOM_QUIRK_BATTERY 0x0008
#define WACOM_QUIRK_TOOLSERIAL 0x0010
/* device types */
#define WACOM_DEVICETYPE_NONE 0x0000
@ -107,6 +110,7 @@
#define WACOM_HID_WD_PEN (WACOM_HID_UP_WACOMDIGITIZER | 0x02)
#define WACOM_HID_WD_SENSE (WACOM_HID_UP_WACOMDIGITIZER | 0x36)
#define WACOM_HID_WD_DIGITIZERFNKEYS (WACOM_HID_UP_WACOMDIGITIZER | 0x39)
#define WACOM_HID_WD_SERIALNUMBER (WACOM_HID_UP_WACOMDIGITIZER | 0x5b)
#define WACOM_HID_WD_SERIALHI (WACOM_HID_UP_WACOMDIGITIZER | 0x5c)
#define WACOM_HID_WD_TOOLTYPE (WACOM_HID_UP_WACOMDIGITIZER | 0x77)
#define WACOM_HID_WD_DISTANCE (WACOM_HID_UP_WACOMDIGITIZER | 0x0132)
@ -150,6 +154,7 @@
#define WACOM_HID_WT_TOUCHSCREEN (WACOM_HID_UP_WACOMTOUCH | 0x04)
#define WACOM_HID_WT_TOUCHPAD (WACOM_HID_UP_WACOMTOUCH | 0x05)
#define WACOM_HID_WT_CONTACTMAX (WACOM_HID_UP_WACOMTOUCH | 0x55)
#define WACOM_HID_WT_SERIALNUMBER (WACOM_HID_UP_WACOMTOUCH | 0x5b)
#define WACOM_HID_WT_X (WACOM_HID_UP_WACOMTOUCH | 0x130)
#define WACOM_HID_WT_Y (WACOM_HID_UP_WACOMTOUCH | 0x131)
@ -336,6 +341,7 @@ struct wacom_wac {
struct input_dev *pen_input;
struct input_dev *touch_input;
struct input_dev *pad_input;
struct kfifo_rec_ptr_2 pen_fifo;
int pid;
int num_contacts_left;
u8 bt_features;

View File

@ -281,6 +281,7 @@ struct hid_item {
#define HID_DG_DEVICECONFIG 0x000d000e
#define HID_DG_DEVICESETTINGS 0x000d0023
#define HID_DG_AZIMUTH 0x000d003f
#define HID_DG_CONFIDENCE 0x000d0047
#define HID_DG_WIDTH 0x000d0048
#define HID_DG_HEIGHT 0x000d0049
@ -342,6 +343,7 @@ struct hid_item {
#define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000
#define HID_QUIRK_SKIP_OUTPUT_REPORT_ID 0x00020000
#define HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP 0x00040000
#define HID_QUIRK_HAVE_SPECIAL_DRIVER 0x00080000
#define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000
#define HID_QUIRK_NO_INIT_REPORTS 0x20000000
#define HID_QUIRK_NO_IGNORE 0x40000000
@ -671,6 +673,7 @@ struct hid_usage_id {
* to be called)
* @dyn_list: list of dynamically added device ids
* @dyn_lock: lock protecting @dyn_list
* @match: check if the given device is handled by this driver
* @probe: new device inserted
* @remove: device removed (NULL if not a hot-plug capable driver)
* @report_table: on which reports to call raw_event (NULL means all)
@ -683,6 +686,8 @@ struct hid_usage_id {
* @input_mapped: invoked on input registering after mapping an usage
* @input_configured: invoked just before the device is registered
* @feature_mapping: invoked on feature registering
* @bus_add_driver: invoked when a HID driver is about to be added
* @bus_removed_driver: invoked when a HID driver has been removed
* @suspend: invoked on suspend (NULL means nop)
* @resume: invoked on resume if device was not reset (NULL means nop)
* @reset_resume: invoked on resume if device was reset (NULL means nop)
@ -711,6 +716,7 @@ struct hid_driver {
struct list_head dyn_list;
spinlock_t dyn_lock;
bool (*match)(struct hid_device *dev, bool ignore_special_driver);
int (*probe)(struct hid_device *dev, const struct hid_device_id *id);
void (*remove)(struct hid_device *dev);
@ -736,6 +742,8 @@ struct hid_driver {
void (*feature_mapping)(struct hid_device *hdev,
struct hid_field *field,
struct hid_usage *usage);
void (*bus_add_driver)(struct hid_driver *driver);
void (*bus_removed_driver)(struct hid_driver *driver);
#ifdef CONFIG_PM
int (*suspend)(struct hid_device *hdev, pm_message_t message);
int (*resume)(struct hid_device *hdev);
@ -814,6 +822,8 @@ extern bool hid_ignore(struct hid_device *);
extern int hid_add_device(struct hid_device *);
extern void hid_destroy_device(struct hid_device *);
extern struct bus_type hid_bus_type;
extern int __must_check __hid_register_driver(struct hid_driver *,
struct module *, const char *mod_name);
@ -860,8 +870,12 @@ int hid_open_report(struct hid_device *device);
int hid_check_keys_pressed(struct hid_device *hid);
int hid_connect(struct hid_device *hid, unsigned int connect_mask);
void hid_disconnect(struct hid_device *hid);
const struct hid_device_id *hid_match_id(struct hid_device *hdev,
bool hid_match_one_id(const struct hid_device *hdev,
const struct hid_device_id *id);
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);
s32 hid_snto32(__u32 value, unsigned n);
__u32 hid_field_extract(const struct hid_device *hid, __u8 *report,
unsigned offset, unsigned n);
@ -1098,9 +1112,9 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size,
int interrupt);
/* HID quirks API */
u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct);
int usbhid_quirks_init(char **quirks_param);
void usbhid_quirks_exit(void);
unsigned long hid_lookup_quirk(const struct hid_device *hdev);
int hid_quirks_init(char **quirks_param, __u16 bus, int count);
void hid_quirks_exit(__u16 bus);
#ifdef CONFIG_HID_PID
int hid_pidff_init(struct hid_device *hid);

View File

@ -789,7 +789,7 @@ static int hidp_setup_hid(struct hidp_session *session,
hid->dev.parent = &session->conn->hcon->dev;
hid->ll_driver = &hidp_hid_driver;
/* True if device is blacklisted in drivers/hid/hid-core.c */
/* True if device is blacklisted in drivers/hid/hid-quirks.c */
if (hid_ignore(hid)) {
hid_destroy_device(session->hid);
session->hid = NULL;