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

Pull HID updates from Jiri Kosina:
 "Highlights:

   - support for USI style pens (Tero Kristo, Mika Westerberg)

   - quirk for devices that need inverted X/Y axes (Alistair Francis)

   - small core code cleanups and deduplication (Benjamin Tissoires)

   - Apple Magic Keyboard support improvements (José Expósito, Alex
     Henrie, Benjamin Berg)

   - locking performance improvement for hidraw code (André Almeida)

   - PM wakeup support for i2c-hid driver (Matthias Kaehlcke

   - new driver to support for LetSketch device (Hans de Goede)

   - proper batter reporting for hid-magicmouse USB-connected devices
     (José Expósito)"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (32 commits)
  HID: magicmouse: Fix an error handling path in magicmouse_probe()
  HID: address kernel-doc warnings
  HID: intel-ish-hid: ishtp-fw-loader: Fix a kernel-doc formatting issue
  HID: intel-ish-hid: ipc: Specify no cache snooping on TGL and ADL
  HID: hid-uclogic-params: Invalid parameter check in uclogic_params_frame_init_v1_buttonpad
  HID: hid-uclogic-params: Invalid parameter check in uclogic_params_huion_init
  HID: hid-uclogic-params: Invalid parameter check in uclogic_params_get_str_desc
  HID: hid-uclogic-params: Invalid parameter check in uclogic_params_init
  HID: Add new Letsketch tablet driver
  HID: apple: Add Magic Keyboard 2021 with fingerprint reader FN key mapping
  HID: apple: Add 2021 magic keyboard FN key mapping
  HID: magicmouse: set Magic Trackpad 2021 name
  HID: magicmouse: set device name when it has been personalized
  HID: apple: Add 2021 Magic Keyboard with number pad
  HID: apple: Add 2021 Magic Keyboard with fingerprint reader
  HID: i2c-hid-of: Expose the touchscreen-inverted properties
  HID: quirks: Allow inverting the absolute X/Y values
  HID: hidraw: Replace hidraw device table mutex with a rwsem
  HID: thrustmaster use swap() to make code cleaner
  HID: debug: Add USI usages
  ...
This commit is contained in:
Linus Torvalds 2022-01-11 10:53:57 -08:00
commit 26b88fba2a
26 changed files with 863 additions and 178 deletions

View File

@ -32,6 +32,8 @@ 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
or powering it on, before it is ready for communication.
- touchscreen-inverted-x: See touchscreen.txt
- touchscreen-inverted-y: See touchscreen.txt
Example:

View File

@ -8542,6 +8542,12 @@ F: drivers/hid/
F: include/linux/hid*
F: include/uapi/linux/hid*
HID LOGITECH DRIVERS
R: Filipe Laíns <lains@riseup.net>
L: linux-input@vger.kernel.org
S: Maintained
F: drivers/hid/hid-logitech-*
HID PLAYSTATION DRIVER
M: Roderick Colenbrander <roderick.colenbrander@sony.com>
L: linux-input@vger.kernel.org
@ -10804,6 +10810,13 @@ S: Maintained
W: http://legousb.sourceforge.net/
F: drivers/usb/misc/legousbtower.c
LETSKETCH HID TABLET DRIVER
M: Hans de Goede <hdegoede@redhat.com>
L: linux-input@vger.kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git
F: drivers/hid/hid-letsketch.c
LG LAPTOP EXTRAS
M: Matan Ziv-Av <matan@svgalib.org>
L: platform-driver-x86@vger.kernel.org

View File

@ -558,6 +558,20 @@ config HID_LENOVO
- ThinkPad Compact Bluetooth Keyboard with TrackPoint (supports Fn keys)
- ThinkPad Compact USB Keyboard with TrackPoint (supports Fn keys)
config HID_LETSKETCH
tristate "Letsketch WP9620N tablets"
depends on USB_HID
help
Driver for the LetSketch / VSON WP9620N drawing tablet. This
drawing tablet is also sold under other brand names such as Case U,
presumably this driver will work for all of them. But it has only been
tested with a LetSketch WP9620N model.
These tablets also work without a special HID driver, but then only
part of the active area works and both the pad and stylus buttons are
hardwired to special key-combos. E.g. the 2 stylus buttons send right
mouse clicks / resp. "e" key presses.
config HID_LOGITECH
tristate "Logitech devices"
depends on USB_HID

View File

@ -66,6 +66,7 @@ obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o
obj-$(CONFIG_HID_KYE) += hid-kye.o
obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o
obj-$(CONFIG_HID_LENOVO) += hid-lenovo.o
obj-$(CONFIG_HID_LETSKETCH) += hid-letsketch.o
obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o
obj-$(CONFIG_HID_LOGITECH) += hid-lg-g15.o
obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o

View File

@ -16,24 +16,28 @@
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include "hid-ids.h"
#define APPLE_RDESC_JIS 0x0001
#define APPLE_IGNORE_MOUSE 0x0002
#define APPLE_HAS_FN 0x0004
/* 0x0008 reserved, was: APPLE_HIDDEV */
#define APPLE_ISO_TILDE_QUIRK 0x0010
#define APPLE_MIGHTYMOUSE 0x0020
#define APPLE_INVERT_HWHEEL 0x0040
/* 0x0080 reserved, was: APPLE_IGNORE_HIDINPUT */
#define APPLE_NUMLOCK_EMULATION 0x0100
#define APPLE_RDESC_JIS BIT(0)
#define APPLE_IGNORE_MOUSE BIT(1)
#define APPLE_HAS_FN BIT(2)
/* BIT(3) reserved, was: APPLE_HIDDEV */
#define APPLE_ISO_TILDE_QUIRK BIT(4)
#define APPLE_MIGHTYMOUSE BIT(5)
#define APPLE_INVERT_HWHEEL BIT(6)
/* BIT(7) reserved, was: APPLE_IGNORE_HIDINPUT */
#define APPLE_NUMLOCK_EMULATION BIT(8)
#define APPLE_RDESC_BATTERY BIT(9)
#define APPLE_FLAG_FKEY 0x01
#define HID_COUNTRY_INTERNATIONAL_ISO 13
#define APPLE_BATTERY_TIMEOUT_MS 60000
static unsigned int fnmode = 1;
module_param(fnmode, uint, 0644);
@ -58,10 +62,12 @@ MODULE_PARM_DESC(swap_fn_leftctrl, "Swap the Fn and left Control keys. "
"[0] = as-is, Mac layout, 1 = swapped, PC layout)");
struct apple_sc {
struct hid_device *hdev;
unsigned long quirks;
unsigned int fn_on;
unsigned int fn_found;
DECLARE_BITMAP(pressed_numlock, KEY_CNT);
struct timer_list battery_timer;
};
struct apple_key_translation {
@ -70,6 +76,28 @@ struct apple_key_translation {
u8 flags;
};
static const struct apple_key_translation apple2021_fn_keys[] = {
{ KEY_BACKSPACE, KEY_DELETE },
{ KEY_ENTER, KEY_INSERT },
{ KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY },
{ KEY_F2, KEY_BRIGHTNESSUP, APPLE_FLAG_FKEY },
{ KEY_F3, KEY_SCALE, APPLE_FLAG_FKEY },
{ KEY_F4, KEY_SEARCH, APPLE_FLAG_FKEY },
{ KEY_F5, KEY_MICMUTE, APPLE_FLAG_FKEY },
{ KEY_F6, KEY_SLEEP, APPLE_FLAG_FKEY },
{ KEY_F7, KEY_PREVIOUSSONG, APPLE_FLAG_FKEY },
{ KEY_F8, KEY_PLAYPAUSE, APPLE_FLAG_FKEY },
{ KEY_F9, KEY_NEXTSONG, APPLE_FLAG_FKEY },
{ KEY_F10, KEY_MUTE, APPLE_FLAG_FKEY },
{ KEY_F11, KEY_VOLUMEDOWN, APPLE_FLAG_FKEY },
{ KEY_F12, KEY_VOLUMEUP, APPLE_FLAG_FKEY },
{ KEY_UP, KEY_PAGEUP },
{ KEY_DOWN, KEY_PAGEDOWN },
{ KEY_LEFT, KEY_HOME },
{ KEY_RIGHT, KEY_END },
{ }
};
static const struct apple_key_translation macbookair_fn_keys[] = {
{ KEY_BACKSPACE, KEY_DELETE },
{ KEY_ENTER, KEY_INSERT },
@ -214,7 +242,11 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
}
if (fnmode) {
if (hid->product >= USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI &&
if (hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021 ||
hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021 ||
hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021)
table = apple2021_fn_keys;
else if (hid->product >= USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI &&
hid->product <= USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS)
table = macbookair_fn_keys;
else if (hid->product < 0x21d || hid->product >= 0x300)
@ -333,6 +365,43 @@ static int apple_event(struct hid_device *hdev, struct hid_field *field,
return 0;
}
static int apple_fetch_battery(struct hid_device *hdev)
{
#ifdef CONFIG_HID_BATTERY_STRENGTH
struct apple_sc *asc = hid_get_drvdata(hdev);
struct hid_report_enum *report_enum;
struct hid_report *report;
if (!(asc->quirks & APPLE_RDESC_BATTERY) || !hdev->battery)
return -1;
report_enum = &hdev->report_enum[hdev->battery_report_type];
report = report_enum->report_id_hash[hdev->battery_report_id];
if (!report || report->maxfield < 1)
return -1;
if (hdev->battery_capacity == hdev->battery_max)
return -1;
hid_hw_request(hdev, report, HID_REQ_GET_REPORT);
return 0;
#else
return -1;
#endif
}
static void apple_battery_timer_tick(struct timer_list *t)
{
struct apple_sc *asc = from_timer(asc, t, battery_timer);
struct hid_device *hdev = asc->hdev;
if (apple_fetch_battery(hdev) == 0) {
mod_timer(&asc->battery_timer,
jiffies + msecs_to_jiffies(APPLE_BATTERY_TIMEOUT_MS));
}
}
/*
* MacBook JIS keyboard has wrong logical maximum
* Magic Keyboard JIS has wrong logical maximum
@ -354,6 +423,30 @@ static __u8 *apple_report_fixup(struct hid_device *hdev, __u8 *rdesc,
"fixing up MacBook JIS keyboard report descriptor\n");
rdesc[53] = rdesc[59] = 0xe7;
}
/*
* Change the usage from:
* 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 0
* 0x09, 0x0b, // Usage (Vendor Usage 0x0b) 3
* To:
* 0x05, 0x01, // Usage Page (Generic Desktop) 0
* 0x09, 0x06, // Usage (Keyboard) 2
*/
if ((asc->quirks & APPLE_RDESC_BATTERY) && *rsize == 83 &&
rdesc[46] == 0x84 && rdesc[58] == 0x85) {
hid_info(hdev,
"fixing up Magic Keyboard battery report descriptor\n");
*rsize = *rsize - 1;
rdesc = kmemdup(rdesc + 1, *rsize, GFP_KERNEL);
if (!rdesc)
return NULL;
rdesc[0] = 0x05;
rdesc[1] = 0x01;
rdesc[2] = 0x09;
rdesc[3] = 0x06;
}
return rdesc;
}
@ -376,6 +469,9 @@ static void apple_setup_input(struct input_dev *input)
for (trans = apple_iso_keyboard; trans->from; trans++)
set_bit(trans->to, input->keybit);
for (trans = apple2021_fn_keys; trans->from; trans++)
set_bit(trans->to, input->keybit);
if (swap_fn_leftctrl) {
for (trans = swapped_fn_leftctrl_keys; trans->from; trans++)
set_bit(trans->to, input->keybit);
@ -428,7 +524,7 @@ static int apple_input_configured(struct hid_device *hdev,
if ((asc->quirks & APPLE_HAS_FN) && !asc->fn_found) {
hid_info(hdev, "Fn key not found (Apple Wireless Keyboard clone?), disabling Fn key handling\n");
asc->quirks = 0;
asc->quirks &= ~APPLE_HAS_FN;
}
return 0;
@ -447,6 +543,7 @@ static int apple_probe(struct hid_device *hdev,
return -ENOMEM;
}
asc->hdev = hdev;
asc->quirks = quirks;
hid_set_drvdata(hdev, asc);
@ -463,9 +560,23 @@ static int apple_probe(struct hid_device *hdev,
return ret;
}
timer_setup(&asc->battery_timer, apple_battery_timer_tick, 0);
mod_timer(&asc->battery_timer,
jiffies + msecs_to_jiffies(APPLE_BATTERY_TIMEOUT_MS));
apple_fetch_battery(hdev);
return 0;
}
static void apple_remove(struct hid_device *hdev)
{
struct apple_sc *asc = hid_get_drvdata(hdev);
del_timer_sync(&asc->battery_timer);
hid_hw_stop(hdev);
}
static const struct hid_device_id apple_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE),
.driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL },
@ -540,11 +651,11 @@ static const struct hid_device_id apple_devices[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS),
.driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2015),
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_RDESC_BATTERY },
{ HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2015),
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2015),
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_RDESC_BATTERY },
{ HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2015),
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI),
@ -640,6 +751,14 @@ static const struct hid_device_id apple_devices[] = {
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
{ HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021),
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021),
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
{ HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021),
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021),
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
{ HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021),
.driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK },
{ }
};
@ -650,6 +769,7 @@ static struct hid_driver apple_driver = {
.id_table = apple_devices,
.report_fixup = apple_report_fixup,
.probe = apple_probe,
.remove = apple_remove,
.event = apple_event,
.input_mapping = apple_input_mapping,
.input_mapped = apple_input_mapped,

View File

@ -2126,6 +2126,99 @@ void hid_hw_close(struct hid_device *hdev)
}
EXPORT_SYMBOL_GPL(hid_hw_close);
/**
* hid_hw_request - send report request to device
*
* @hdev: hid device
* @report: report to send
* @reqtype: hid request type
*/
void hid_hw_request(struct hid_device *hdev,
struct hid_report *report, int reqtype)
{
if (hdev->ll_driver->request)
return hdev->ll_driver->request(hdev, report, reqtype);
__hid_request(hdev, report, reqtype);
}
EXPORT_SYMBOL_GPL(hid_hw_request);
/**
* hid_hw_raw_request - send report request to device
*
* @hdev: hid device
* @reportnum: report ID
* @buf: in/out data to transfer
* @len: length of buf
* @rtype: HID report type
* @reqtype: HID_REQ_GET_REPORT or HID_REQ_SET_REPORT
*
* Return: count of data transferred, negative if error
*
* Same behavior as hid_hw_request, but with raw buffers instead.
*/
int hid_hw_raw_request(struct hid_device *hdev,
unsigned char reportnum, __u8 *buf,
size_t len, unsigned char rtype, int reqtype)
{
if (len < 1 || len > HID_MAX_BUFFER_SIZE || !buf)
return -EINVAL;
return hdev->ll_driver->raw_request(hdev, reportnum, buf, len,
rtype, reqtype);
}
EXPORT_SYMBOL_GPL(hid_hw_raw_request);
/**
* hid_hw_output_report - send output report to device
*
* @hdev: hid device
* @buf: raw data to transfer
* @len: length of buf
*
* Return: count of data transferred, negative if error
*/
int hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len)
{
if (len < 1 || len > HID_MAX_BUFFER_SIZE || !buf)
return -EINVAL;
if (hdev->ll_driver->output_report)
return hdev->ll_driver->output_report(hdev, buf, len);
return -ENOSYS;
}
EXPORT_SYMBOL_GPL(hid_hw_output_report);
#ifdef CONFIG_PM
int hid_driver_suspend(struct hid_device *hdev, pm_message_t state)
{
if (hdev->driver && hdev->driver->suspend)
return hdev->driver->suspend(hdev, state);
return 0;
}
EXPORT_SYMBOL_GPL(hid_driver_suspend);
int hid_driver_reset_resume(struct hid_device *hdev)
{
if (hdev->driver && hdev->driver->reset_resume)
return hdev->driver->reset_resume(hdev);
return 0;
}
EXPORT_SYMBOL_GPL(hid_driver_reset_resume);
int hid_driver_resume(struct hid_device *hdev)
{
if (hdev->driver && hdev->driver->resume)
return hdev->driver->resume(hdev);
return 0;
}
EXPORT_SYMBOL_GPL(hid_driver_resume);
#endif /* CONFIG_PM */
struct hid_dynid {
struct list_head list;
struct hid_device_id id;

View File

@ -141,8 +141,10 @@ static const struct hid_usage_entry hid_usage_table[] = {
{0, 0x33, "Touch"},
{0, 0x34, "UnTouch"},
{0, 0x35, "Tap"},
{0, 0x38, "Transducer Index"},
{0, 0x39, "TabletFunctionKey"},
{0, 0x3a, "ProgramChangeKey"},
{0, 0x3B, "Battery Strength"},
{0, 0x3c, "Invert"},
{0, 0x42, "TipSwitch"},
{0, 0x43, "SecondaryTipSwitch"},
@ -160,7 +162,40 @@ static const struct hid_usage_entry hid_usage_table[] = {
{0, 0x59, "ButtonType"},
{0, 0x5A, "SecondaryBarrelSwitch"},
{0, 0x5B, "TransducerSerialNumber"},
{0, 0x5C, "Preferred Color"},
{0, 0x5D, "Preferred Color is Locked"},
{0, 0x5E, "Preferred Line Width"},
{0, 0x5F, "Preferred Line Width is Locked"},
{0, 0x6e, "TransducerSerialNumber2"},
{0, 0x70, "Preferred Line Style"},
{0, 0x71, "Preferred Line Style is Locked"},
{0, 0x72, "Ink"},
{0, 0x73, "Pencil"},
{0, 0x74, "Highlighter"},
{0, 0x75, "Chisel Marker"},
{0, 0x76, "Brush"},
{0, 0x77, "No Preference"},
{0, 0x80, "Digitizer Diagnostic"},
{0, 0x81, "Digitizer Error"},
{0, 0x82, "Err Normal Status"},
{0, 0x83, "Err Transducers Exceeded"},
{0, 0x84, "Err Full Trans Features Unavailable"},
{0, 0x85, "Err Charge Low"},
{0, 0x90, "Transducer Software Info"},
{0, 0x91, "Transducer Vendor Id"},
{0, 0x92, "Transducer Product Id"},
{0, 0x93, "Device Supported Protocols"},
{0, 0x94, "Transducer Supported Protocols"},
{0, 0x95, "No Protocol"},
{0, 0x96, "Wacom AES Protocol"},
{0, 0x97, "USI Protocol"},
{0, 0x98, "Microsoft Pen Protocol"},
{0, 0xA0, "Supported Report Rates"},
{0, 0xA1, "Report Rate"},
{0, 0xA2, "Transducer Connected"},
{0, 0xA3, "Switch Disabled"},
{0, 0xA4, "Switch Unimplemented"},
{0, 0xA5, "Transducer Switches"},
{ 15, 0, "PhysicalInterfaceDevice" },
{0, 0x00, "Undefined"},
{0, 0x01, "Physical_Interface_Device"},

View File

@ -175,6 +175,8 @@
#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
#define USB_DEVICE_ID_APPLE_IRCONTROL5 0x8243
#define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021 0x029c
#define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021 0x029a
#define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021 0x029f
#define USB_VENDOR_ID_ASUS 0x0486
#define USB_DEVICE_ID_ASUS_T91MT 0x0185
@ -763,6 +765,9 @@
#define USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_602E 0x602e
#define USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_6093 0x6093
#define USB_VENDOR_ID_LETSKETCH 0x6161
#define USB_DEVICE_ID_WP9620N 0x4d15
#define USB_VENDOR_ID_LG 0x1fd2
#define USB_DEVICE_ID_LG_MULTITOUCH 0x0064
#define USB_DEVICE_ID_LG_MELFAS_MT 0x6007

View File

@ -52,6 +52,7 @@ static const struct {
#define map_rel(c) hid_map_usage(hidinput, usage, &bit, &max, EV_REL, (c))
#define map_key(c) hid_map_usage(hidinput, usage, &bit, &max, EV_KEY, (c))
#define map_led(c) hid_map_usage(hidinput, usage, &bit, &max, EV_LED, (c))
#define map_msc(c) hid_map_usage(hidinput, usage, &bit, &max, EV_MSC, (c))
#define map_abs_clear(c) hid_map_usage_clear(hidinput, usage, &bit, \
&max, EV_ABS, (c))
@ -876,10 +877,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
case 0x5b: /* TransducerSerialNumber */
case 0x6e: /* TransducerSerialNumber2 */
usage->type = EV_MSC;
usage->code = MSC_SERIAL;
bit = input->mscbit;
max = MSC_MAX;
map_msc(MSC_SERIAL);
break;
default: goto unknown;
@ -1333,6 +1331,12 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
input = field->hidinput->input;
if (usage->type == EV_ABS &&
(((*quirks & HID_QUIRK_X_INVERT) && usage->code == ABS_X) ||
((*quirks & HID_QUIRK_Y_INVERT) && usage->code == ABS_Y))) {
value = field->logical_maximum - value;
}
if (usage->hat_min < usage->hat_max || usage->hat_dir) {
int hat_dir = usage->hat_dir;
if (!hat_dir)
@ -1465,7 +1469,8 @@ void hidinput_report_event(struct hid_device *hid, struct hid_report *report)
}
EXPORT_SYMBOL_GPL(hidinput_report_event);
int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field)
static int hidinput_find_field(struct hid_device *hid, unsigned int type,
unsigned int code, struct hid_field **field)
{
struct hid_report *report;
int i, j;
@ -1480,7 +1485,6 @@ int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int
}
return -1;
}
EXPORT_SYMBOL_GPL(hidinput_find_field);
struct hid_field *hidinput_get_led_field(struct hid_device *hid)
{
@ -1743,6 +1747,16 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid,
case HID_GD_MOUSE:
suffix = "Mouse";
break;
case HID_DG_PEN:
/*
* yes, there is an issue here:
* DG_PEN -> "Stylus"
* DG_STYLUS -> "Pen"
* But changing this now means users with config snippets
* will have to change it and the test suite will not be happy.
*/
suffix = "Stylus";
break;
case HID_DG_STYLUS:
suffix = "Pen";
break;

322
drivers/hid/hid-letsketch.c Normal file
View File

@ -0,0 +1,322 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021 Hans de Goede <hdegoede@redhat.com>
*
* Driver for the LetSketch / VSON WP9620N drawing tablet.
* This drawing tablet is also sold under other brand names such as Case U,
* presumably this driver will work for all of them. But it has only been
* tested with a LetSketch WP9620N model.
*
* These tablets also work without a special HID driver, but then only part
* of the active area works and both the pad and stylus buttons are hardwired
* to special key-combos. E.g. the 2 stylus buttons send right mouse clicks /
* resp. "e" key presses.
*
* This device has 4 USB interfaces:
*
* Interface 0 EP 0x81 bootclass mouse, rdesc len 18, report id 0x08,
* Application(ff00.0001)
* This interface sends raw event input reports in a custom format, but only
* after doing the special dance from letsketch_probe(). After enabling this
* interface the other 3 interfaces are disabled.
*
* Interface 1 EP 0x82 bootclass mouse, rdesc len 83, report id 0x0a, Tablet
* This interface sends absolute events for the pen, including pressure,
* but only for some part of the active area due to special "aspect ratio"
* correction and only half by default since it assumes it will be used
* with a phone in portraid mode, while using the tablet in landscape mode.
* Also stylus + pad button events are not reported here.
*
* Interface 2 EP 0x83 bootclass keybd, rdesc len 64, report id none, Std Kbd
* This interfaces send various hard-coded key-combos for the pad buttons
* and "e" keypresses for the 2nd stylus button
*
* Interface 3 EP 0x84 bootclass mouse, rdesc len 75, report id 0x01, Std Mouse
* This reports right-click mouse-button events for the 1st stylus button
*/
#include <linux/device.h>
#include <linux/input.h>
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/usb.h>
#include <asm/unaligned.h>
#include "hid-ids.h"
#define LETSKETCH_RAW_IF 0
#define LETSKETCH_RAW_DATA_LEN 12
#define LETSKETCH_RAW_REPORT_ID 8
#define LETSKETCH_PAD_BUTTONS 5
#define LETSKETCH_INFO_STR_IDX_BEGIN 0xc8
#define LETSKETCH_INFO_STR_IDX_END 0xca
#define LETSKETCH_GET_STRING_RETRIES 5
struct letsketch_data {
struct hid_device *hdev;
struct input_dev *input_tablet;
struct input_dev *input_tablet_pad;
struct timer_list inrange_timer;
};
static int letsketch_open(struct input_dev *dev)
{
struct letsketch_data *data = input_get_drvdata(dev);
return hid_hw_open(data->hdev);
}
static void letsketch_close(struct input_dev *dev)
{
struct letsketch_data *data = input_get_drvdata(dev);
hid_hw_close(data->hdev);
}
static struct input_dev *letsketch_alloc_input_dev(struct letsketch_data *data)
{
struct input_dev *input;
input = devm_input_allocate_device(&data->hdev->dev);
if (!input)
return NULL;
input->id.bustype = data->hdev->bus;
input->id.vendor = data->hdev->vendor;
input->id.product = data->hdev->product;
input->id.version = data->hdev->bus;
input->phys = data->hdev->phys;
input->uniq = data->hdev->uniq;
input->open = letsketch_open;
input->close = letsketch_close;
input_set_drvdata(input, data);
return input;
}
static int letsketch_setup_input_tablet(struct letsketch_data *data)
{
struct input_dev *input;
input = letsketch_alloc_input_dev(data);
if (!input)
return -ENOMEM;
input_set_abs_params(input, ABS_X, 0, 50800, 0, 0);
input_set_abs_params(input, ABS_Y, 0, 31750, 0, 0);
input_set_abs_params(input, ABS_PRESSURE, 0, 8192, 0, 0);
input_abs_set_res(input, ABS_X, 240);
input_abs_set_res(input, ABS_Y, 225);
input_set_capability(input, EV_KEY, BTN_TOUCH);
input_set_capability(input, EV_KEY, BTN_TOOL_PEN);
input_set_capability(input, EV_KEY, BTN_STYLUS);
input_set_capability(input, EV_KEY, BTN_STYLUS2);
/* All known brands selling this tablet use WP9620[N] as model name */
input->name = "WP9620 Tablet";
data->input_tablet = input;
return input_register_device(data->input_tablet);
}
static int letsketch_setup_input_tablet_pad(struct letsketch_data *data)
{
struct input_dev *input;
int i;
input = letsketch_alloc_input_dev(data);
if (!input)
return -ENOMEM;
for (i = 0; i < LETSKETCH_PAD_BUTTONS; i++)
input_set_capability(input, EV_KEY, BTN_0 + i);
/*
* These are never send on the pad input_dev, but must be set
* on the Pad to make udev / libwacom happy.
*/
input_set_abs_params(input, ABS_X, 0, 1, 0, 0);
input_set_abs_params(input, ABS_Y, 0, 1, 0, 0);
input_set_capability(input, EV_KEY, BTN_STYLUS);
input->name = "WP9620 Pad";
data->input_tablet_pad = input;
return input_register_device(data->input_tablet_pad);
}
static void letsketch_inrange_timeout(struct timer_list *t)
{
struct letsketch_data *data = from_timer(data, t, inrange_timer);
struct input_dev *input = data->input_tablet;
input_report_key(input, BTN_TOOL_PEN, 0);
input_sync(input);
}
static int letsketch_raw_event(struct hid_device *hdev,
struct hid_report *report,
u8 *raw_data, int size)
{
struct letsketch_data *data = hid_get_drvdata(hdev);
struct input_dev *input;
int i;
if (size != LETSKETCH_RAW_DATA_LEN || raw_data[0] != LETSKETCH_RAW_REPORT_ID)
return 0;
switch (raw_data[1] & 0xf0) {
case 0x80: /* Pen data */
input = data->input_tablet;
input_report_key(input, BTN_TOOL_PEN, 1);
input_report_key(input, BTN_TOUCH, raw_data[1] & 0x01);
input_report_key(input, BTN_STYLUS, raw_data[1] & 0x02);
input_report_key(input, BTN_STYLUS2, raw_data[1] & 0x04);
input_report_abs(input, ABS_X,
get_unaligned_le16(raw_data + 2));
input_report_abs(input, ABS_Y,
get_unaligned_le16(raw_data + 4));
input_report_abs(input, ABS_PRESSURE,
get_unaligned_le16(raw_data + 6));
/*
* There is no out of range event, so use a timer for this
* when in range we get an event approx. every 8 ms.
*/
mod_timer(&data->inrange_timer, jiffies + msecs_to_jiffies(100));
break;
case 0xe0: /* Pad data */
input = data->input_tablet_pad;
for (i = 0; i < LETSKETCH_PAD_BUTTONS; i++)
input_report_key(input, BTN_0 + i, raw_data[4] == (i + 1));
break;
default:
hid_warn(data->hdev, "Warning unknown data header: 0x%02x\n",
raw_data[0]);
return 0;
}
input_sync(input);
return 0;
}
/*
* The tablets magic handshake to put it in raw mode relies on getting
* string descriptors. But the firmware is buggy and does not like it if
* we do this too fast. Even if we go slow sometimes the usb_string() call
* fails. Ignore errors and retry it a couple of times if necessary.
*/
static int letsketch_get_string(struct usb_device *udev, int index, char *buf, int size)
{
int i, ret;
for (i = 0; i < LETSKETCH_GET_STRING_RETRIES; i++) {
usleep_range(5000, 7000);
ret = usb_string(udev, index, buf, size);
if (ret > 0)
return 0;
}
dev_err(&udev->dev, "Max retries (%d) exceeded reading string descriptor %d\n",
LETSKETCH_GET_STRING_RETRIES, index);
return ret ? ret : -EIO;
}
static int letsketch_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
struct device *dev = &hdev->dev;
struct letsketch_data *data;
struct usb_interface *intf;
struct usb_device *udev;
char buf[256];
int i, ret;
if (!hid_is_using_ll_driver(hdev, &usb_hid_driver))
return -ENODEV;
intf = to_usb_interface(hdev->dev.parent);
if (intf->altsetting->desc.bInterfaceNumber != LETSKETCH_RAW_IF)
return -ENODEV; /* Ignore the other interfaces */
udev = interface_to_usbdev(intf);
/*
* Instead of using a set-feature request, or even a custom USB ctrl
* message the tablet needs this elaborate magic reading of USB
* string descriptors to kick it into raw mode. This is what the
* Windows drivers are seen doing in an USB trace under Windows.
*/
for (i = LETSKETCH_INFO_STR_IDX_BEGIN; i <= LETSKETCH_INFO_STR_IDX_END; i++) {
ret = letsketch_get_string(udev, i, buf, sizeof(buf));
if (ret)
return ret;
hid_info(hdev, "Device info: %s\n", buf);
}
for (i = 1; i <= 250; i++) {
ret = letsketch_get_string(udev, i, buf, sizeof(buf));
if (ret)
return ret;
}
ret = letsketch_get_string(udev, 0x64, buf, sizeof(buf));
if (ret)
return ret;
ret = letsketch_get_string(udev, LETSKETCH_INFO_STR_IDX_BEGIN, buf, sizeof(buf));
if (ret)
return ret;
/*
* The tablet should be in raw mode now, end with a final delay before
* doing further IO to the device.
*/
usleep_range(5000, 7000);
ret = hid_parse(hdev);
if (ret)
return ret;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->hdev = hdev;
timer_setup(&data->inrange_timer, letsketch_inrange_timeout, 0);
hid_set_drvdata(hdev, data);
ret = letsketch_setup_input_tablet(data);
if (ret)
return ret;
ret = letsketch_setup_input_tablet_pad(data);
if (ret)
return ret;
return hid_hw_start(hdev, HID_CONNECT_HIDRAW);
}
static const struct hid_device_id letsketch_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LETSKETCH, USB_DEVICE_ID_WP9620N) },
{ }
};
MODULE_DEVICE_TABLE(hid, letsketch_devices);
static struct hid_driver letsketch_driver = {
.name = "letsketch",
.id_table = letsketch_devices,
.probe = letsketch_probe,
.raw_event = letsketch_raw_event,
};
module_hid_driver(letsketch_driver);
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_LICENSE("GPL");

