mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 12:42:02 +00:00
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:
commit
26b88fba2a
@ -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:
|
||||
|
||||
|
13
MAINTAINERS
13
MAINTAINERS
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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"},
|
||||
|
@ -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
|
||||
|
@ -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
322
drivers/hid/hid-letsketch.c
Normal 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");
|
@ -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,
|
||||
|
@ -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";
|
||||
|
@ -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) },
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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[] = {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 = {
|
||||
|
@ -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[] = {
|
||||
|
@ -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);
|
||||
|
@ -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 = {
|
||||
|
@ -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
|
||||
|
@ -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 = {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user