diff options
author | Linus Torvalds | 2022-01-11 10:53:57 -0800 |
---|---|---|
committer | Linus Torvalds | 2022-01-11 10:53:57 -0800 |
commit | 26b88fba2ad9b573b8433926294fe48fbf815deb (patch) | |
tree | 7c176cfafc3faa1658f7aacd6e5f9f44fdfd8144 /drivers/hid | |
parent | 4a110907a118346cfafc3aa3a75a632fac11b7a9 (diff) | |
parent | cd598d21294e088c3c9e518a7e9098f94ae8cf6a (diff) |
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
...
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/Kconfig | 14 | ||||
-rw-r--r-- | drivers/hid/Makefile | 1 | ||||
-rw-r--r-- | drivers/hid/hid-apple.c | 146 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 93 | ||||
-rw-r--r-- | drivers/hid/hid-debug.c | 35 | ||||
-rw-r--r-- | drivers/hid/hid-ids.h | 5 | ||||
-rw-r--r-- | drivers/hid/hid-input.c | 26 | ||||
-rw-r--r-- | drivers/hid/hid-letsketch.c | 322 | ||||
-rw-r--r-- | drivers/hid/hid-magicmouse.c | 115 | ||||
-rw-r--r-- | drivers/hid/hid-multitouch.c | 3 | ||||
-rw-r--r-- | drivers/hid/hid-quirks.c | 1 | ||||
-rw-r--r-- | drivers/hid/hid-tmff.c | 8 | ||||
-rw-r--r-- | drivers/hid/hid-uclogic-params.c | 31 | ||||
-rw-r--r-- | drivers/hid/hidraw.c | 34 | ||||
-rw-r--r-- | drivers/hid/i2c-hid/i2c-hid-acpi.c | 2 | ||||
-rw-r--r-- | drivers/hid/i2c-hid/i2c-hid-core.c | 24 | ||||
-rw-r--r-- | drivers/hid/i2c-hid/i2c-hid-of-goodix.c | 2 | ||||
-rw-r--r-- | drivers/hid/i2c-hid/i2c-hid-of.c | 10 | ||||
-rw-r--r-- | drivers/hid/i2c-hid/i2c-hid.h | 2 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ipc/ipc.c | 6 | ||||
-rw-r--r-- | drivers/hid/intel-ish-hid/ishtp-fw-loader.c | 3 | ||||
-rw-r--r-- | drivers/hid/surface-hid/surface_hid_core.c | 25 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-core.c | 19 |
23 files changed, 814 insertions, 113 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index a7c78ac96270..f5544157576c 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -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 diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 55a6fa3eca5a..6d3e630e81af 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -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 diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 2c9c5faa74a9..24802a4a636e 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -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, diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index dbed2524fd47..f1aed5bbd000 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -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; diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 7a92e2a04a09..26c31d759914 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -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"}, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 19da07777d62..26cee452ec44 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -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 diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 03f994541981..1ce75e8b49d5 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -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; diff --git a/drivers/hid/hid-letsketch.c b/drivers/hid/hid-letsketch.c new file mode 100644 index 000000000000..74d17cf518ba --- /dev/null +++ b/drivers/hid/hid-letsketch.c @@ -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"); diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index d7687ce70614..664a624a363d 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -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, diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 082376a6cb3d..99eabfb4145b 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -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"; diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index ee7e504e7279..9af1dc8ae3a2 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -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) }, diff --git a/drivers/hid/hid-tmff.c b/drivers/hid/hid-tmff.c index 90acef304536..4040cd98dafe 100644 --- a/drivers/hid/hid-tmff.c +++ b/drivers/hid/hid-tmff.c @@ -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; diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c index adff1bd68d9f..3e70f969fb84 100644 --- a/drivers/hid/hid-uclogic-params.c +++ b/drivers/hid/hid-uclogic-params.c @@ -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. diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 79faac87a06f..681614a8302a 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -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); diff --git a/drivers/hid/i2c-hid/i2c-hid-acpi.c b/drivers/hid/i2c-hid/i2c-hid-acpi.c index a6f0257a26de..b96ae15e0ad9 100644 --- a/drivers/hid/i2c-hid/i2c-hid-acpi.c +++ b/drivers/hid/i2c-hid/i2c-hid-acpi.c @@ -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[] = { diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c index 517141138b00..6726567d7297 100644 --- a/drivers/hid/i2c-hid/i2c-hid-core.c +++ b/drivers/hid/i2c-hid/i2c-hid-core.c @@ -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 diff --git a/drivers/hid/i2c-hid/i2c-hid-of-goodix.c b/drivers/hid/i2c-hid/i2c-hid-of-goodix.c index 52674149a275..b4dad66fa954 100644 --- a/drivers/hid/i2c-hid/i2c-hid-of-goodix.c +++ b/drivers/hid/i2c-hid/i2c-hid-of-goodix.c @@ -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 = { diff --git a/drivers/hid/i2c-hid/i2c-hid-of.c b/drivers/hid/i2c-hid/i2c-hid-of.c index 4bf7cea92637..97a27a803f58 100644 --- a/drivers/hid/i2c-hid/i2c-hid-of.c +++ b/drivers/hid/i2c-hid/i2c-hid-of.c @@ -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[] = { diff --git a/drivers/hid/i2c-hid/i2c-hid.h b/drivers/hid/i2c-hid/i2c-hid.h index 05a7827d211a..236cc062d5ef 100644 --- a/drivers/hid/i2c-hid/i2c-hid.h +++ b/drivers/hid/i2c-hid/i2c-hid.h @@ -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); diff --git a/drivers/hid/intel-ish-hid/ipc/ipc.c b/drivers/hid/intel-ish-hid/ipc/ipc.c index 45e0c7b1c9ec..8ccb246b0114 100644 --- a/drivers/hid/intel-ish-hid/ipc/ipc.c +++ b/drivers/hid/intel-ish-hid/ipc/ipc.c @@ -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 = { diff --git a/drivers/hid/intel-ish-hid/ishtp-fw-loader.c b/drivers/hid/intel-ish-hid/ishtp-fw-loader.c index 0e1183e96147..e24988586710 100644 --- a/drivers/hid/intel-ish-hid/ishtp-fw-loader.c +++ b/drivers/hid/intel-ish-hid/ishtp-fw-loader.c @@ -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 diff --git a/drivers/hid/surface-hid/surface_hid_core.c b/drivers/hid/surface-hid/surface_hid_core.c index 5571e74abe91..e46330b2e561 100644 --- a/drivers/hid/surface-hid/surface_hid_core.c +++ b/drivers/hid/surface-hid/surface_hid_core.c @@ -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 = { diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 2dcaf31eb9cd..54752c85604b 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -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; } |