View File

@ -51,12 +51,16 @@ static bool report_undeciphered;
module_param(report_undeciphered, bool, 0644);
MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event");
#define TRACKPAD2_2021_BT_VERSION 0x110
#define TRACKPAD_REPORT_ID 0x28
#define TRACKPAD2_USB_REPORT_ID 0x02
#define TRACKPAD2_BT_REPORT_ID 0x31
#define MOUSE_REPORT_ID 0x29
#define MOUSE2_REPORT_ID 0x12
#define DOUBLE_REPORT_ID 0xf7
#define USB_BATTERY_TIMEOUT_MS 60000
/* These definitions are not precise, but they're close enough. (Bits
* 0x03 seem to indicate the aspect ratio of the touch, bits 0x70 seem
* to be some kind of bit mask -- 0x20 may be a near-field reading,
@ -140,6 +144,7 @@ struct magicmouse_sc {
struct hid_device *hdev;
struct delayed_work work;
struct timer_list battery_timer;
};
static int magicmouse_firm_touch(struct magicmouse_sc *msc)
@ -538,10 +543,22 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
__set_bit(REL_HWHEEL_HI_RES, input->relbit);
}
} else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
/* setting the device name to ensure the same driver settings
* get loaded, whether connected through bluetooth or USB
/* If the trackpad has been connected to a Mac, the name is
* automatically personalized, e.g., "José Expósito's Trackpad".
* When connected through Bluetooth, the personalized name is
* reported, however, when connected through USB the generic
* name is reported.
* Set the device name to ensure the same driver settings get
* loaded, whether connected through bluetooth or USB.
*/
input->name = "Apple Inc. Magic Trackpad 2";
if (hdev->vendor == BT_VENDOR_ID_APPLE) {
if (input->id.version == TRACKPAD2_2021_BT_VERSION)
input->name = "Apple Inc. Magic Trackpad";
else
input->name = "Apple Inc. Magic Trackpad 2";
} else { /* USB_VENDOR_ID_APPLE */
input->name = hdev->name;
}
__clear_bit(EV_MSC, input->evbit);
__clear_bit(BTN_0, input->keybit);
@ -738,6 +755,44 @@ static void magicmouse_enable_mt_work(struct work_struct *work)
hid_err(msc->hdev, "unable to request touch data (%d)\n", ret);
}
static int magicmouse_fetch_battery(struct hid_device *hdev)
{
#ifdef CONFIG_HID_BATTERY_STRENGTH
struct hid_report_enum *report_enum;
struct hid_report *report;
if (!hdev->battery || hdev->vendor != USB_VENDOR_ID_APPLE ||
(hdev->product != USB_DEVICE_ID_APPLE_MAGICMOUSE2 &&
hdev->product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2))
return -1;
report_enum = &hdev->report_enum[hdev->battery_report_type];
report = report_enum->report_id_hash[hdev->battery_report_id];
if (!report || report->maxfield < 1)
return -1;
if (hdev->battery_capacity == hdev->battery_max)
return -1;
hid_hw_request(hdev, report, HID_REQ_GET_REPORT);
return 0;
#else
return -1;
#endif
}
static void magicmouse_battery_timer_tick(struct timer_list *t)
{
struct magicmouse_sc *msc = from_timer(msc, t, battery_timer);
struct hid_device *hdev = msc->hdev;
if (magicmouse_fetch_battery(hdev) == 0) {
mod_timer(&msc->battery_timer,
jiffies + msecs_to_jiffies(USB_BATTERY_TIMEOUT_MS));
}
}
static int magicmouse_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
@ -745,11 +800,6 @@ static int magicmouse_probe(struct hid_device *hdev,
struct hid_report *report;
int ret;
if (id->vendor == USB_VENDOR_ID_APPLE &&
id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 &&
hdev->type != HID_TYPE_USBMOUSE)
return -ENODEV;
msc = devm_kzalloc(&hdev->dev, sizeof(*msc), GFP_KERNEL);
if (msc == NULL) {
hid_err(hdev, "can't alloc magicmouse descriptor\n");
@ -775,6 +825,16 @@ static int magicmouse_probe(struct hid_device *hdev,
return ret;
}
timer_setup(&msc->battery_timer, magicmouse_battery_timer_tick, 0);
mod_timer(&msc->battery_timer,
jiffies + msecs_to_jiffies(USB_BATTERY_TIMEOUT_MS));
magicmouse_fetch_battery(hdev);
if (id->vendor == USB_VENDOR_ID_APPLE &&
(id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2 ||
(id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 && hdev->type != HID_TYPE_USBMOUSE)))
return 0;
if (!msc->input) {
hid_err(hdev, "magicmouse input not registered\n");
ret = -ENOMEM;
@ -827,6 +887,7 @@ static int magicmouse_probe(struct hid_device *hdev,
return 0;
err_stop_hw:
del_timer_sync(&msc->battery_timer);
hid_hw_stop(hdev);
return ret;
}
@ -835,17 +896,52 @@ static void magicmouse_remove(struct hid_device *hdev)
{
struct magicmouse_sc *msc = hid_get_drvdata(hdev);
if (msc)
if (msc) {
cancel_delayed_work_sync(&msc->work);
del_timer_sync(&msc->battery_timer);
}
hid_hw_stop(hdev);
}
static __u8 *magicmouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
/*
* Change the usage from:
* 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 0
* 0x09, 0x0b, // Usage (Vendor Usage 0x0b) 3
* To:
* 0x05, 0x01, // Usage Page (Generic Desktop) 0
* 0x09, 0x02, // Usage (Mouse) 2
*/
if (hdev->vendor == USB_VENDOR_ID_APPLE &&
(hdev->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2 ||
hdev->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) &&
*rsize == 83 && rdesc[46] == 0x84 && rdesc[58] == 0x85) {
hid_info(hdev,
"fixing up magicmouse battery report descriptor\n");
*rsize = *rsize - 1;
rdesc = kmemdup(rdesc + 1, *rsize, GFP_KERNEL);
if (!rdesc)
return NULL;
rdesc[0] = 0x05;
rdesc[1] = 0x01;
rdesc[2] = 0x09;
rdesc[3] = 0x02;
}
return rdesc;
}
static const struct hid_device_id magic_mice[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
USB_DEVICE_ID_APPLE_MAGICMOUSE), .driver_data = 0 },
{ HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE,
USB_DEVICE_ID_APPLE_MAGICMOUSE2), .driver_data = 0 },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE,
USB_DEVICE_ID_APPLE_MAGICMOUSE2), .driver_data = 0 },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
USB_DEVICE_ID_APPLE_MAGICTRACKPAD), .driver_data = 0 },
{ HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE,
@ -861,6 +957,7 @@ static struct hid_driver magicmouse_driver = {
.id_table = magic_mice,
.probe = magicmouse_probe,
.remove = magicmouse_remove,
.report_fixup = magicmouse_report_fixup,
.raw_event = magicmouse_raw_event,
.event = magicmouse_event,
.input_mapping = magicmouse_input_mapping,

View File

@ -1606,9 +1606,6 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
case HID_DG_STYLUS:
/* force BTN_STYLUS to allow tablet matching in udev */
__set_bit(BTN_STYLUS, hi->input->keybit);
fallthrough;
case HID_DG_PEN:
suffix = "Stylus";
break;
default:
suffix = "UNKNOWN";

View File

@ -304,6 +304,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021) },
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021) },
#endif
#if IS_ENABLED(CONFIG_HID_APPLEIR)
{ HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },

