提交 c6400e5c 编写于 作者: L Linus Torvalds

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

Pull HID updates from Jiri Kosina:

 - support for Pro Pen slim, from Jason Gerecke

 - power management improvements to Intel-ISH driver, from Song Hongyan

 - UCLogic driver revamp in order to be able to support wider range of
   Huion tablets, from Nikolai Kondrashov

 - Asus Transbook support, from NOGUCHI Hiroshi

 - other assorted small bugfixes / cleanups and device ID additions

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (46 commits)
  HID: Remove Waltop tablets from hid_have_special_driver
  HID: Remove KYE tablets from hid_have_special_driver
  HID: Remove hid-uclogic entries from hid_have_special_driver
  HID: uclogic: Do not initialize non-USB devices
  HID: uclogic: Add support for Ugee G5
  HID: uclogic: Support Gray-coded rotary encoders
  HID: uclogic: Support faking Wacom pad device ID
  HID: uclogic: Add support for XP-Pen Deco 01
  HID: uclogic: Add support for XP-Pen Star G640
  HID: uclogic: Add support for XP-Pen Star G540
  HID: uclogic: Add support for Ugee EX07S frame controls
  HID: uclogic: Add support for Ugee M540
  HID: uclogic: Add support for Ugee 2150
  HID: uclogic: Support v2 protocol
  HID: uclogic: Support fragmented high-res reports
  HID: uclogic: Support in-range reporting emulation
  HID: uclogic: Designate current protocol v1
  HID: uclogic: Re-initialize tablets on resume
  HID: uclogic: Extract tablet parameter discovery into a module
  HID: uclogic: Extract report descriptors to a module
  ...