View File

@ -78,7 +78,6 @@ static int tmff_play(struct input_dev *dev, void *data,
struct hid_field *ff_field = tmff->ff_field;
int x, y;
int left, right; /* Rumbling */
int motor_swap;
switch (effect->type) {
case FF_CONSTANT:
@ -104,11 +103,8 @@ static int tmff_play(struct input_dev *dev, void *data,
ff_field->logical_maximum);
/* 2-in-1 strong motor is left */
if (hid->product == THRUSTMASTER_DEVICE_ID_2_IN_1_DT) {
motor_swap = left;
left = right;
right = motor_swap;
}
if (hid->product == THRUSTMASTER_DEVICE_ID_2_IN_1_DT)
swap(left, right);
dbg_hid("(left,right)=(%08x, %08x)\n", left, right);
ff_field->value[0] = left;

View File

@ -66,7 +66,7 @@ static int uclogic_params_get_str_desc(__u8 **pbuf, struct hid_device *hdev,
__u8 idx, size_t len)
{
int rc;
struct usb_device *udev = hid_to_usb_dev(hdev);
struct usb_device *udev;
__u8 *buf = NULL;
/* Check arguments */
@ -75,6 +75,8 @@ static int uclogic_params_get_str_desc(__u8 **pbuf, struct hid_device *hdev,
goto cleanup;
}
udev = hid_to_usb_dev(hdev);
buf = kmalloc(len, GFP_KERNEL);
if (buf == NULL) {
rc = -ENOMEM;
@ -450,7 +452,7 @@ static int uclogic_params_frame_init_v1_buttonpad(
{
int rc;
bool found = false;
struct usb_device *usb_dev = hid_to_usb_dev(hdev);
struct usb_device *usb_dev;
char *str_buf = NULL;
const size_t str_len = 16;
@ -460,6 +462,8 @@ static int uclogic_params_frame_init_v1_buttonpad(
goto cleanup;
}
usb_dev = hid_to_usb_dev(hdev);
/*
* Enable generic button mode
*/
@ -707,9 +711,9 @@ static int uclogic_params_huion_init(struct uclogic_params *params,
struct hid_device *hdev)
{
int rc;
struct usb_device *udev = hid_to_usb_dev(hdev);
struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
__u8 bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
struct usb_device *udev;
struct usb_interface *iface;
__u8 bInterfaceNumber;
bool found;
/* The resulting parameters (noop) */
struct uclogic_params p = {0, };
@ -723,6 +727,10 @@ static int uclogic_params_huion_init(struct uclogic_params *params,
goto cleanup;
}
udev = hid_to_usb_dev(hdev);
iface = to_usb_interface(hdev->dev.parent);
bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
/* If it's not a pen interface */
if (bInterfaceNumber != 0) {
/* TODO: Consider marking the interface invalid */
@ -834,10 +842,10 @@ int uclogic_params_init(struct uclogic_params *params,
struct hid_device *hdev)
{
int rc;
struct usb_device *udev = hid_to_usb_dev(hdev);
__u8 bNumInterfaces = udev->config->desc.bNumInterfaces;
struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
__u8 bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
struct usb_device *udev;
__u8 bNumInterfaces;
struct usb_interface *iface;
__u8 bInterfaceNumber;
bool found;
/* The resulting parameters (noop) */
struct uclogic_params p = {0, };
@ -848,6 +856,11 @@ int uclogic_params_init(struct uclogic_params *params,
goto cleanup;
}
udev = hid_to_usb_dev(hdev);
bNumInterfaces = udev->config->desc.bNumInterfaces;
iface = to_usb_interface(hdev->dev.parent);
bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
/*
* Set replacement report descriptor if the original matches the
* specified size. Otherwise keep interface unchanged.

View File

@ -34,7 +34,7 @@ static int hidraw_major;
static struct cdev hidraw_cdev;
static struct class *hidraw_class;
static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES];
static DEFINE_MUTEX(minors_lock);
static DECLARE_RWSEM(minors_rwsem);
static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
@ -107,7 +107,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
__u8 *buf;
int ret = 0;
lockdep_assert_held(&minors_lock);
lockdep_assert_held(&minors_rwsem);
if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
ret = -ENODEV;
@ -160,9 +160,9 @@ out:
static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
ssize_t ret;
mutex_lock(&minors_lock);
down_read(&minors_rwsem);
ret = hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT);
mutex_unlock(&minors_lock);
up_read(&minors_rwsem);
return ret;
}
@ -182,7 +182,7 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t
int ret = 0, len;
unsigned char report_number;
lockdep_assert_held(&minors_lock);
lockdep_assert_held(&minors_rwsem);
if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
ret = -ENODEV;
@ -272,7 +272,7 @@ static int hidraw_open(struct inode *inode, struct file *file)
goto out;
}
mutex_lock(&minors_lock);
down_read(&minors_rwsem);
if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
err = -ENODEV;
goto out_unlock;
@ -301,7 +301,7 @@ static int hidraw_open(struct inode *inode, struct file *file)
spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags);
file->private_data = list;
out_unlock:
mutex_unlock(&minors_lock);
up_read(&minors_rwsem);
out:
if (err < 0)
kfree(list);
@ -347,7 +347,7 @@ static int hidraw_release(struct inode * inode, struct file * file)
struct hidraw_list *list = file->private_data;
unsigned long flags;
mutex_lock(&minors_lock);
down_write(&minors_rwsem);
spin_lock_irqsave(&hidraw_table[minor]->list_lock, flags);
list_del(&list->node);
@ -356,7 +356,7 @@ static int hidraw_release(struct inode * inode, struct file * file)
drop_ref(hidraw_table[minor], 0);
mutex_unlock(&minors_lock);
up_write(&minors_rwsem);
return 0;
}
@ -369,7 +369,7 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
struct hidraw *dev;
void __user *user_arg = (void __user*) arg;
mutex_lock(&minors_lock);
down_read(&minors_rwsem);
dev = hidraw_table[minor];
if (!dev || !dev->exist) {
ret = -ENODEV;
@ -487,7 +487,7 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
ret = -ENOTTY;
}
out:
mutex_unlock(&minors_lock);
up_read(&minors_rwsem);
return ret;
}
@ -546,7 +546,7 @@ int hidraw_connect(struct hid_device *hid)
result = -EINVAL;
mutex_lock(&minors_lock);
down_write(&minors_rwsem);
for (minor = 0; minor < HIDRAW_MAX_DEVICES; minor++) {
if (hidraw_table[minor])
@ -557,7 +557,7 @@ int hidraw_connect(struct hid_device *hid)
}
if (result) {
mutex_unlock(&minors_lock);
up_write(&minors_rwsem);
kfree(dev);
goto out;
}
@ -567,7 +567,7 @@ int hidraw_connect(struct hid_device *hid)
if (IS_ERR(dev->dev)) {
hidraw_table[minor] = NULL;
mutex_unlock(&minors_lock);
up_write(&minors_rwsem);
result = PTR_ERR(dev->dev);
kfree(dev);
goto out;
@ -583,7 +583,7 @@ int hidraw_connect(struct hid_device *hid)
dev->exist = 1;
hid->hidraw = dev;
mutex_unlock(&minors_lock);
up_write(&minors_rwsem);
out:
return result;
@ -594,11 +594,11 @@ void hidraw_disconnect(struct hid_device *hid)
{
struct hidraw *hidraw = hid->hidraw;
mutex_lock(&minors_lock);
down_write(&minors_rwsem);
drop_ref(hidraw, 1);
mutex_unlock(&minors_lock);
up_write(&minors_rwsem);
}
EXPORT_SYMBOL_GPL(hidraw_disconnect);

View File

@ -111,7 +111,7 @@ static int i2c_hid_acpi_probe(struct i2c_client *client)
}
return i2c_hid_core_probe(client, &ihid_acpi->ops,
hid_descriptor_address);
hid_descriptor_address, 0);
}
static const struct acpi_device_id i2c_hid_acpi_match[] = {

View File

@ -522,9 +522,12 @@ static void i2c_hid_get_input(struct i2c_hid *ihid)
i2c_hid_dbg(ihid, "input: %*ph\n", ret_size, ihid->inbuf);
if (test_bit(I2C_HID_STARTED, &ihid->flags))
if (test_bit(I2C_HID_STARTED, &ihid->flags)) {
pm_wakeup_event(&ihid->client->dev, 0);
hid_input_report(ihid->hid, HID_INPUT_REPORT, ihid->inbuf + 2,
ret_size - 2, 1);
}
return;
}
@ -912,7 +915,7 @@ static void i2c_hid_core_shutdown_tail(struct i2c_hid *ihid)
}
int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops,
u16 hid_descriptor_address)
u16 hid_descriptor_address, u32 quirks)
{
int ret;
struct i2c_hid *ihid;
@ -1009,6 +1012,8 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops,
goto err_mem_free;
}
hid->quirks |= quirks;
return 0;
err_mem_free:
@ -1063,11 +1068,9 @@ static int i2c_hid_core_suspend(struct device *dev)
int ret;
int wake_status;
if (hid->driver && hid->driver->suspend) {
ret = hid->driver->suspend(hid, PMSG_SUSPEND);
if (ret < 0)
return ret;
}
ret = hid_driver_suspend(hid, PMSG_SUSPEND);
if (ret < 0)
return ret;
/* Save some power */
i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
@ -1125,12 +1128,7 @@ static int i2c_hid_core_resume(struct device *dev)
if (ret)
return ret;
if (hid->driver && hid->driver->reset_resume) {
ret = hid->driver->reset_resume(hid);
return ret;
}
return 0;
return hid_driver_reset_resume(hid);
}
#endif

View File

@ -150,7 +150,7 @@ static int i2c_hid_of_goodix_probe(struct i2c_client *client,
goodix_i2c_hid_deassert_reset(ihid_goodix, true);
mutex_unlock(&ihid_goodix->regulator_mutex);
return i2c_hid_core_probe(client, &ihid_goodix->ops, 0x0001);
return i2c_hid_core_probe(client, &ihid_goodix->ops, 0x0001, 0);
}
static const struct goodix_i2c_hid_timing_data goodix_gt7375p_timing_data = {

View File

@ -21,6 +21,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
@ -71,6 +72,7 @@ static int i2c_hid_of_probe(struct i2c_client *client,
struct device *dev = &client->dev;
struct i2c_hid_of *ihid_of;
u16 hid_descriptor_address;
u32 quirks = 0;
int ret;
u32 val;
@ -105,8 +107,14 @@ static int i2c_hid_of_probe(struct i2c_client *client,
if (ret)
return ret;
if (device_property_read_bool(dev, "touchscreen-inverted-x"))
quirks |= HID_QUIRK_X_INVERT;
if (device_property_read_bool(dev, "touchscreen-inverted-y"))
quirks |= HID_QUIRK_Y_INVERT;
return i2c_hid_core_probe(client, &ihid_of->ops,
hid_descriptor_address);
hid_descriptor_address, quirks);
}
static const struct of_device_id i2c_hid_of_match[] = {

View File

@ -32,7 +32,7 @@ struct i2chid_ops {
};
int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops,
u16 hid_descriptor_address);
u16 hid_descriptor_address, u32 quirks);
int i2c_hid_core_remove(struct i2c_client *client);
void i2c_hid_core_shutdown(struct i2c_client *client);

View File

@ -909,7 +909,11 @@ static uint32_t ish_ipc_get_header(struct ishtp_device *dev, int length,
*/
static bool _dma_no_cache_snooping(struct ishtp_device *dev)
{
return dev->pdev->device == EHL_Ax_DEVICE_ID;
return (dev->pdev->device == EHL_Ax_DEVICE_ID ||
dev->pdev->device == TGL_LP_DEVICE_ID ||
dev->pdev->device == TGL_H_DEVICE_ID ||
dev->pdev->device == ADL_S_DEVICE_ID ||
dev->pdev->device == ADL_P_DEVICE_ID);
}
static const struct ishtp_hw_ops ish_hw_ops = {

View File

@ -268,7 +268,8 @@ static int get_firmware_variant(struct ishtp_cl_data *client_data,
}
/**
* loader_cl_send() Send message from host to firmware
* loader_cl_send() - Send message from host to firmware
*
* @client_data: Client data instance
* @out_msg: Message buffer to be sent to firmware
* @out_size: Size of out going message

View File

@ -204,50 +204,35 @@ static int surface_hid_suspend(struct device *dev)
{
struct surface_hid_device *d = dev_get_drvdata(dev);
if (d->hid->driver && d->hid->driver->suspend)
return d->hid->driver->suspend(d->hid, PMSG_SUSPEND);
return 0;
return hid_driver_suspend(d->hid, PMSG_SUSPEND);
}
static int surface_hid_resume(struct device *dev)
{
struct surface_hid_device *d = dev_get_drvdata(dev);
if (d->hid->driver && d->hid->driver->resume)
return d->hid->driver->resume(d->hid);
return 0;
return hid_driver_resume(d->hid);
}
static int surface_hid_freeze(struct device *dev)
{
struct surface_hid_device *d = dev_get_drvdata(dev);
if (d->hid->driver && d->hid->driver->suspend)
return d->hid->driver->suspend(d->hid, PMSG_FREEZE);
return 0;
return hid_driver_suspend(d->hid, PMSG_FREEZE);
}
static int surface_hid_poweroff(struct device *dev)
{
struct surface_hid_device *d = dev_get_drvdata(dev);
if (d->hid->driver && d->hid->driver->suspend)
return d->hid->driver->suspend(d->hid, PMSG_HIBERNATE);
return 0;
return hid_driver_suspend(d->hid, PMSG_HIBERNATE);
}
static int surface_hid_restore(struct device *dev)
{
struct surface_hid_device *d = dev_get_drvdata(dev);
if (d->hid->driver && d->hid->driver->reset_resume)
return d->hid->driver->reset_resume(d->hid);
return 0;
return hid_driver_reset_resume(d->hid);
}
const struct dev_pm_ops surface_hid_pm_ops = {

View File

@ -1563,8 +1563,8 @@ static int hid_resume_common(struct hid_device *hid, bool driver_suspended)
int status = 0;
hid_restart_io(hid);
if (driver_suspended && hid->driver && hid->driver->resume)
status = hid->driver->resume(hid);
if (driver_suspended)
status = hid_driver_resume(hid);
return status;
}
@ -1588,11 +1588,9 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
{
set_bit(HID_SUSPENDED, &usbhid->iofl);
spin_unlock_irq(&usbhid->lock);
if (hid->driver && hid->driver->suspend) {
status = hid->driver->suspend(hid, message);
if (status < 0)
goto failed;
}
status = hid_driver_suspend(hid, message);
if (status < 0)
goto failed;
driver_suspended = true;
} else {
usbhid_mark_busy(usbhid);
@ -1602,8 +1600,7 @@ static int hid_suspend(struct usb_interface *intf, pm_message_t message)
} else {
/* TODO: resume() might need to handle suspend failure */
if (hid->driver && hid->driver->suspend)
status = hid->driver->suspend(hid, message);
status = hid_driver_suspend(hid, message);
driver_suspended = true;
spin_lock_irq(&usbhid->lock);
set_bit(HID_SUSPENDED, &usbhid->iofl);
@ -1644,8 +1641,8 @@ static int hid_reset_resume(struct usb_interface *intf)
int status;
status = hid_post_reset(intf);
if (status >= 0 && hid->driver && hid->driver->reset_resume) {
int ret = hid->driver->reset_resume(hid);
if (status >= 0) {
int ret = hid_driver_reset_resume(hid);
if (ret < 0)
status = ret;
}

View File

@ -241,6 +241,7 @@ struct hid_item {
#define HID_DG_TOUCH 0x000d0033
#define HID_DG_UNTOUCH 0x000d0034
#define HID_DG_TAP 0x000d0035
#define HID_DG_TRANSDUCER_INDEX 0x000d0038
#define HID_DG_TABLETFUNCTIONKEY 0x000d0039
#define HID_DG_PROGRAMCHANGEKEY 0x000d003a
#define HID_DG_BATTERYSTRENGTH 0x000d003b
@ -253,6 +254,15 @@ struct hid_item {
#define HID_DG_BARRELSWITCH 0x000d0044
#define HID_DG_ERASER 0x000d0045
#define HID_DG_TABLETPICK 0x000d0046
#define HID_DG_PEN_COLOR 0x000d005c
#define HID_DG_PEN_LINE_WIDTH 0x000d005e
#define HID_DG_PEN_LINE_STYLE 0x000d0070
#define HID_DG_PEN_LINE_STYLE_INK 0x000d0072
#define HID_DG_PEN_LINE_STYLE_PENCIL 0x000d0073
#define HID_DG_PEN_LINE_STYLE_HIGHLIGHTER 0x000d0074
#define HID_DG_PEN_LINE_STYLE_CHISEL_MARKER 0x000d0075
#define HID_DG_PEN_LINE_STYLE_BRUSH 0x000d0076
#define HID_DG_PEN_LINE_STYLE_NO_PREFERENCE 0x000d0077
#define HID_CP_CONSUMERCONTROL 0x000c0001
#define HID_CP_NUMERICKEYPAD 0x000c0002
@ -349,6 +359,8 @@ struct hid_item {
/* BIT(9) reserved for backward compatibility, was NO_INIT_INPUT_REPORTS */
#define HID_QUIRK_ALWAYS_POLL BIT(10)
#define HID_QUIRK_INPUT_PER_APP BIT(11)
#define HID_QUIRK_X_INVERT BIT(12)
#define HID_QUIRK_Y_INVERT BIT(13)
#define HID_QUIRK_SKIP_OUTPUT_REPORTS BIT(16)
#define HID_QUIRK_SKIP_OUTPUT_REPORT_ID BIT(17)
#define HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP BIT(18)
@ -788,7 +800,7 @@ struct hid_driver {
container_of(pdrv, struct hid_driver, driver)
/**
* hid_ll_driver - low level driver callbacks
* struct hid_ll_driver - low level driver callbacks
* @start: called on probe to start the device
* @stop: called on remove
* @open: called by input layer on open
@ -894,7 +906,6 @@ extern void hidinput_disconnect(struct hid_device *);
int hid_set_field(struct hid_field *, unsigned, __s32);
int hid_input_report(struct hid_device *, int type, u8 *, u32, int);
int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field);
struct hid_field *hidinput_get_led_field(struct hid_device *hid);
unsigned int hidinput_count_leds(struct hid_device *hid);
__s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code);
@ -928,6 +939,16 @@ s32 hid_snto32(__u32 value, unsigned n);
__u32 hid_field_extract(const struct hid_device *hid, __u8 *report,
unsigned offset, unsigned n);
#ifdef CONFIG_PM
int hid_driver_suspend(struct hid_device *hdev, pm_message_t state);
int hid_driver_reset_resume(struct hid_device *hdev);
int hid_driver_resume(struct hid_device *hdev);
#else
static inline int hid_driver_suspend(struct hid_device *hdev, pm_message_t state) { return 0; }
static inline int hid_driver_reset_resume(struct hid_device *hdev) { return 0; }
static inline int hid_driver_resume(struct hid_device *hdev) { return 0; }
#endif
/**
* hid_device_io_start - enable HID input during probe, remove
*
@ -1005,6 +1026,10 @@ static inline void hid_map_usage(struct hid_input *hidinput,
bmap = input->ledbit;
limit = LED_MAX;
break;
case EV_MSC:
bmap = input->mscbit;
limit = MSC_MAX;
break;
}
if (unlikely(c > limit || !bmap)) {
@ -1061,6 +1086,12 @@ int __must_check hid_hw_start(struct hid_device *hdev,
void hid_hw_stop(struct hid_device *hdev);
int __must_check hid_hw_open(struct hid_device *hdev);
void hid_hw_close(struct hid_device *hdev);
void hid_hw_request(struct hid_device *hdev,
struct hid_report *report, int reqtype);
int hid_hw_raw_request(struct hid_device *hdev,
unsigned char reportnum, __u8 *buf,
size_t len, unsigned char rtype, int reqtype);
int hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len);
/**
* hid_hw_power - requests underlying HW to go into given power mode
@ -1078,68 +1109,6 @@ static inline int hid_hw_power(struct hid_device *hdev, int level)
}
/**
* hid_hw_request - send report request to device
*
* @hdev: hid device
* @report: report to send
* @reqtype: hid request type
*/
static inline void hid_hw_request(struct hid_device *hdev,
struct hid_report *report, int reqtype)
{
if (hdev->ll_driver->request)
return hdev->ll_driver->request(hdev, report, reqtype);
__hid_request(hdev, report, reqtype);
}
/**
* hid_hw_raw_request - send report request to device
*
* @hdev: hid device
* @reportnum: report ID
* @buf: in/out data to transfer
* @len: length of buf
* @rtype: HID report type
* @reqtype: HID_REQ_GET_REPORT or HID_REQ_SET_REPORT
*
* Return: count of data transferred, negative if error
*
* Same behavior as hid_hw_request, but with raw buffers instead.
*/
static inline int hid_hw_raw_request(struct hid_device *hdev,
unsigned char reportnum, __u8 *buf,
size_t len, unsigned char rtype, int reqtype)
{
if (len < 1 || len > HID_MAX_BUFFER_SIZE || !buf)
return -EINVAL;
return hdev->ll_driver->raw_request(hdev, reportnum, buf, len,
rtype, reqtype);
}
/**
* hid_hw_output_report - send output report to device
*
* @hdev: hid device
* @buf: raw data to transfer
* @len: length of buf
*
* Return: count of data transferred, negative if error
*/
static inline int hid_hw_output_report(struct hid_device *hdev, __u8 *buf,
size_t len)
{
if (len < 1 || len > HID_MAX_BUFFER_SIZE || !buf)
return -EINVAL;
if (hdev->ll_driver->output_report)
return hdev->ll_driver->output_report(hdev, buf, len);
return -ENOSYS;
}
/**
* hid_hw_idle - send idle request to device
*
@ -1158,7 +1127,7 @@ static inline int hid_hw_idle(struct hid_device *hdev, int report, int idle,
}
/**
* hid_may_wakeup - return if the hid device may act as a wakeup source during system-suspend
* hid_hw_may_wakeup - return if the hid device may act as a wakeup source during system-suspend
*
* @hdev: hid device
*/