......@@ -412,6 +412,12 @@ config HID_WALTOP
---help---
Support for Waltop tablets.
config HID_VIEWSONIC
tristate "ViewSonic/Signotec"
depends on HID
help
Support for ViewSonic/Signotec PD1011 signature pad.
config HID_GYRATION
tristate "Gyration remote control"
depends on HID
......@@ -590,6 +596,13 @@ config HID_MAGICMOUSE
Say Y here if you want support for the multi-touch features of the
Apple Wireless "Magic" Mouse and the Apple Wireless "Magic" Trackpad.
config HID_MALTRON
tristate "Maltron L90 keyboard"
depends on HID
---help---
Adds support for the volume up, volume down, mute, and play/pause buttons
of the Maltron L90 keyboard.
config HID_MAYFLASH
tristate "Mayflash game controller adapter force feedback"
depends on HID
......
......@@ -66,6 +66,7 @@ obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o
obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o
obj-$(CONFIG_HID_LOGITECH_HIDPP) += hid-logitech-hidpp.o
obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o
obj-$(CONFIG_HID_MALTRON) += hid-maltron.o
obj-$(CONFIG_HID_MAYFLASH) += hid-mf.o
obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o
obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o
......@@ -108,12 +109,16 @@ obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o
obj-$(CONFIG_HID_TIVO) += hid-tivo.o
obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o
obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o
hid-uclogic-objs := hid-uclogic-core.o \
hid-uclogic-rdesc.o \
hid-uclogic-params.o
obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o
obj-$(CONFIG_HID_UDRAW_PS3) += hid-udraw-ps3.o
obj-$(CONFIG_HID_LED) += hid-led.o
obj-$(CONFIG_HID_XINMO) += hid-xinmo.o
obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o
obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o
obj-$(CONFIG_HID_VIEWSONIC) += hid-viewsonic.o
wacom-objs := wacom_wac.o wacom_sys.o
obj-$(CONFIG_HID_WACOM) += wacom.o
......
......@@ -32,6 +32,7 @@
#include <linux/platform_data/x86/asus-wmi.h>
#include <linux/input/mt.h>
#include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */
#include <linux/power_supply.h>
#include "hid-ids.h"
......@@ -61,6 +62,13 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
#define CONTACT_TOUCH_MAJOR_MASK 0x07
#define CONTACT_PRESSURE_MASK 0x7f
#define BATTERY_REPORT_ID (0x03)
#define BATTERY_REPORT_SIZE (1 + 8)
#define BATTERY_LEVEL_MAX ((u8)255)
#define BATTERY_STAT_DISCONNECT (0)
#define BATTERY_STAT_CHARGING (1)
#define BATTERY_STAT_FULL (2)
#define QUIRK_FIX_NOTEBOOK_REPORT BIT(0)
#define QUIRK_NO_INIT_REPORTS BIT(1)
#define QUIRK_SKIP_INPUT_MAPPING BIT(2)
......@@ -71,6 +79,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
#define QUIRK_T100CHI BIT(7)
#define QUIRK_G752_KEYBOARD BIT(8)
#define QUIRK_T101HA_DOCK BIT(9)
#define QUIRK_T90CHI BIT(10)
#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \
QUIRK_NO_INIT_REPORTS | \
......@@ -100,12 +109,21 @@ struct asus_touchpad_info {
struct asus_drvdata {
unsigned long quirks;
struct hid_device *hdev;
struct input_dev *input;
struct asus_kbd_leds *kbd_backlight;
const struct asus_touchpad_info *tp;
bool enable_backlight;
struct power_supply *battery;
struct power_supply_desc battery_desc;
int battery_capacity;
int battery_stat;
bool battery_in_query;
unsigned long battery_next_query;
};
static int asus_report_battery(struct asus_drvdata *, u8 *, int);
static const struct asus_touchpad_info asus_i2c_tp = {
.max_x = 2794,
.max_y = 1758,
......@@ -259,6 +277,9 @@ static int asus_raw_event(struct hid_device *hdev,
{
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
if (drvdata->battery && data[0] == BATTERY_REPORT_ID)
return asus_report_battery(drvdata, data, size);
if (drvdata->tp && data[0] == INPUT_REPORT_ID)
return asus_report_input(drvdata, data, size);
......@@ -428,6 +449,164 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
return ret;
}
/*
* [0] REPORT_ID (same value defined in report descriptor)
* [1] rest battery level. range [0..255]
* [2]..[7] Bluetooth hardware address (MAC address)
* [8] charging status
* = 0 : AC offline / discharging
* = 1 : AC online / charging
* = 2 : AC online / fully charged
*/
static int asus_parse_battery(struct asus_drvdata *drvdata, u8 *data, int size)
{
u8 sts;
u8 lvl;
int val;
lvl = data[1];
sts = data[8];
drvdata->battery_capacity = ((int)lvl * 100) / (int)BATTERY_LEVEL_MAX;
switch (sts) {
case BATTERY_STAT_CHARGING:
val = POWER_SUPPLY_STATUS_CHARGING;
break;
case BATTERY_STAT_FULL:
val = POWER_SUPPLY_STATUS_FULL;
break;
case BATTERY_STAT_DISCONNECT:
default:
val = POWER_SUPPLY_STATUS_DISCHARGING;
break;
}
drvdata->battery_stat = val;
return 0;
}
static int asus_report_battery(struct asus_drvdata *drvdata, u8 *data, int size)
{
/* notify only the autonomous event by device */
if ((drvdata->battery_in_query == false) &&
(size == BATTERY_REPORT_SIZE))
power_supply_changed(drvdata->battery);
return 0;
}
static int asus_battery_query(struct asus_drvdata *drvdata)
{
u8 *buf;
int ret = 0;
buf = kmalloc(BATTERY_REPORT_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
drvdata->battery_in_query = true;
ret = hid_hw_raw_request(drvdata->hdev, BATTERY_REPORT_ID,
buf, BATTERY_REPORT_SIZE,
HID_INPUT_REPORT, HID_REQ_GET_REPORT);
drvdata->battery_in_query = false;
if (ret == BATTERY_REPORT_SIZE)
ret = asus_parse_battery(drvdata, buf, BATTERY_REPORT_SIZE);
else
ret = -ENODATA;
kfree(buf);
return ret;
}
static enum power_supply_property asus_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_SCOPE,
POWER_SUPPLY_PROP_MODEL_NAME,
};
#define QUERY_MIN_INTERVAL (60 * HZ) /* 60[sec] */
static int asus_battery_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct asus_drvdata *drvdata = power_supply_get_drvdata(psy);
int ret = 0;
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
case POWER_SUPPLY_PROP_CAPACITY:
if (time_before(drvdata->battery_next_query, jiffies)) {
drvdata->battery_next_query =
jiffies + QUERY_MIN_INTERVAL;
ret = asus_battery_query(drvdata);
if (ret)
return ret;
}
if (psp == POWER_SUPPLY_PROP_STATUS)
val->intval = drvdata->battery_stat;
else
val->intval = drvdata->battery_capacity;
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = 1;
break;
case POWER_SUPPLY_PROP_SCOPE:
val->intval = POWER_SUPPLY_SCOPE_DEVICE;
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = drvdata->hdev->name;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int asus_battery_probe(struct hid_device *hdev)
{
struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
struct power_supply_config pscfg = { .drv_data = drvdata };
int ret = 0;
drvdata->battery_capacity = 0;
drvdata->battery_stat = POWER_SUPPLY_STATUS_UNKNOWN;
drvdata->battery_in_query = false;
drvdata->battery_desc.properties = asus_battery_props;
drvdata->battery_desc.num_properties = ARRAY_SIZE(asus_battery_props);
drvdata->battery_desc.get_property = asus_battery_get_property;
drvdata->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;
drvdata->battery_desc.use_for_apm = 0;
drvdata->battery_desc.name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
"asus-keyboard-%s-battery",
strlen(hdev->uniq) ?
hdev->uniq : dev_name(&hdev->dev));
if (!drvdata->battery_desc.name)
return -ENOMEM;
drvdata->battery_next_query = jiffies;
drvdata->battery = devm_power_supply_register(&hdev->dev,
&(drvdata->battery_desc), &pscfg);
if (IS_ERR(drvdata->battery)) {
ret = PTR_ERR(drvdata->battery);
drvdata->battery = NULL;
hid_err(hdev, "Unable to register battery device\n");
return ret;
}
power_supply_powers(drvdata->battery, &hdev->dev);
return ret;
}
static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi)
{
struct input_dev *input = hi->input;
......@@ -500,7 +679,7 @@ static int asus_input_mapping(struct hid_device *hdev,
* This avoids a bunch of non-functional hid_input devices getting
* created because of the T100CHI using HID_QUIRK_MULTI_INPUT.
*/
if (drvdata->quirks & QUIRK_T100CHI) {
if (drvdata->quirks & (QUIRK_T100CHI | QUIRK_T90CHI)) {
if (field->application == (HID_UP_GENDESK | 0x0080) ||
usage->hid == (HID_UP_GENDEVCTRLS | 0x0024) ||
usage->hid == (HID_UP_GENDEVCTRLS | 0x0025) ||
......@@ -660,6 +839,15 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
drvdata->quirks = id->driver_data;
/*
* T90CHI's keyboard dock returns same ID values as T100CHI's dock.
* Thus, identify T90CHI dock with product name string.
*/
if (strstr(hdev->name, "T90CHI")) {
drvdata->quirks &= ~QUIRK_T100CHI;
drvdata->quirks |= QUIRK_T90CHI;
}
if (drvdata->quirks & QUIRK_IS_MULTITOUCH)
drvdata->tp = &asus_i2c_tp;
......@@ -694,6 +882,17 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (drvdata->quirks & QUIRK_NO_INIT_REPORTS)
hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
drvdata->hdev = hdev;
if (drvdata->quirks & (QUIRK_T100CHI | QUIRK_T90CHI)) {
ret = asus_battery_probe(hdev);
if (ret) {
hid_err(hdev,
"Asus hid battery_probe failed: %d\n", ret);
return ret;
}
}
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "Asus hid parse failed: %d\n", ret);
......@@ -769,28 +968,44 @@ static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc,
hid_info(hdev, "Fixing up Asus T100 keyb report descriptor\n");
rdesc[74] &= ~HID_MAIN_ITEM_CONSTANT;
}
/* For the T100CHI keyboard dock */
if (drvdata->quirks & QUIRK_T100CHI &&
*rsize == 403 && rdesc[388] == 0x09 && rdesc[389] == 0x76) {
/* For the T100CHI/T90CHI keyboard dock */
if (drvdata->quirks & (QUIRK_T100CHI | QUIRK_T90CHI)) {
int rsize_orig;
int offs;
if (drvdata->quirks & QUIRK_T100CHI) {
rsize_orig = 403;
offs = 388;
} else {
rsize_orig = 306;
offs = 291;
}
/*
* Change Usage (76h) to Usage Minimum (00h), Usage Maximum
* (FFh) and clear the flags in the Input() byte.
* Note the descriptor has a bogus 0 byte at the end so we
* only need 1 extra byte.
*/
*rsize = 404;
rdesc = kmemdup(rdesc, *rsize, GFP_KERNEL);
if (!rdesc)
return NULL;
hid_info(hdev, "Fixing up T100CHI keyb report descriptor\n");
memmove(rdesc + 392, rdesc + 390, 12);
rdesc[388] = 0x19;
rdesc[389] = 0x00;
rdesc[390] = 0x29;
rdesc[391] = 0xff;
rdesc[402] = 0x00;
if (*rsize == rsize_orig &&
rdesc[offs] == 0x09 && rdesc[offs + 1] == 0x76) {
*rsize = rsize_orig + 1;
rdesc = kmemdup(rdesc, *rsize, GFP_KERNEL);
if (!rdesc)
return NULL;
hid_info(hdev, "Fixing up %s keyb report descriptor\n",
drvdata->quirks & QUIRK_T100CHI ?
"T100CHI" : "T90CHI");
memmove(rdesc + offs + 4, rdesc + offs + 2, 12);
rdesc[offs] = 0x19;
rdesc[offs + 1] = 0x00;
rdesc[offs + 2] = 0x29;
rdesc[offs + 3] = 0xff;
rdesc[offs + 14] = 0x00;
}
}
if (drvdata->quirks & QUIRK_G752_KEYBOARD &&
*rsize == 75 && rdesc[61] == 0x15 && rdesc[62] == 0x00) {
/* report is missing usage mninum and maximum */
......
......@@ -393,7 +393,7 @@ static int elan_start_multitouch(struct hid_device *hdev)
* This byte sequence will enable multitouch mode and disable
* mouse emulation
*/
const unsigned char buf[] = { 0x0D, 0x00, 0x03, 0x21, 0x00 };
static const unsigned char buf[] = { 0x0D, 0x00, 0x03, 0x21, 0x00 };
unsigned char *dmabuf = kmemdup(buf, sizeof(buf), GFP_KERNEL);
if (!dmabuf)
......
......@@ -72,6 +72,7 @@
#define USB_VENDOR_ID_ALCOR 0x058f
#define USB_DEVICE_ID_ALCOR_USBRS232 0x9720
#define USB_DEVICE_ID_ALCOR_MALTRON_KB 0x9410
#define USB_VENDOR_ID_ALPS 0x0433
#define USB_DEVICE_ID_IBM_GAMEPAD 0x1101
......@@ -273,6 +274,7 @@
#define USB_DEVICE_ID_CHICONY_PIXART_USB_OPTICAL_MOUSE 0x1053
#define USB_DEVICE_ID_CHICONY_WIRELESS2 0x1123
#define USB_DEVICE_ID_ASUS_AK1D 0x1125
#define USB_DEVICE_ID_CHICONY_TOSHIBA_WT10A 0x1408
#define USB_DEVICE_ID_CHICONY_ACER_SWITCH12 0x1421
#define USB_VENDOR_ID_CHUNGHWAT 0x2247
......@@ -661,6 +663,7 @@
#define USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2 0x501a
#define USB_DEVICE_ID_KYE_EASYPEN_M610X 0x5013
#define USB_DEVICE_ID_KYE_PENSKETCH_M912 0x5015
#define USB_DEVICE_ID_KYE_EASYPEN_M406XE 0x5019
#define USB_VENDOR_ID_LABTEC 0x1020
#define USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD 0x0006
......@@ -714,6 +717,7 @@
#define USB_DEVICE_ID_LENOVO_TPPRODOCK 0x6067
#define USB_DEVICE_ID_LENOVO_X1_COVER 0x6085
#define USB_DEVICE_ID_LENOVO_X1_TAB 0x60a3
#define USB_DEVICE_ID_LENOVO_X1_TAB3 0x60b5
#define USB_VENDOR_ID_LG 0x1fd2
#define USB_DEVICE_ID_LG_MULTITOUCH 0x0064
......@@ -744,6 +748,7 @@
#define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283
#define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO 0xc286
#define USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940 0xc287
#define USB_DEVICE_ID_LOGITECH_WINGMAN_FG 0xc20e
#define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG 0xc293
#define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294
#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295
......@@ -1134,11 +1139,16 @@
#define USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850 0x0522
#define USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60 0x0781
#define USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3 0x3031
#define USB_DEVICE_ID_UGEE_TABLET_81 0x0081
#define USB_DEVICE_ID_UGEE_TABLET_45 0x0045
#define USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81 0x0081
#define USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45 0x0045
#define USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47 0x0047
#define USB_DEVICE_ID_YIYNOVA_TABLET 0x004d
#define USB_VENDOR_ID_UGEE 0x28bd
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540 0x0075
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640 0x0094
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01 0x0042
#define USB_DEVICE_ID_UGEE_TABLET_G5 0x0074
#define USB_DEVICE_ID_UGEE_TABLET_EX07S 0x0071
#define USB_VENDOR_ID_UNITEC 0x227d
......@@ -1240,4 +1250,10 @@
#define USB_VENDOR_ID_UGTIZER 0x2179
#define USB_DEVICE_ID_UGTIZER_TABLET_GP0610 0x0053
#define USB_VENDOR_ID_VIEWSONIC 0x0543
#define USB_DEVICE_ID_VIEWSONIC_PD1011 0xe621
#define USB_VENDOR_ID_SIGNOTEC 0x2133
#define USB_DEVICE_ID_SIGNOTEC_VIEWSONIC_PD1011 0x0018
#endif
......@@ -328,6 +328,9 @@ static const struct hid_device_id hid_battery_quirks[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_SYMBOL,
USB_DEVICE_ID_SYMBOL_SCANNER_3),
HID_BATTERY_QUIRK_IGNORE },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_T100CHI_KEYBOARD),
HID_BATTERY_QUIRK_IGNORE },
{}
};
......
......@@ -483,6 +483,80 @@ static __u8 pensketch_m912_rdesc_fixed[] = {
0xC0 /* End Collection */
};
/* Original EasyPen M406XE report descriptor size */
#define EASYPEN_M406XE_RDESC_ORIG_SIZE 476
/* Fixed EasyPen M406XE report descriptor */
static __u8 easypen_m406xe_rdesc_fixed[] = {
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x01, /* Usage (01h), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x05, /* Report ID (5), */
0x09, 0x01, /* Usage (01h), */
0x15, 0x80, /* Logical Minimum (-128), */
0x25, 0x7F, /* Logical Maximum (127), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x07, /* Report Count (7), */
0xB1, 0x02, /* Feature (Variable), */
0xC0, /* End Collection, */
0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x10, /* Report ID (16), */
0x09, 0x20, /* Usage (Stylus), */
0xA0, /* Collection (Physical), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x09, 0x42, /* Usage (Tip Switch), */
0x09, 0x44, /* Usage (Barrel Switch), */
0x09, 0x46, /* Usage (Tablet Pick), */
0x95, 0x03, /* Report Count (3), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x04, /* Report Count (4), */
0x81, 0x03, /* Input (Constant, Variable), */
0x09, 0x32, /* Usage (In Range), */
0x95, 0x01, /* Report Count (1), */
0x81, 0x02, /* Input (Variable), */
0x75, 0x10, /* Report Size (16), */
0x95, 0x01, /* Report Count (1), */
0xA4, /* Push, */
0x05, 0x01, /* Usage Page (Desktop), */
0x55, 0xFD, /* Unit Exponent (-3), */
0x65, 0x13, /* Unit (Inch), */
0x34, /* Physical Minimum (0), */
0x09, 0x30, /* Usage (X), */
0x46, 0x70, 0x17, /* Physical Maximum (6000), */
0x26, 0x00, 0x3C, /* Logical Maximum (15360), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x31, /* Usage (Y), */
0x46, 0xA0, 0x0F, /* Physical Maximum (4000), */
0x26, 0x00, 0x28, /* Logical Maximum (10240), */
0x81, 0x02, /* Input (Variable), */
0xB4, /* Pop, */
0x09, 0x30, /* Usage (Tip Pressure), */
0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
0x81, 0x02, /* Input (Variable), */
0xC0, /* End Collection, */
0xC0, /* End Collection */
0x05, 0x0C, /* Usage Page (Consumer), */
0x09, 0x01, /* Usage (Consumer Control), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x12, /* Report ID (18), */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x04, /* Report Count (4), */
0x0A, 0x79, 0x02, /* Usage (AC Redo Or Repeat), */
0x0A, 0x1A, 0x02, /* Usage (AC Undo), */
0x0A, 0x2D, 0x02, /* Usage (AC Zoom In), */
0x0A, 0x2E, 0x02, /* Usage (AC Zoom Out), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x34, /* Report Count (52), */
0x81, 0x03, /* Input (Constant, Variable), */
0xC0 /* End Collection */
};
static __u8 *kye_consumer_control_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize, int offset, const char *device_name) {
/*
......@@ -555,6 +629,12 @@ static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc,
*rsize = sizeof(easypen_m610x_rdesc_fixed);
}
break;
case USB_DEVICE_ID_KYE_EASYPEN_M406XE:
if (*rsize == EASYPEN_M406XE_RDESC_ORIG_SIZE) {
rdesc = easypen_m406xe_rdesc_fixed;
*rsize = sizeof(easypen_m406xe_rdesc_fixed);
}
break;
case USB_DEVICE_ID_KYE_PENSKETCH_M912:
if (*rsize == PENSKETCH_M912_RDESC_ORIG_SIZE) {
rdesc = pensketch_m912_rdesc_fixed;
......@@ -644,6 +724,7 @@ static int kye_probe(struct hid_device *hdev, const struct hid_device_id *id)
case USB_DEVICE_ID_KYE_MOUSEPEN_I608X:
case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2:
case USB_DEVICE_ID_KYE_EASYPEN_M610X:
case USB_DEVICE_ID_KYE_EASYPEN_M406XE:
case USB_DEVICE_ID_KYE_PENSKETCH_M912:
ret = kye_tablet_enable(hdev);
if (ret) {
......@@ -678,6 +759,8 @@ static const struct hid_device_id kye_devices[] = {
USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
USB_DEVICE_ID_KYE_EASYPEN_M610X) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
USB_DEVICE_ID_KYE_EASYPEN_M406XE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE,
......
......@@ -50,6 +50,7 @@
#define MOMO_RDESC_ORIG_SIZE 87
#define MOMO2_RDESC_ORIG_SIZE 87
#define FFG_RDESC_ORIG_SIZE 85
#define FG_RDESC_ORIG_SIZE 82
/* Fixed report descriptors for Logitech Driving Force (and Pro)
* wheel controllers
......@@ -381,6 +382,49 @@ static __u8 ffg_rdesc_fixed[] = {
0xC0 /* End Collection */
};
static __u8 fg_rdesc_fixed[] = {
0x05, 0x01, /* Usage Page (Desktop), */
0x09, 0x04, /* Usage (Joystik), */
0xA1, 0x01, /* Collection (Application), */
0xA1, 0x02, /* Collection (Logical), */
0x15, 0x00, /* Logical Minimum (0), */
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
0x35, 0x00, /* Physical Minimum (0), */
0x46, 0xFF, 0x00, /* Physical Maximum (255), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x01, /* Report Count (1), */
0x09, 0x30, /* Usage (X), */
0x81, 0x02, /* Input (Variable), */
0xA4, /* Push, */
0x25, 0x01, /* Logical Maximum (1), */
0x45, 0x01, /* Physical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x02, /* Report Count (2), */
0x81, 0x01, /* Input (Constant), */
0x95, 0x06, /* Report Count (6), */
0x05, 0x09, /* Usage Page (Button), */
0x19, 0x01, /* Usage Minimum (01h), */
0x29, 0x06, /* Usage Maximum (06h), */
0x81, 0x02, /* Input (Variable), */
0x05, 0x01, /* Usage Page (Desktop), */
0xB4, /* Pop, */
0x81, 0x02, /* Input (Constant), */
0x09, 0x31, /* Usage (Y), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x32, /* Usage (Z), */
0x81, 0x02, /* Input (Variable), */
0xC0, /* End Collection, */
0xA1, 0x02, /* Collection (Logical), */
0x26, 0xFF, 0x00, /* Logical Maximum (255), */
0x46, 0xFF, 0x00, /* Physical Maximum (255), */
0x75, 0x08, /* Report Size (8), */
0x95, 0x04, /* Report Count (4), */
0x09, 0x02, /* Usage (02h), */
0xB1, 0x02, /* Feature (Variable), */
0xC0, /* End Collection, */
0xC0 /* End Collection, */
};
/*
* Certain Logitech keyboards send in report #3 keys which are far
* above the logical maximum described in descriptor. This extends
......@@ -408,6 +452,19 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
switch (hdev->product) {
case USB_DEVICE_ID_LOGITECH_WINGMAN_FG:
if (*rsize == FG_RDESC_ORIG_SIZE) {
hid_info(hdev,
"fixing up Logitech Wingman Formula GP report descriptor\n");
rdesc = fg_rdesc_fixed;
*rsize = sizeof(fg_rdesc_fixed);
} else {
hid_info(hdev,
"rdesc size test failed for formula gp\n");
}
break;
case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
if (*rsize == FFG_RDESC_ORIG_SIZE) {
hid_info(hdev,
......@@ -664,6 +721,7 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
usage->code == ABS_RZ)) {
switch (hdev->product) {
case USB_DEVICE_ID_LOGITECH_G29_WHEEL:
case USB_DEVICE_ID_LOGITECH_WINGMAN_FG:
case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
case USB_DEVICE_ID_LOGITECH_WHEEL:
case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
......@@ -871,6 +929,8 @@ static const struct hid_device_id lg_devices[] = {
.driver_data = LG_NOGET | LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
.driver_data = LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FG),
.driver_data = LG_NOGET },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG),
.driver_data = LG_NOGET | LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
......
......@@ -103,6 +103,10 @@ static const signed short lg4ff_wheel_effects[] = {
-1
};
static const signed short no_wheel_effects[] = {
-1
};
struct lg4ff_wheel {
const u32 product_id;
const signed short *ff_effects;
......@@ -137,6 +141,7 @@ struct lg4ff_alternate_mode {
};
static const struct lg4ff_wheel lg4ff_devices[] = {
{USB_DEVICE_ID_LOGITECH_WINGMAN_FG, no_wheel_effects, 40, 180, NULL},
{USB_DEVICE_ID_LOGITECH_WINGMAN_FFG, lg4ff_wheel_effects, 40, 180, NULL},
{USB_DEVICE_ID_LOGITECH_WHEEL, lg4ff_wheel_effects, 40, 270, NULL},
{USB_DEVICE_ID_LOGITECH_MOMO_WHEEL, lg4ff_wheel_effects, 40, 270, NULL},
......@@ -346,6 +351,7 @@ int lg4ff_raw_event(struct hid_device *hdev, struct hid_report *report,
rd[5] = rd[3];
rd[6] = 0x7F;
return 1;
case USB_DEVICE_ID_LOGITECH_WINGMAN_FG:
case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
......
// SPDX-License-Identifier: GPL-2.0
/*
* HID driver for Maltron L90
*
* 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) 2008 Jiri Slaby
* Copyright (c) 2012 David Dillow <dave@thedillows.org>
* Copyright (c) 2006-2013 Jiri Kosina
* Copyright (c) 2013 Colin Leitner <colin.leitner@gmail.com>
* Copyright (c) 2014-2016 Frank Praznik <frank.praznik@gmail.com>
* Copyright (c) 2010 Richard Nauber <Richard.Nauber@gmail.com>
* Copyright (c) 2016 Yuxuan Shui <yshuiv7@gmail.com>
* Copyright (c) 2018 William Whistler <wtbw@wtbw.co.uk>
*/
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>
#include "hid-ids.h"
/* The original buggy USB descriptor */
static u8 maltron_rdesc_o[] = {
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
0x09, 0x80, /* Usage (Sys Control) */
0xA1, 0x01, /* Collection (Application) */
0x85, 0x02, /* Report ID (2) */
0x75, 0x01, /* Report Size (1) */
0x95, 0x01, /* Report Count (1) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x09, 0x82, /* Usage (Sys Sleep) */
0x81, 0x06, /* Input (Data,Var,Rel) */
0x09, 0x82, /* Usage (Sys Sleep) */
0x81, 0x06, /* Input (Data,Var,Rel) */
0x09, 0x83, /* Usage (Sys Wake Up) */
0x81, 0x06, /* Input (Data,Var,Rel) */
0x75, 0x05, /* Report Size (5) */
0x81, 0x01, /* Input (Const,Array,Abs) */
0xC0, /* End Collection */
0x05, 0x0C, /* Usage Page (Consumer) */
0x09, 0x01, /* Usage (Consumer Control) */
0xA1, 0x01, /* Collection (Application) */
0x85, 0x03, /* Report ID (3) */
0x95, 0x01, /* Report Count (1) */
0x75, 0x10, /* Report Size (16) */
0x19, 0x00, /* Usage Minimum (Unassigned) */
0x2A, 0xFF, 0x7F, /* Usage Maximum (0x7FFF) */
0x81, 0x00, /* Input (Data,Array,Abs) */
0xC0, /* End Collection */
0x06, 0x7F, 0xFF, /* Usage Page (Vendor Defined 0xFF7F) */
0x09, 0x01, /* Usage (0x01) */
0xA1, 0x01, /* Collection (Application) */
0x85, 0x04, /* Report ID (4) */
0x95, 0x01, /* Report Count (1) */
0x75, 0x10, /* Report Size (16) */
0x19, 0x00, /* Usage Minimum (0x00) */
0x2A, 0xFF, 0x7F, /* Usage Maximum (0x7FFF) */
0x81, 0x00, /* Input (Data,Array,Abs) */
0x75, 0x02, /* Report Size (2) */
0x25, 0x02, /* Logical Maximum (2) */
0x09, 0x90, /* Usage (0x90) */
0xB1, 0x02, /* Feature (Data,Var,Abs) */
0x75, 0x06, /* Report Size (6) */
0xB1, 0x01, /* Feature (Const,Array,Abs) */
0x75, 0x01, /* Report Size (1) */
0x25, 0x01, /* Logical Maximum (1) */
0x05, 0x08, /* Usage Page (LEDs) */
0x09, 0x2A, /* Usage (On-Line) */
0x91, 0x02, /* Output (Data,Var,Abs) */
0x09, 0x4B, /* Usage (Generic Indicator) */
0x91, 0x02, /* Output (Data,Var,Abs) */
0x75, 0x06, /* Report Size (6) */
0x95, 0x01, /* Report Count (1) */
0x91, 0x01, /* Output (Const,Array,Abs) */
0xC0 /* End Collection */
};
/* The patched descriptor, allowing media key events to be accepted as valid */
static u8 maltron_rdesc[] = {
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
0x09, 0x80, /* Usage (Sys Control) */
0xA1, 0x01, /* Collection (Application) */
0x85, 0x02, /* Report ID (2) */
0x75, 0x01, /* Report Size (1) */
0x95, 0x01, /* Report Count (1) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x09, 0x82, /* Usage (Sys Sleep) */
0x81, 0x06, /* Input (Data,Var,Rel) */
0x09, 0x82, /* Usage (Sys Sleep) */
0x81, 0x06, /* Input (Data,Var,Rel) */
0x09, 0x83, /* Usage (Sys Wake Up) */
0x81, 0x06, /* Input (Data,Var,Rel) */
0x75, 0x05, /* Report Size (5) */
0x81, 0x01, /* Input (Const,Array,Abs) */
0xC0, /* End Collection */
0x05, 0x0C, /* Usage Page (Consumer) */
0x09, 0x01, /* Usage (Consumer Control) */
0xA1, 0x01, /* Collection (Application) */
0x85, 0x03, /* Report ID (3) */
0x15, 0x00, /* Logical Minimum (0) - changed */
0x26, 0xFF, 0x7F, /* Logical Maximum (32767) - changed */
0x95, 0x01, /* Report Count (1) */
0x75, 0x10, /* Report Size (16) */
0x19, 0x00, /* Usage Minimum (Unassigned) */
0x2A, 0xFF, 0x7F, /* Usage Maximum (0x7FFF) */
0x81, 0x00, /* Input (Data,Array,Abs) */
0xC0, /* End Collection */
0x06, 0x7F, 0xFF, /* Usage Page (Vendor Defined 0xFF7F) */
0x09, 0x01, /* Usage (0x01) */
0xA1, 0x01, /* Collection (Application) */
0x85, 0x04, /* Report ID (4) */
0x95, 0x01, /* Report Count (1) */
0x75, 0x10, /* Report Size (16) */
0x19, 0x00, /* Usage Minimum (0x00) */
0x2A, 0xFF, 0x7F, /* Usage Maximum (0x7FFF) */
0x81, 0x00, /* Input (Data,Array,Abs) */
0x75, 0x02, /* Report Size (2) */
0x25, 0x02, /* Logical Maximum (2) */
0x09, 0x90, /* Usage (0x90) */
0xB1, 0x02, /* Feature (Data,Var,Abs) */
0x75, 0x06, /* Report Size (6) */
0xB1, 0x01, /* Feature (Const,Array,Abs) */
0x75, 0x01, /* Report Size (1) */
0x25, 0x01, /* Logical Maximum (1) */
0x05, 0x08, /* Usage Page (LEDs) */
0x09, 0x2A, /* Usage (On-Line) */
0x91, 0x02, /* Output (Data,Var,Abs) */
0x09, 0x4B, /* Usage (Generic Indicator) */
0x91, 0x02, /* Output (Data,Var,Abs) */
0x75, 0x06, /* Report Size (6) */
0x95, 0x01, /* Report Count (1) */
0x91, 0x01, /* Output (Const,Array,Abs) */
0xC0 /* End Collection */
};
static __u8 *maltron_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
if (*rsize == sizeof(maltron_rdesc_o) &&
!memcmp(maltron_rdesc_o, rdesc, sizeof(maltron_rdesc_o))) {
hid_info(hdev, "Replacing Maltron L90 keyboard report descriptor\n");
*rsize = sizeof(maltron_rdesc);
return maltron_rdesc;
}
return rdesc;
}
static const struct hid_device_id maltron_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_MALTRON_KB)},
{ }
};
MODULE_DEVICE_TABLE(hid, maltron_devices);
static struct hid_driver maltron_driver = {
.name = "maltron",
.id_table = maltron_devices,
.report_fixup = maltron_report_fixup
};
module_hid_driver(maltron_driver);
MODULE_LICENSE("GPL");
......@@ -1780,6 +1780,12 @@ static const struct hid_device_id mt_devices[] = {
USB_VENDOR_ID_LENOVO,
USB_DEVICE_ID_LENOVO_X1_TAB) },
/* Lenovo X1 TAB Gen 3 */
{ .driver_data = MT_CLS_WIN_8_DUAL,
HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8,
USB_VENDOR_ID_LENOVO,
USB_DEVICE_ID_LENOVO_X1_TAB3) },
/* Anton devices */
{ .driver_data = MT_CLS_EXPORT_ALL_INPUTS,
MT_USB_DEVICE(USB_VENDOR_ID_ANTON,
......
......@@ -99,6 +99,7 @@ static const struct hid_device_id hid_quirks[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_M912), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M406XE), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE_ID2), HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_C007), HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_C077), HID_QUIRK_ALWAYS_POLL },
......@@ -411,11 +412,6 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_MANTICORE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_GX_IMPERATOR) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_I405X) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_M912) },
#endif
#if IS_ENABLED(CONFIG_HID_LCPOWER)
{ HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000) },
......@@ -451,6 +447,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G29_WHEEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FG) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940) },
......@@ -673,35 +670,9 @@ static const struct hid_device_id hid_have_special_driver[] = {
#if IS_ENABLED(CONFIG_HID_TWINHAN)
{ HID_USB_DEVICE(USB_VENDOR_ID_TWINHAN, USB_DEVICE_ID_TWINHAN_IR_REMOTE) },
#endif
#if IS_ENABLED(CONFIG_HID_UCLOGIC)
{ HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_81) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_45) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_TABLET_EX07S) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610) },
#endif
#if IS_ENABLED(CONFIG_HID_UDRAW_PS3)
{ HID_USB_DEVICE(USB_VENDOR_ID_THQ, USB_DEVICE_ID_THQ_PS3_UDRAW) },
#endif
#if IS_ENABLED(CONFIG_HID_WALTOP)
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_Q_PAD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_PID_0038) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET) },
#endif
#if IS_ENABLED(CONFIG_HID_XINMO)
{ HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_XIN_MO_DUAL_ARCADE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_THT_2P_ARCADE) },
......
......@@ -783,6 +783,7 @@ static void kone_keep_values_up_to_date(struct kone_device *kone,
case kone_mouse_event_switch_profile:
kone->actual_dpi = kone->profiles[event->value - 1].
startup_dpi;
/* fall through */
case kone_mouse_event_osd_profile:
kone->actual_profile = event->value;
break;
......
......@@ -58,6 +58,7 @@
#define FUTUREMAX_DANCE_MAT BIT(13)
#define NSG_MR5U_REMOTE_BT BIT(14)
#define NSG_MR7U_REMOTE_BT BIT(15)
#define SHANWAN_GAMEPAD BIT(16)
#define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)
#define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT)
......@@ -1490,6 +1491,7 @@ static int sony_register_sensors(struct sony_sc *sc)
*/
static int sixaxis_set_operational_usb(struct hid_device *hdev)
{
struct sony_sc *sc = hid_get_drvdata(hdev);
const int buf_size =
max(SIXAXIS_REPORT_0xF2_SIZE, SIXAXIS_REPORT_0xF5_SIZE);
u8 *buf;
......@@ -1519,14 +1521,15 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev)
/*
* But the USB interrupt would cause SHANWAN controllers to
* start rumbling non-stop.
* start rumbling non-stop, so skip step 3 for these controllers.
*/
if (strcmp(hdev->name, "SHANWAN PS3 GamePad")) {
ret = hid_hw_output_report(hdev, buf, 1);
if (ret < 0) {
hid_info(hdev, "can't set operational mode: step 3, ignoring\n");
ret = 0;
}
if (sc->quirks & SHANWAN_GAMEPAD)
goto out;
ret = hid_hw_output_report(hdev, buf, 1);
if (ret < 0) {
hid_info(hdev, "can't set operational mode: step 3, ignoring\n");
ret = 0;
}
out:
......@@ -2097,9 +2100,14 @@ static void sixaxis_send_output_report(struct sony_sc *sc)
}
}
hid_hw_raw_request(sc->hdev, report->report_id, (u8 *)report,
sizeof(struct sixaxis_output_report),
HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
/* SHANWAN controllers require output reports via intr channel */
if (sc->quirks & SHANWAN_GAMEPAD)
hid_hw_output_report(sc->hdev, (u8 *)report,
sizeof(struct sixaxis_output_report));
else
hid_hw_raw_request(sc->hdev, report->report_id, (u8 *)report,
sizeof(struct sixaxis_output_report),
HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
}
static void dualshock4_send_output_report(struct sony_sc *sc)
......@@ -2811,6 +2819,9 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (!strcmp(hdev->name, "FutureMax Dance Mat"))
quirks |= FUTUREMAX_DANCE_MAT;
if (!strcmp(hdev->name, "SHANWAN PS3 GamePad"))
quirks |= SHANWAN_GAMEPAD;
sc = devm_kzalloc(&hdev->dev, sizeof(*sc), GFP_KERNEL);
if (sc == NULL) {
hid_err(hdev, "can't alloc sony descriptor\n");
......
......@@ -283,11 +283,6 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable)
static int steam_input_open(struct input_dev *dev)
{
struct steam_device *steam = input_get_drvdata(dev);
int ret;
ret = hid_hw_open(steam->hdev);
if (ret)
return ret;
mutex_lock(&steam->mutex);
if (!steam->client_opened && lizard_mode)
......@@ -304,8 +299,6 @@ static void steam_input_close(struct input_dev *dev)
if (!steam->client_opened && lizard_mode)
steam_set_lizard_mode(steam, true);
mutex_unlock(&steam->mutex);
hid_hw_close(steam->hdev);
}
static enum power_supply_property steam_battery_props[] = {
......@@ -623,11 +616,6 @@ static void steam_client_ll_stop(struct hid_device *hdev)
static int steam_client_ll_open(struct hid_device *hdev)
{
struct steam_device *steam = hdev->driver_data;
int ret;
ret = hid_hw_open(steam->hdev);
if (ret)
return ret;
mutex_lock(&steam->mutex);
steam->client_opened = true;
......@@ -635,7 +623,7 @@ static int steam_client_ll_open(struct hid_device *hdev)
steam_input_unregister(steam);
return ret;
return 0;
}
static void steam_client_ll_close(struct hid_device *hdev)
......@@ -646,7 +634,6 @@ static void steam_client_ll_close(struct hid_device *hdev)
steam->client_opened = false;
mutex_unlock(&steam->mutex);
hid_hw_close(steam->hdev);
if (steam->connected) {
steam_set_lizard_mode(steam, lizard_mode);
steam_input_register(steam);
......@@ -759,14 +746,15 @@ static int steam_probe(struct hid_device *hdev,
if (ret)
goto client_hdev_add_fail;
ret = hid_hw_open(hdev);
if (ret) {
hid_err(hdev,
"%s:hid_hw_open\n",
__func__);
goto hid_hw_open_fail;
}
if (steam->quirks & STEAM_QUIRK_WIRELESS) {
ret = hid_hw_open(hdev);
if (ret) {
hid_err(hdev,
"%s:hid_hw_open for wireless\n",
__func__);
goto hid_hw_open_fail;
}
hid_info(hdev, "Steam wireless receiver connected");
steam_request_conn_status(steam);
} else {
......@@ -781,8 +769,8 @@ static int steam_probe(struct hid_device *hdev,
return 0;
hid_hw_open_fail:
input_register_fail:
hid_hw_open_fail:
client_hdev_add_fail:
hid_hw_stop(hdev);
hid_hw_start_fail:
......@@ -809,8 +797,8 @@ static void steam_remove(struct hid_device *hdev)
cancel_work_sync(&steam->work_connect);
if (steam->quirks & STEAM_QUIRK_WIRELESS) {
hid_info(hdev, "Steam wireless receiver disconnected");
hid_hw_close(hdev);
}
hid_hw_close(hdev);
hid_hw_stop(hdev);
steam_unregister(steam);
}
......
......@@ -34,7 +34,9 @@ static int ts_input_mapping(struct hid_device *hdev, struct hid_input *hi,
return 0;
switch (usage->hid & HID_USAGE) {
case 0x00c: ts_map_key_clear(KEY_WLAN); break;
case 0x00d: ts_map_key_clear(KEY_MEDIA); break;
case 0x010: ts_map_key_clear(KEY_ZOOM); break;
case 0x024: ts_map_key_clear(KEY_MENU); break;
case 0x025: ts_map_key_clear(KEY_TV); break;
case 0x027: ts_map_key_clear(KEY_MODE); break;
......@@ -67,6 +69,7 @@ static const struct hid_device_id ts_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED2, USB_DEVICE_ID_TOPSEED2_RF_COMBO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TOSHIBA_WT10A) },
{ }
};
MODULE_DEVICE_TABLE(hid, ts_devices);
......
// SPDX-License-Identifier: GPL-2.0+
/*
* HID driver for UC-Logic devices not fully compliant with HID standard
*
* Copyright (c) 2010-2014 Nikolai Kondrashov
* Copyright (c) 2013 Martin Rusko
*/
/*
* 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/device.h>
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/timer.h>
#include "usbhid/usbhid.h"
#include "hid-uclogic-params.h"
#include "hid-ids.h"
/* Driver data */
struct uclogic_drvdata {
/* Interface parameters */
struct uclogic_params params;
/* Pointer to the replacement report descriptor. NULL if none. */
__u8 *desc_ptr;
/*
* Size of the replacement report descriptor.
* Only valid if desc_ptr is not NULL
*/
unsigned int desc_size;
/* Pen input device */
struct input_dev *pen_input;
/* In-range timer */
struct timer_list inrange_timer;
/* Last rotary encoder state, or U8_MAX for none */
u8 re_state;
};
/**
* uclogic_inrange_timeout - handle pen in-range state timeout.
* Emulate input events normally generated when pen goes out of range for
* tablets which don't report that.
*
* @t: The timer the timeout handler is attached to, stored in a struct
* uclogic_drvdata.
*/
static void uclogic_inrange_timeout(struct timer_list *t)
{
struct uclogic_drvdata *drvdata = from_timer(drvdata, t,
inrange_timer);
struct input_dev *input = drvdata->pen_input;
if (input == NULL)
return;
input_report_abs(input, ABS_PRESSURE, 0);
/* If BTN_TOUCH state is changing */
if (test_bit(BTN_TOUCH, input->key)) {
input_event(input, EV_MSC, MSC_SCAN,
/* Digitizer Tip Switch usage */
0xd0042);
input_report_key(input, BTN_TOUCH, 0);
}
input_report_key(input, BTN_TOOL_PEN, 0);
input_sync(input);
}
static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
if (drvdata->desc_ptr != NULL) {
rdesc = drvdata->desc_ptr;
*rsize = drvdata->desc_size;
}
return rdesc;
}
static int uclogic_input_mapping(struct hid_device *hdev,
struct hid_input *hi,
struct hid_field *field,
struct hid_usage *usage,
unsigned long **bit,
int *max)
{
struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
struct uclogic_params *params = &drvdata->params;
/* discard the unused pen interface */
if (params->pen_unused && (field->application == HID_DG_PEN))
return -1;
/* let hid-core decide what to do */
return 0;
}
static int uclogic_input_configured(struct hid_device *hdev,
struct hid_input *hi)
{
struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
struct uclogic_params *params = &drvdata->params;
char *name;
const char *suffix = NULL;
struct hid_field *field;
size_t len;
/* no report associated (HID_QUIRK_MULTI_INPUT not set) */
if (!hi->report)
return 0;
/*
* If this is the input corresponding to the pen report
* in need of tweaking.
*/
if (hi->report->id == params->pen.id) {
/* Remember the input device so we can simulate events */
drvdata->pen_input = hi->input;
}
field = hi->report->field[0];
switch (field->application) {
case HID_GD_KEYBOARD:
suffix = "Keyboard";
break;
case HID_GD_MOUSE:
suffix = "Mouse";
break;
case HID_GD_KEYPAD:
suffix = "Pad";
break;
case HID_DG_PEN:
suffix = "Pen";
break;
case HID_CP_CONSUMER_CONTROL:
suffix = "Consumer Control";
break;
case HID_GD_SYSTEM_CONTROL:
suffix = "System Control";
break;
}
if (suffix) {
len = strlen(hdev->name) + 2 + strlen(suffix);
name = devm_kzalloc(&hi->input->dev, len, GFP_KERNEL);
if (name) {
snprintf(name, len, "%s %s", hdev->name, suffix);
hi->input->name = name;
}
}
return 0;
}
static int uclogic_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
int rc;
struct uclogic_drvdata *drvdata = NULL;
bool params_initialized = false;
/*
* libinput requires the pad interface to be on a different node
* than the pen, so use QUIRK_MULTI_INPUT for all tablets.
*/
hdev->quirks |= HID_QUIRK_MULTI_INPUT;
/* Allocate and assign driver data */
drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
if (drvdata == NULL) {
rc = -ENOMEM;
goto failure;
}
timer_setup(&drvdata->inrange_timer, uclogic_inrange_timeout, 0);
drvdata->re_state = U8_MAX;
hid_set_drvdata(hdev, drvdata);
/* Initialize the device and retrieve interface parameters */
rc = uclogic_params_init(&drvdata->params, hdev);
if (rc != 0) {
hid_err(hdev, "failed probing parameters: %d\n", rc);
goto failure;
}
params_initialized = true;
hid_dbg(hdev, "parameters:\n" UCLOGIC_PARAMS_FMT_STR,
UCLOGIC_PARAMS_FMT_ARGS(&drvdata->params));
if (drvdata->params.invalid) {
hid_info(hdev, "interface is invalid, ignoring\n");
rc = -ENODEV;
goto failure;
}
/* Generate replacement report descriptor */
rc = uclogic_params_get_desc(&drvdata->params,
&drvdata->desc_ptr,
&drvdata->desc_size);
if (rc) {
hid_err(hdev,
"failed generating replacement report descriptor: %d\n",
rc);
goto failure;
}
rc = hid_parse(hdev);
if (rc) {
hid_err(hdev, "parse failed\n");
goto failure;
}
rc = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (rc) {
hid_err(hdev, "hw start failed\n");
goto failure;
}
return 0;
failure:
/* Assume "remove" might not be called if "probe" failed */
if (params_initialized)
uclogic_params_cleanup(&drvdata->params);
return rc;
}
#ifdef CONFIG_PM
static int uclogic_resume(struct hid_device *hdev)
{
int rc;
struct uclogic_params params;
/* Re-initialize the device, but discard parameters */
rc = uclogic_params_init(&params, hdev);
if (rc != 0)
hid_err(hdev, "failed to re-initialize the device\n");
else
uclogic_params_cleanup(&params);
return rc;
}
#endif
static int uclogic_raw_event(struct hid_device *hdev,
struct hid_report *report,
u8 *data, int size)
{
struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
struct uclogic_params *params = &drvdata->params;
/* Tweak pen reports, if necessary */
if (!params->pen_unused &&
(report->type == HID_INPUT_REPORT) &&
(report->id == params->pen.id) &&
(size >= 2)) {
/* If it's the "virtual" frame controls report */
if (params->frame.id != 0 &&
data[1] & params->pen_frame_flag) {
/* Change to virtual frame controls report ID */
data[0] = params->frame.id;
return 0;
}
/* If in-range reports are inverted */
if (params->pen.inrange ==
UCLOGIC_PARAMS_PEN_INRANGE_INVERTED) {
/* Invert the in-range bit */
data[1] ^= 0x40;
}
/*
* If report contains fragmented high-resolution pen
* coordinates
*/
if (size >= 10 && params->pen.fragmented_hires) {
u8 pressure_low_byte;
u8 pressure_high_byte;
/* Lift pressure bytes */
pressure_low_byte = data[6];
pressure_high_byte = data[7];
/*
* Move Y coord to make space for high-order X
* coord byte
*/
data[6] = data[5];
data[5] = data[4];
/* Move high-order X coord byte */
data[4] = data[8];
/* Move high-order Y coord byte */
data[7] = data[9];
/* Place pressure bytes */
data[8] = pressure_low_byte;
data[9] = pressure_high_byte;
}
/* If we need to emulate in-range detection */
if (params->pen.inrange == UCLOGIC_PARAMS_PEN_INRANGE_NONE) {
/* Set in-range bit */
data[1] |= 0x40;
/* (Re-)start in-range timeout */
mod_timer(&drvdata->inrange_timer,
jiffies + msecs_to_jiffies(100));
}
}
/* Tweak frame control reports, if necessary */
if ((report->type == HID_INPUT_REPORT) &&
(report->id == params->frame.id)) {
/* If need to, and can, set pad device ID for Wacom drivers */
if (params->frame.dev_id_byte > 0 &&
params->frame.dev_id_byte < size) {
data[params->frame.dev_id_byte] = 0xf;
}
/* If need to, and can, read rotary encoder state change */
if (params->frame.re_lsb > 0 &&
params->frame.re_lsb / 8 < size) {
unsigned int byte = params->frame.re_lsb / 8;
unsigned int bit = params->frame.re_lsb % 8;
u8 change;
u8 prev_state = drvdata->re_state;
/* Read Gray-coded state */
u8 state = (data[byte] >> bit) & 0x3;
/* Encode state change into 2-bit signed integer */
if ((prev_state == 1 && state == 0) ||
(prev_state == 2 && state == 3)) {
change = 1;
} else if ((prev_state == 2 && state == 0) ||
(prev_state == 1 && state == 3)) {
change = 3;
} else {
change = 0;
}
/* Write change */
data[byte] = (data[byte] & ~((u8)3 << bit)) |
(change << bit);
/* Remember state */
drvdata->re_state = state;
}
}
return 0;
}
static void uclogic_remove(struct hid_device *hdev)
{
struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
del_timer_sync(&drvdata->inrange_timer);
hid_hw_stop(hdev);
kfree(drvdata->desc_ptr);
uclogic_params_cleanup(&drvdata->params);
}
static const struct hid_device_id uclogic_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) },
{ HID_USB_DEVICE(USB_VENDOR_ID_HUION,
USB_DEVICE_ID_HUION_TABLET) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_HUION_TABLET) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_YIYNOVA_TABLET) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER,
USB_DEVICE_ID_UGTIZER_TABLET_GP0610) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
USB_DEVICE_ID_UGEE_TABLET_G5) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
USB_DEVICE_ID_UGEE_TABLET_EX07S) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01) },
{ }
};
MODULE_DEVICE_TABLE(hid, uclogic_devices);
static struct hid_driver uclogic_driver = {
.name = "uclogic",
.id_table = uclogic_devices,
.probe = uclogic_probe,
.remove = uclogic_remove,
.report_fixup = uclogic_report_fixup,
.raw_event = uclogic_raw_event,
.input_mapping = uclogic_input_mapping,
.input_configured = uclogic_input_configured,
#ifdef CONFIG_PM
.resume = uclogic_resume,
.reset_resume = uclogic_resume,
#endif
};
module_hid_driver(uclogic_driver);
MODULE_AUTHOR("Martin Rusko");
MODULE_AUTHOR("Nikolai Kondrashov");
MODULE_LICENSE("GPL");
此差异已折叠。
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* HID driver for UC-Logic devices not fully compliant with HID standard
* - tablet initialization and parameter retrieval
*
* Copyright (c) 2018 Nikolai Kondrashov
*/
/*
* 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.
*/
#ifndef _HID_UCLOGIC_PARAMS_H
#define _HID_UCLOGIC_PARAMS_H
#include <linux/usb.h>
#include <linux/hid.h>
/* Types of pen in-range reporting */
enum uclogic_params_pen_inrange {
/* Normal reports: zero - out of proximity, one - in proximity */
UCLOGIC_PARAMS_PEN_INRANGE_NORMAL = 0,
/* Inverted reports: zero - in proximity, one - out of proximity */
UCLOGIC_PARAMS_PEN_INRANGE_INVERTED,
/* No reports */
UCLOGIC_PARAMS_PEN_INRANGE_NONE,
};
/* Convert a pen in-range reporting type to a string */
extern const char *uclogic_params_pen_inrange_to_str(
enum uclogic_params_pen_inrange inrange);
/*
* Tablet interface's pen input parameters.
*
* Must use declarative (descriptive) language, not imperative, to simplify
* understanding and maintain consistency.
*
* Noop (preserving functionality) when filled with zeroes.
*/
struct uclogic_params_pen {
/*
* Pointer to report descriptor describing the inputs.
* Allocated with kmalloc.
*/
__u8 *desc_ptr;
/*
* Size of the report descriptor.
* Only valid, if "desc_ptr" is not NULL.
*/
unsigned int desc_size;
/* Report ID, if reports should be tweaked, zero if not */
unsigned int id;
/* Type of in-range reporting, only valid if "id" is not zero */
enum uclogic_params_pen_inrange inrange;
/*
* True, if reports include fragmented high resolution coords, with
* high-order X and then Y bytes following the pressure field.
* Only valid if "id" is not zero.
*/
bool fragmented_hires;
};
/*
* Parameters of frame control inputs of a tablet interface.
*
* Must use declarative (descriptive) language, not imperative, to simplify
* understanding and maintain consistency.
*
* Noop (preserving functionality) when filled with zeroes.
*/
struct uclogic_params_frame {
/*
* Pointer to report descriptor describing the inputs.
* Allocated with kmalloc.
*/
__u8 *desc_ptr;
/*
* Size of the report descriptor.
* Only valid, if "desc_ptr" is not NULL.
*/
unsigned int desc_size;
/*
* Report ID, if reports should be tweaked, zero if not.
*/
unsigned int id;
/*
* Number of the least-significant bit of the 2-bit state of a rotary
* encoder, in the report. Cannot point to a 2-bit field crossing a
* byte boundary. Zero if not present. Only valid if "id" is not zero.
*/
unsigned int re_lsb;
/*
* Offset of the Wacom-style device ID byte in the report, to be set
* to pad device ID (0xf), for compatibility with Wacom drivers. Zero
* if no changes to the report should be made. Only valid if "id" is
* not zero.
*/
unsigned int dev_id_byte;
};
/*
* Tablet interface report parameters.
*
* Must use declarative (descriptive) language, not imperative, to simplify
* understanding and maintain consistency.
*
* When filled with zeros represents a "noop" configuration - passes all
* reports unchanged and lets the generic HID driver handle everything.
*
* The resulting device report descriptor is assembled from all the report
* descriptor parts referenced by the structure. No order of assembly should
* be assumed. The structure represents original device report descriptor if
* all the parts are NULL.
*/
struct uclogic_params {
/*
* True if the whole interface is invalid, false otherwise.
*/
bool invalid;
/*
* Pointer to the common part of the replacement report descriptor,
* allocated with kmalloc. NULL if no common part is needed.
* Only valid, if "invalid" is false.
*/
__u8 *desc_ptr;
/*
* Size of the common part of the replacement report descriptor.
* Only valid, if "desc_ptr" is not NULL.
*/
unsigned int desc_size;
/*
* True, if pen usage in report descriptor is invalid, when present.
* Only valid, if "invalid" is false.
*/
bool pen_unused;
/*
* Pen parameters and optional report descriptor part.
* Only valid if "pen_unused" is valid and false.
*/
struct uclogic_params_pen pen;
/*
* Frame control parameters and optional report descriptor part.
* Only valid, if "invalid" is false.
*/
struct uclogic_params_frame frame;
/*
* Bitmask matching frame controls "sub-report" flag in the second
* byte of the pen report, or zero if it's not expected.
* Only valid if both "pen" and "frame" are valid, and "frame.id" is
* not zero.
*/
__u8 pen_frame_flag;
};
/* Initialize a tablet interface and discover its parameters */
extern int uclogic_params_init(struct uclogic_params *params,
struct hid_device *hdev);
/* Tablet interface parameters *printf format string */
#define UCLOGIC_PARAMS_FMT_STR \
".invalid = %s\n" \
".desc_ptr = %p\n" \
".desc_size = %u\n" \
".pen_unused = %s\n" \
".pen.desc_ptr = %p\n" \
".pen.desc_size = %u\n" \
".pen.id = %u\n" \
".pen.inrange = %s\n" \
".pen.fragmented_hires = %s\n" \
".frame.desc_ptr = %p\n" \
".frame.desc_size = %u\n" \
".frame.id = %u\n" \
".frame.re_lsb = %u\n" \
".frame.dev_id_byte = %u\n" \
".pen_frame_flag = 0x%02x\n"
/* Tablet interface parameters *printf format arguments */
#define UCLOGIC_PARAMS_FMT_ARGS(_params) \
((_params)->invalid ? "true" : "false"), \
(_params)->desc_ptr, \
(_params)->desc_size, \
((_params)->pen_unused ? "true" : "false"), \
(_params)->pen.desc_ptr, \
(_params)->pen.desc_size, \
(_params)->pen.id, \
uclogic_params_pen_inrange_to_str((_params)->pen.inrange), \
((_params)->pen.fragmented_hires ? "true" : "false"), \
(_params)->frame.desc_ptr, \
(_params)->frame.desc_size, \
(_params)->frame.id, \
(_params)->frame.re_lsb, \
(_params)->frame.dev_id_byte, \
(_params)->pen_frame_flag
/* Get a replacement report descriptor for a tablet's interface. */
extern int uclogic_params_get_desc(const struct uclogic_params *params,
__u8 **pdesc,
unsigned int *psize);
/* Free resources used by tablet interface's parameters */
extern void uclogic_params_cleanup(struct uclogic_params *params);
#endif /* _HID_UCLOGIC_PARAMS_H */
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* HID driver for UC-Logic devices not fully compliant with HID standard
* - original and fixed report descriptors
*
* Copyright (c) 2010-2018 Nikolai Kondrashov
* Copyright (c) 2013 Martin Rusko
*/
/*
* 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.
*/
#ifndef _HID_UCLOGIC_RDESC_H
#define _HID_UCLOGIC_RDESC_H
#include <linux/usb.h>
/* Size of the original descriptor of WPXXXXU tablets */
#define UCLOGIC_RDESC_WPXXXXU_ORIG_SIZE 212
/* Fixed WP4030U report descriptor */
extern __u8 uclogic_rdesc_wp4030u_fixed_arr[];
extern const size_t uclogic_rdesc_wp4030u_fixed_size;
/* Fixed WP5540U report descriptor */
extern __u8 uclogic_rdesc_wp5540u_fixed_arr[];
extern const size_t uclogic_rdesc_wp5540u_fixed_size;
/* Fixed WP8060U report descriptor */
extern __u8 uclogic_rdesc_wp8060u_fixed_arr[];
extern const size_t uclogic_rdesc_wp8060u_fixed_size;
/* Size of the original descriptor of the new WP5540U tablet */
#define UCLOGIC_RDESC_WP5540U_V2_ORIG_SIZE 232
/* Size of the original descriptor of WP1062 tablet */
#define UCLOGIC_RDESC_WP1062_ORIG_SIZE 254
/* Fixed WP1062 report descriptor */
extern __u8 uclogic_rdesc_wp1062_fixed_arr[];
extern const size_t uclogic_rdesc_wp1062_fixed_size;
/* Size of the original descriptor of PF1209 tablet */
#define UCLOGIC_RDESC_PF1209_ORIG_SIZE 234
/* Fixed PF1209 report descriptor */
extern __u8 uclogic_rdesc_pf1209_fixed_arr[];
extern const size_t uclogic_rdesc_pf1209_fixed_size;
/* Size of the original descriptors of TWHL850 tablet */
#define UCLOGIC_RDESC_TWHL850_ORIG0_SIZE 182
#define UCLOGIC_RDESC_TWHL850_ORIG1_SIZE 161
#define UCLOGIC_RDESC_TWHL850_ORIG2_SIZE 92
/* Fixed PID 0522 tablet report descriptor, interface 0 (stylus) */
extern __u8 uclogic_rdesc_twhl850_fixed0_arr[];
extern const size_t uclogic_rdesc_twhl850_fixed0_size;
/* Fixed PID 0522 tablet report descriptor, interface 1 (mouse) */
extern __u8 uclogic_rdesc_twhl850_fixed1_arr[];
extern const size_t uclogic_rdesc_twhl850_fixed1_size;
/* Fixed PID 0522 tablet report descriptor, interface 2 (frame buttons) */
extern __u8 uclogic_rdesc_twhl850_fixed2_arr[];
extern const size_t uclogic_rdesc_twhl850_fixed2_size;
/* Size of the original descriptors of TWHA60 tablet */
#define UCLOGIC_RDESC_TWHA60_ORIG0_SIZE 254
#define UCLOGIC_RDESC_TWHA60_ORIG1_SIZE 139
/* Fixed TWHA60 report descriptor, interface 0 (stylus) */
extern __u8 uclogic_rdesc_twha60_fixed0_arr[];
extern const size_t uclogic_rdesc_twha60_fixed0_size;
/* Fixed TWHA60 report descriptor, interface 1 (frame buttons) */
extern __u8 uclogic_rdesc_twha60_fixed1_arr[];
extern const size_t uclogic_rdesc_twha60_fixed1_size;
/* Report descriptor template placeholder head */
#define UCLOGIC_RDESC_PH_HEAD 0xFE, 0xED, 0x1D
/* Apply report descriptor parameters to a report descriptor template */
extern __u8 *uclogic_rdesc_template_apply(const __u8 *template_ptr,
size_t template_size,
const s32 *param_list,
size_t param_num);
/* Pen report descriptor template placeholder IDs */
enum uclogic_rdesc_pen_ph_id {
UCLOGIC_RDESC_PEN_PH_ID_X_LM,
UCLOGIC_RDESC_PEN_PH_ID_X_PM,
UCLOGIC_RDESC_PEN_PH_ID_Y_LM,
UCLOGIC_RDESC_PEN_PH_ID_Y_PM,
UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM,
UCLOGIC_RDESC_PEN_PH_ID_NUM
};
/* Report descriptor pen template placeholder */
#define UCLOGIC_RDESC_PEN_PH(_ID) \
UCLOGIC_RDESC_PH_HEAD, UCLOGIC_RDESC_PEN_PH_ID_##_ID
/* Report ID for v1 pen reports */
#define UCLOGIC_RDESC_PEN_V1_ID 0x07
/* Fixed report descriptor template for (tweaked) v1 pen reports */
extern const __u8 uclogic_rdesc_pen_v1_template_arr[];
extern const size_t uclogic_rdesc_pen_v1_template_size;
/* Report ID for v2 pen reports */
#define UCLOGIC_RDESC_PEN_V2_ID 0x08
/* Fixed report descriptor template for (tweaked) v2 pen reports */
extern const __u8 uclogic_rdesc_pen_v2_template_arr[];
extern const size_t uclogic_rdesc_pen_v2_template_size;
/* Fixed report descriptor for (tweaked) v1 buttonpad reports */
extern const __u8 uclogic_rdesc_buttonpad_v1_arr[];
extern const size_t uclogic_rdesc_buttonpad_v1_size;
/* Report ID for tweaked v1 buttonpad reports */
#define UCLOGIC_RDESC_BUTTONPAD_V1_ID 0xf7
/* Fixed report descriptor for (tweaked) v2 buttonpad reports */
extern const __u8 uclogic_rdesc_buttonpad_v2_arr[];
extern const size_t uclogic_rdesc_buttonpad_v2_size;
/* Report ID for tweaked v2 buttonpad reports */
#define UCLOGIC_RDESC_BUTTONPAD_V2_ID 0xf7
/* Fixed report descriptor for Ugee EX07 buttonpad */
extern const __u8 uclogic_rdesc_ugee_ex07_buttonpad_arr[];
extern const size_t uclogic_rdesc_ugee_ex07_buttonpad_size;
/* Fixed report descriptor for XP-Pen Deco 01 frame controls */
extern const __u8 uclogic_rdesc_xppen_deco01_frame_arr[];
extern const size_t uclogic_rdesc_xppen_deco01_frame_size;
/* Fixed report descriptor for Ugee G5 frame controls */
extern const __u8 uclogic_rdesc_ugee_g5_frame_arr[];
extern const size_t uclogic_rdesc_ugee_g5_frame_size;
/* Report ID of Ugee G5 frame control reports */
#define UCLOGIC_RDESC_UGEE_G5_FRAME_ID 0x06
/* Device ID byte offset in Ugee G5 frame report */
#define UCLOGIC_RDESC_UGEE_G5_FRAME_DEV_ID_BYTE 0x2
/* Least-significant bit of Ugee G5 frame rotary encoder state */
#define UCLOGIC_RDESC_UGEE_G5_FRAME_RE_LSB 38
#endif /* _HID_UCLOGIC_RDESC_H */
// SPDX-License-Identifier: GPL-2.0+
/*
* HID driver for ViewSonic devices not fully compliant with HID standard
*
* Copyright (c) 2017 Nikolai Kondrashov
*/
/*
* 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/device.h>
#include <linux/hid.h>
#include <linux/module.h>
#include "hid-ids.h"
/* Size of the original descriptor of PD1011 signature pad */
#define PD1011_RDESC_ORIG_SIZE 408
/* Fixed report descriptor of PD1011 signature pad */
static __u8 pd1011_rdesc_fixed[] = {
0x05, 0x0D, /* Usage Page (Digitizer), */
0x09, 0x02, /* Usage (Pen), */
0xA1, 0x01, /* Collection (Application), */
0x85, 0x02, /* Report ID (2), */
0x09, 0x20, /* Usage (Stylus), */
0xA0, /* Collection (Physical), */
0x75, 0x10, /* Report Size (16), */
0x95, 0x01, /* Report Count (1), */
0xA4, /* Push, */
0x05, 0x01, /* Usage Page (Desktop), */
0x65, 0x13, /* Unit (Inch), */
0x55, 0xFD, /* Unit Exponent (-3), */
0x34, /* Physical Minimum (0), */
0x09, 0x30, /* Usage (X), */
0x46, 0x5D, 0x21, /* Physical Maximum (8541), */
0x27, 0x80, 0xA9,
0x00, 0x00, /* Logical Maximum (43392), */
0x81, 0x02, /* Input (Variable), */
0x09, 0x31, /* Usage (Y), */
0x46, 0xDA, 0x14, /* Physical Maximum (5338), */
0x26, 0xF0, 0x69, /* Logical Maximum (27120), */
0x81, 0x02, /* Input (Variable), */
0xB4, /* Pop, */
0x14, /* Logical Minimum (0), */
0x25, 0x01, /* Logical Maximum (1), */
0x75, 0x01, /* Report Size (1), */
0x95, 0x01, /* Report Count (1), */
0x81, 0x03, /* Input (Constant, Variable), */
0x09, 0x32, /* Usage (In Range), */
0x09, 0x42, /* Usage (Tip Switch), */
0x95, 0x02, /* Report Count (2), */
0x81, 0x02, /* Input (Variable), */
0x95, 0x05, /* Report Count (5), */
0x81, 0x03, /* Input (Constant, Variable), */
0x75, 0x10, /* Report Size (16), */
0x95, 0x01, /* Report Count (1), */
0x09, 0x30, /* Usage (Tip Pressure), */
0x15, 0x05, /* Logical Minimum (5), */
0x26, 0xFF, 0x07, /* Logical Maximum (2047), */
0x81, 0x02, /* Input (Variable), */
0x75, 0x10, /* Report Size (16), */
0x95, 0x01, /* Report Count (1), */
0x81, 0x03, /* Input (Constant, Variable), */
0xC0, /* End Collection, */
0xC0 /* End Collection */
};
static __u8 *viewsonic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
switch (hdev->product) {
case USB_DEVICE_ID_VIEWSONIC_PD1011:
case USB_DEVICE_ID_SIGNOTEC_VIEWSONIC_PD1011:
if (*rsize == PD1011_RDESC_ORIG_SIZE) {
rdesc = pd1011_rdesc_fixed;
*rsize = sizeof(pd1011_rdesc_fixed);
}
break;
}
return rdesc;
}
static const struct hid_device_id viewsonic_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_VIEWSONIC,
USB_DEVICE_ID_VIEWSONIC_PD1011) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SIGNOTEC,
USB_DEVICE_ID_SIGNOTEC_VIEWSONIC_PD1011) },
{ }
};
MODULE_DEVICE_TABLE(hid, viewsonic_devices);
static struct hid_driver viewsonic_driver = {
.name = "viewsonic",
.id_table = viewsonic_devices,
.report_fixup = viewsonic_report_fixup,
};
module_hid_driver(viewsonic_driver);
MODULE_LICENSE("GPL");
......@@ -50,6 +50,7 @@
#define I2C_HID_QUIRK_NO_IRQ_AFTER_RESET BIT(1)
#define I2C_HID_QUIRK_NO_RUNTIME_PM BIT(2)
#define I2C_HID_QUIRK_DELAY_AFTER_SLEEP BIT(3)
#define I2C_HID_QUIRK_BOGUS_IRQ BIT(4)
/* flags */
#define I2C_HID_STARTED 0
......@@ -181,6 +182,8 @@ static const struct i2c_hid_quirks {
I2C_HID_QUIRK_NO_RUNTIME_PM },
{ I2C_VENDOR_ID_GOODIX, I2C_DEVICE_ID_GOODIX_01F0,
I2C_HID_QUIRK_NO_RUNTIME_PM },
{ USB_VENDOR_ID_ELAN, HID_ANY_ID,
I2C_HID_QUIRK_BOGUS_IRQ },
{ 0, 0 }
};
......@@ -505,6 +508,12 @@ static void i2c_hid_get_input(struct i2c_hid *ihid)
return;
}
if (ihid->quirks & I2C_HID_QUIRK_BOGUS_IRQ && ret_size == 0xffff) {
dev_warn_once(&ihid->client->dev, "%s: IRQ triggered but "
"there's no data\n", __func__);
return;
}
if ((ret_size > size) || (ret_size < 2)) {
dev_err(&ihid->client->dev, "%s: incomplete report (%d/%d)\n",
__func__, size, ret_size);
......
......@@ -91,7 +91,10 @@ static bool check_generated_interrupt(struct ishtp_device *dev)
IPC_INT_FROM_ISH_TO_HOST_CHV_AB(pisr_val);
} else {
pisr_val = ish_reg_read(dev, IPC_REG_PISR_BXT);
interrupt_generated = IPC_INT_FROM_ISH_TO_HOST_BXT(pisr_val);
interrupt_generated = !!pisr_val;
/* only busy-clear bit is RW, others are RO */
if (pisr_val)
ish_reg_write(dev, IPC_REG_PISR_BXT, pisr_val);
}
return interrupt_generated;
......@@ -256,33 +259,22 @@ static int write_ipc_from_queue(struct ishtp_device *dev)
int i;
void (*ipc_send_compl)(void *);
void *ipc_send_compl_prm;
static int out_ipc_locked;
unsigned long out_ipc_flags;
if (dev->dev_state == ISHTP_DEV_DISABLED)
return -EINVAL;
return -EINVAL;
spin_lock_irqsave(&dev->out_ipc_spinlock, out_ipc_flags);
if (out_ipc_locked) {
spin_unlock_irqrestore(&dev->out_ipc_spinlock, out_ipc_flags);
return -EBUSY;
}
out_ipc_locked = 1;
spin_lock_irqsave(&dev->wr_processing_spinlock, flags);
if (!ish_is_input_ready(dev)) {
out_ipc_locked = 0;
spin_unlock_irqrestore(&dev->out_ipc_spinlock, out_ipc_flags);
spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags);
return -EBUSY;
}
spin_unlock_irqrestore(&dev->out_ipc_spinlock, out_ipc_flags);
spin_lock_irqsave(&dev->wr_processing_spinlock, flags);
/*
* if tx send list is empty - return 0;
* may happen, as RX_COMPLETE handler doesn't check list emptiness.
*/
if (list_empty(&dev->wr_processing_list)) {
spin_unlock_irqrestore(&dev->wr_processing_spinlock, flags);
out_ipc_locked = 0;
return 0;
}
......@@ -325,6 +317,8 @@ static int write_ipc_from_queue(struct ishtp_device *dev)
memcpy(&reg, &r_buf[length >> 2], rem);
ish_reg_write(dev, reg_addr, reg);
}
ish_reg_write(dev, IPC_REG_HOST2ISH_DRBL, doorbell_val);
/* Flush writes to msg registers and doorbell */
ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS);
......@@ -332,9 +326,6 @@ static int write_ipc_from_queue(struct ishtp_device *dev)
++dev->ipc_tx_cnt;
dev->ipc_tx_bytes_cnt += IPC_HEADER_GET_LENGTH(doorbell_val);
ish_reg_write(dev, IPC_REG_HOST2ISH_DRBL, doorbell_val);
out_ipc_locked = 0;
ipc_send_compl = ipc_link->ipc_send_compl;
ipc_send_compl_prm = ipc_link->ipc_send_compl_prm;
list_del_init(&ipc_link->link);
......@@ -839,11 +830,11 @@ int ish_hw_start(struct ishtp_device *dev)
{
ish_set_host_rdy(dev);
set_host_ready(dev);
/* After that we can enable ISH DMA operation and wakeup ISHFW */
ish_wakeup(dev);
set_host_ready(dev);
/* wait for FW-initiated reset flow */
if (!dev->recvd_hw_ready)
wait_event_interruptible_timeout(dev->wait_hw_ready,
......@@ -914,7 +905,6 @@ struct ishtp_device *ish_dev_init(struct pci_dev *pdev)
init_waitqueue_head(&dev->wait_hw_ready);
spin_lock_init(&dev->wr_processing_spinlock);
spin_lock_init(&dev->out_ipc_spinlock);
/* Init IPC processing and free lists */
INIT_LIST_HEAD(&dev->wr_processing_list);
......
......@@ -788,8 +788,8 @@ static int hid_ishtp_cl_probe(struct ishtp_cl_device *cl_device)
if (!cl_device)
return -ENODEV;
if (uuid_le_cmp(hid_ishtp_guid,
cl_device->fw_client->props.protocol_name) != 0)
if (!guid_equal(&hid_ishtp_guid,
&cl_device->fw_client->props.protocol_name))
return -ENODEV;
client_data = devm_kzalloc(&cl_device->dev, sizeof(*client_data),
......
......@@ -206,8 +206,8 @@ int ishtp_hid_probe(unsigned int cur_hid_dev,
hid->bus = BUS_INTEL_ISHTP;
hid->dev.parent = &client_data->cl_device->dev;
hid->version = le16_to_cpu(ISH_HID_VERSION);
hid->vendor = le16_to_cpu(ISH_HID_VENDOR);
hid->product = le16_to_cpu(ISH_HID_PRODUCT);
hid->vendor = le16_to_cpu(client_data->hid_devices[cur_hid_dev].vid);
hid->product = le16_to_cpu(client_data->hid_devices[cur_hid_dev].pid);
snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "hid-ishtp",
hid->vendor, hid->product);
......
......@@ -29,9 +29,9 @@
client->cl_device->ishtp_dev, __VA_ARGS__)
/* ISH Transport protocol (ISHTP in short) GUID */
static const uuid_le hid_ishtp_guid = UUID_LE(0x33AECD58, 0xB679, 0x4E54,
0x9B, 0xD9, 0xA0, 0x4D, 0x34,
0xF0, 0xC2, 0x26);
static const guid_t hid_ishtp_guid =
GUID_INIT(0x33AECD58, 0xB679, 0x4E54,
0x9B, 0xD9, 0xA0, 0x4D, 0x34, 0xF0, 0xC2, 0x26);
/* ISH HID message structure */
struct hostif_msg_hdr {
......
......@@ -119,7 +119,7 @@ int ishtp_send_msg(struct ishtp_device *dev, struct ishtp_msg_hdr *hdr,
* Return: This returns IPC send message status.
*/
int ishtp_write_message(struct ishtp_device *dev, struct ishtp_msg_hdr *hdr,
unsigned char *buf)
void *buf)
{
return ishtp_send_msg(dev, hdr, buf, NULL, NULL);
}
......@@ -133,18 +133,15 @@ int ishtp_write_message(struct ishtp_device *dev, struct ishtp_msg_hdr *hdr,
*
* Return: fw client index or -ENOENT if not found
*/
int ishtp_fw_cl_by_uuid(struct ishtp_device *dev, const uuid_le *uuid)
int ishtp_fw_cl_by_uuid(struct ishtp_device *dev, const guid_t *uuid)
{
int i, res = -ENOENT;
unsigned int i;
for (i = 0; i < dev->fw_clients_num; ++i) {
if (uuid_le_cmp(*uuid, dev->fw_clients[i].props.protocol_name)
== 0) {
res = i;
break;
}
if (guid_equal(uuid, &dev->fw_clients[i].props.protocol_name))
return i;
}
return res;
return -ENOENT;
}
EXPORT_SYMBOL(ishtp_fw_cl_by_uuid);
......@@ -158,7 +155,7 @@ EXPORT_SYMBOL(ishtp_fw_cl_by_uuid);
* Return: pointer of client information on success, NULL on failure.
*/
struct ishtp_fw_client *ishtp_fw_cl_get_client(struct ishtp_device *dev,
const uuid_le *uuid)
const guid_t *uuid)
{
int i;
unsigned long flags;
......@@ -401,7 +398,7 @@ static const struct device_type ishtp_cl_device_type = {
* Return: ishtp_cl_device pointer or NULL on failure
*/
static struct ishtp_cl_device *ishtp_bus_add_device(struct ishtp_device *dev,
uuid_le uuid, char *name)
guid_t uuid, char *name)
{
struct ishtp_cl_device *device;
int status;
......@@ -629,7 +626,7 @@ int ishtp_bus_new_client(struct ishtp_device *dev)
int i;
char *dev_name;
struct ishtp_cl_device *cl_device;
uuid_le device_uuid;
guid_t device_uuid;
/*
* For all reported clients, create an unconnected client and add its
......@@ -639,7 +636,7 @@ int ishtp_bus_new_client(struct ishtp_device *dev)
*/
i = dev->fw_client_presentation_num - 1;
device_uuid = dev->fw_clients[i].props.protocol_name;
dev_name = kasprintf(GFP_KERNEL, "{%pUL}", device_uuid.b);
dev_name = kasprintf(GFP_KERNEL, "{%pUL}", &device_uuid);
if (!dev_name)
return -ENOMEM;
......@@ -675,7 +672,8 @@ int ishtp_cl_device_bind(struct ishtp_cl *cl)
spin_lock_irqsave(&cl->dev->device_list_lock, flags);
list_for_each_entry(cl_device, &cl->dev->device_list,
device_link) {
if (cl_device->fw_client->client_id == cl->fw_client_id) {
if (cl_device->fw_client &&
cl_device->fw_client->client_id == cl->fw_client_id) {
cl->device = cl_device;
rv = 0;
break;
......@@ -735,6 +733,7 @@ void ishtp_bus_remove_all_clients(struct ishtp_device *ishtp_dev,
spin_lock_irqsave(&ishtp_dev->device_list_lock, flags);
list_for_each_entry_safe(cl_device, n, &ishtp_dev->device_list,
device_link) {
cl_device->fw_client = NULL;
if (warm_reset && cl_device->reference_count)
continue;
......
......@@ -85,7 +85,7 @@ int ishtp_send_msg(struct ishtp_device *dev,
/* Write a single-fragment message */
int ishtp_write_message(struct ishtp_device *dev,
struct ishtp_msg_hdr *hdr,
unsigned char *buf);
void *buf);
/* Use DMA to send/receive messages */
int ishtp_use_dma_transfer(void);
......@@ -112,8 +112,8 @@ void ishtp_cl_driver_unregister(struct ishtp_cl_driver *driver);
int ishtp_register_event_cb(struct ishtp_cl_device *device,
void (*read_cb)(struct ishtp_cl_device *));
int ishtp_fw_cl_by_uuid(struct ishtp_device *dev, const uuid_le *cuuid);
int ishtp_fw_cl_by_uuid(struct ishtp_device *dev, const guid_t *cuuid);
struct ishtp_fw_client *ishtp_fw_cl_get_client(struct ishtp_device *dev,
const uuid_le *uuid);
const guid_t *uuid);
#endif /* _LINUX_ISHTP_CL_BUS_H */
......@@ -126,7 +126,7 @@ struct ishtp_cl {
};
/* Client connection managenment internal functions */
int ishtp_can_client_connect(struct ishtp_device *ishtp_dev, uuid_le *uuid);
int ishtp_can_client_connect(struct ishtp_device *ishtp_dev, guid_t *uuid);
int ishtp_fw_cl_by_id(struct ishtp_device *dev, uint8_t client_id);
void ishtp_cl_send_msg(struct ishtp_device *dev, struct ishtp_cl *cl);
void recv_ishtp_cl_msg(struct ishtp_device *dev,
......
......@@ -136,19 +136,14 @@ int ishtp_hbm_start_wait(struct ishtp_device *dev)
int ishtp_hbm_start_req(struct ishtp_device *dev)
{
struct ishtp_msg_hdr hdr;
unsigned char data[128];
struct ishtp_msg_hdr *ishtp_hdr = &hdr;
struct hbm_host_version_request *start_req;
const size_t len = sizeof(struct hbm_host_version_request);
struct hbm_host_version_request start_req = { 0 };
ishtp_hbm_hdr(ishtp_hdr, len);
ishtp_hbm_hdr(&hdr, sizeof(start_req));
/* host start message */
start_req = (struct hbm_host_version_request *)data;
memset(start_req, 0, len);
start_req->hbm_cmd = HOST_START_REQ_CMD;
start_req->host_version.major_version = HBM_MAJOR_VERSION;
start_req->host_version.minor_version = HBM_MINOR_VERSION;
start_req.hbm_cmd = HOST_START_REQ_CMD;
start_req.host_version.major_version = HBM_MAJOR_VERSION;
start_req.host_version.minor_version = HBM_MINOR_VERSION;
/*
* (!) Response to HBM start may be so quick that this thread would get
......@@ -156,7 +151,7 @@ int ishtp_hbm_start_req(struct ishtp_device *dev)
* So set it at first, change back to ISHTP_HBM_IDLE upon failure
*/
dev->hbm_state = ISHTP_HBM_START;
if (ishtp_write_message(dev, ishtp_hdr, data)) {
if (ishtp_write_message(dev, &hdr, &start_req)) {
dev_err(dev->devc, "version message send failed\n");
dev->dev_state = ISHTP_DEV_RESETTING;
dev->hbm_state = ISHTP_HBM_IDLE;
......@@ -178,19 +173,13 @@ int ishtp_hbm_start_req(struct ishtp_device *dev)
void ishtp_hbm_enum_clients_req(struct ishtp_device *dev)
{
struct ishtp_msg_hdr hdr;
unsigned char data[128];
struct ishtp_msg_hdr *ishtp_hdr = &hdr;
struct hbm_host_enum_request *enum_req;
const size_t len = sizeof(struct hbm_host_enum_request);
struct hbm_host_enum_request enum_req = { 0 };
/* enumerate clients */
ishtp_hbm_hdr(ishtp_hdr, len);
ishtp_hbm_hdr(&hdr, sizeof(enum_req));
enum_req.hbm_cmd = HOST_ENUM_REQ_CMD;
enum_req = (struct hbm_host_enum_request *)data;
memset(enum_req, 0, len);
enum_req->hbm_cmd = HOST_ENUM_REQ_CMD;
if (ishtp_write_message(dev, ishtp_hdr, data)) {
if (ishtp_write_message(dev, &hdr, &enum_req)) {
dev->dev_state = ISHTP_DEV_RESETTING;
dev_err(dev->devc, "enumeration request send failed\n");
ish_hw_reset(dev);
......@@ -208,12 +197,8 @@ void ishtp_hbm_enum_clients_req(struct ishtp_device *dev)
*/
static int ishtp_hbm_prop_req(struct ishtp_device *dev)
{
struct ishtp_msg_hdr hdr;
unsigned char data[128];
struct ishtp_msg_hdr *ishtp_hdr = &hdr;
struct hbm_props_request *prop_req;
const size_t len = sizeof(struct hbm_props_request);
struct hbm_props_request prop_req = { 0 };
unsigned long next_client_index;
uint8_t client_num;
......@@ -237,15 +222,12 @@ static int ishtp_hbm_prop_req(struct ishtp_device *dev)
dev->fw_clients[client_num].client_id = next_client_index;
ishtp_hbm_hdr(ishtp_hdr, len);
prop_req = (struct hbm_props_request *)data;
ishtp_hbm_hdr(&hdr, sizeof(prop_req));
memset(prop_req, 0, sizeof(struct hbm_props_request));
prop_req.hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
prop_req.address = next_client_index;
prop_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
prop_req->address = next_client_index;
if (ishtp_write_message(dev, ishtp_hdr, data)) {
if (ishtp_write_message(dev, &hdr, &prop_req)) {
dev->dev_state = ISHTP_DEV_RESETTING;
dev_err(dev->devc, "properties request send failed\n");
ish_hw_reset(dev);
......@@ -266,19 +248,14 @@ static int ishtp_hbm_prop_req(struct ishtp_device *dev)
static void ishtp_hbm_stop_req(struct ishtp_device *dev)
{
struct ishtp_msg_hdr hdr;
unsigned char data[128];
struct ishtp_msg_hdr *ishtp_hdr = &hdr;
struct hbm_host_stop_request *req;
const size_t len = sizeof(struct hbm_host_stop_request);
struct hbm_host_stop_request stop_req = { 0 } ;
ishtp_hbm_hdr(ishtp_hdr, len);
req = (struct hbm_host_stop_request *)data;
ishtp_hbm_hdr(&hdr, sizeof(stop_req));
memset(req, 0, sizeof(struct hbm_host_stop_request));
req->hbm_cmd = HOST_STOP_REQ_CMD;
req->reason = DRIVER_STOP_REQUEST;
stop_req.hbm_cmd = HOST_STOP_REQ_CMD;
stop_req.reason = DRIVER_STOP_REQUEST;
ishtp_write_message(dev, ishtp_hdr, data);
ishtp_write_message(dev, &hdr, &stop_req);
}
/**
......@@ -294,15 +271,15 @@ int ishtp_hbm_cl_flow_control_req(struct ishtp_device *dev,
struct ishtp_cl *cl)
{
struct ishtp_msg_hdr hdr;
unsigned char data[128];
struct ishtp_msg_hdr *ishtp_hdr = &hdr;
const size_t len = sizeof(struct hbm_flow_control);
struct hbm_flow_control flow_ctrl;
const size_t len = sizeof(flow_ctrl);
int rv;
unsigned long flags;
spin_lock_irqsave(&cl->fc_spinlock, flags);
ishtp_hbm_hdr(ishtp_hdr, len);
ishtp_hbm_cl_hdr(cl, ISHTP_FLOW_CONTROL_CMD, data, len);
ishtp_hbm_hdr(&hdr, len);
ishtp_hbm_cl_hdr(cl, ISHTP_FLOW_CONTROL_CMD, &flow_ctrl, len);
/*
* Sync possible race when RB recycle and packet receive paths
......@@ -315,7 +292,7 @@ int ishtp_hbm_cl_flow_control_req(struct ishtp_device *dev,
cl->recv_msg_num_frags = 0;
rv = ishtp_write_message(dev, ishtp_hdr, data);
rv = ishtp_write_message(dev, &hdr, &flow_ctrl);
if (!rv) {
++cl->out_flow_ctrl_creds;
++cl->out_flow_ctrl_cnt;
......@@ -345,14 +322,13 @@ int ishtp_hbm_cl_flow_control_req(struct ishtp_device *dev,
int ishtp_hbm_cl_disconnect_req(struct ishtp_device *dev, struct ishtp_cl *cl)
{
struct ishtp_msg_hdr hdr;
unsigned char data[128];
struct ishtp_msg_hdr *ishtp_hdr = &hdr;
const size_t len = sizeof(struct hbm_client_connect_request);
struct hbm_client_connect_request disconn_req;
const size_t len = sizeof(disconn_req);
ishtp_hbm_hdr(ishtp_hdr, len);
ishtp_hbm_cl_hdr(cl, CLIENT_DISCONNECT_REQ_CMD, data, len);
ishtp_hbm_hdr(&hdr, len);
ishtp_hbm_cl_hdr(cl, CLIENT_DISCONNECT_REQ_CMD, &disconn_req, len);
return ishtp_write_message(dev, ishtp_hdr, data);
return ishtp_write_message(dev, &hdr, &disconn_req);
}
/**
......@@ -391,14 +367,13 @@ static void ishtp_hbm_cl_disconnect_res(struct ishtp_device *dev,
int ishtp_hbm_cl_connect_req(struct ishtp_device *dev, struct ishtp_cl *cl)
{
struct ishtp_msg_hdr hdr;
unsigned char data[128];
struct ishtp_msg_hdr *ishtp_hdr = &hdr;
const size_t len = sizeof(struct hbm_client_connect_request);
struct hbm_client_connect_request conn_req;
const size_t len = sizeof(conn_req);
ishtp_hbm_hdr(ishtp_hdr, len);
ishtp_hbm_cl_hdr(cl, CLIENT_CONNECT_REQ_CMD, data, len);
ishtp_hbm_hdr(&hdr, len);
ishtp_hbm_cl_hdr(cl, CLIENT_CONNECT_REQ_CMD, &conn_req, len);
return ishtp_write_message(dev, ishtp_hdr, data);
return ishtp_write_message(dev, &hdr, &conn_req);
}
/**
......
......@@ -149,7 +149,7 @@ struct hbm_host_enum_response {
} __packed;
struct ishtp_client_properties {
uuid_le protocol_name;
guid_t protocol_name;
uint8_t protocol_version;
uint8_t max_number_of_connections;
uint8_t fixed_address;
......
......@@ -211,8 +211,6 @@ struct ishtp_device {
/* For both processing list and free list */
spinlock_t wr_processing_spinlock;
spinlock_t out_ipc_spinlock;
struct ishtp_fw_client *fw_clients; /*Note:memory has to be allocated*/
DECLARE_BITMAP(fw_clients_map, ISHTP_CLIENTS_MAX);
DECLARE_BITMAP(host_clients_map, ISHTP_CLIENTS_MAX);
......
......@@ -252,6 +252,38 @@ static void wacom_hid_usage_quirk(struct hid_device *hdev,
}
}
/*
* Wacom's AES devices use different vendor-defined usages to
* report serial number information compared to their branded
* hardware. The usages are also sometimes ill-defined and do
* not have the correct logical min/max values set. Lets patch
* the descriptor to use the branded usage convention and fix
* the errors.
*/
if (usage->hid == WACOM_HID_WT_SERIALNUMBER &&
field->report_size == 16 &&
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;
}
}
/* 2nd-generation Intuos Pro Large has incorrect Y maximum */
if (hdev->vendor == USB_VENDOR_ID_WACOM &&
hdev->product == 0x0358 &&
......
......@@ -626,6 +626,7 @@ static int wacom_intuos_get_tool_type(int tool_id)
case 0x8e2: /* IntuosHT2 pen */
case 0x022:
case 0x10804: /* Intuos4/5 13HD/24HD Art Pen */
case 0x10842: /* MobileStudio Pro Pro Pen slim */
case 0x14802: /* Intuos4/5 13HD/24HD Classic Pen */
case 0x16802: /* Cintiq 13HD Pro Pen */
case 0x18802: /* DTH2242 Pen */
......@@ -667,6 +668,7 @@ static int wacom_intuos_get_tool_type(int tool_id)
case 0x1480a: /* Intuos4/5 13HD/24HD Classic Pen Eraser */
case 0x1090a: /* Intuos4/5 13HD/24HD Airbrush Eraser */
case 0x1080c: /* Intuos4/5 13HD/24HD Art Pen Eraser */
case 0x1084a: /* MobileStudio Pro Pro Pen slim Eraser */
case 0x1680a: /* Cintiq 13HD Pro Pen Eraser */
case 0x1880a: /* DTH2242 Eraser */
case 0x1080a: /* Intuos4/5 13HD/24HD General Pen Eraser */
......@@ -2159,27 +2161,6 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev,
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;
......@@ -3525,6 +3506,7 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
switch (features->type) {
case GRAPHIRE_BT:
__clear_bit(ABS_MISC, input_dev->absbit);
/* fall through */
case WACOM_MO:
case WACOM_G4:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册