Commit 0e382fa7 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull HID updates from Jiri Kosina:

 - more bullet-proof handling of devres-managed resources in HID core
   (Dmitry Torokhov)

 - conversion of hid-wacom to use ktime_t (Jason Gerecke)

 - touch selftests for hid-wacom (Joshua Dickens)

 - support for nVidia Thunderstrike (SHIELD 2017) controller (Rahul
   Rameshbabu)

 - power management reset-during-suspend fix for goodix Chromebook
   devices (Fei Shao)

 - assorted device ID additions, device-specific quirks and code
   cleanups

* tag 'for-linus-2023062701' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (27 commits)
  HID: wacom: Use ktime_t rather than int when dealing with timestamps
  HID: hidraw: fix data race on device refcount
  HID: intel-ish-hid: ipc: Add Arrow Lake PCI device ID
  HID: logitech-hidpp: add HIDPP_QUIRK_DELAYED_INIT for the T651.
  HID: add quirk for 03f0:464a HP Elite Presenter Mouse
  HID: nvidia-shield: Support LED functionality for Thunderstrike
  HID: nvidia-shield: Add mappings for consumer HID USAGE buttons
  HID: nvidia-shield: Initial driver implementation with Thunderstrike support
  HID: apple: Option to swap only left side mod keys
  HID: uclogic: Modular KUnit tests should not depend on KUNIT=y
  HID: fix an error code in hid_check_device_match()
  HID: logitech-hidpp: Add USB and Bluetooth IDs for the Logitech G915 TKL Keyboard
  HID: i2c-hid: Switch i2c drivers back to use .probe()
  HID: i2c-hid: goodix: Add support for "goodix,no-reset-during-suspend" property
  dt-bindings: input: goodix: Add "goodix,no-reset-during-suspend" property
  HID: microsoft: Add rumble support to latest xbox controllers
  selftests: hid: Add touch tests for Wacom devices
  HID: ensure timely release of driver-allocated resources
  HID: split apart hid_device_probe to make logic more apparent
  HID: amd_sfh: Split sensor and HID initialization for SFH1.1
  ...
parents 675285ad b571809e
......@@ -43,6 +43,15 @@ properties:
itself as long as it allows the main board to make signals compatible
with what the touchscreen is expecting for its IO rails.
goodix,no-reset-during-suspend:
description:
Set this to true to enforce the driver to not assert the reset GPIO
during suspend.
Due to potential touchscreen hardware flaw, back-powering could happen in
suspend if the power supply is on and with active-low reset GPIO asserted.
This property is used to avoid the back-powering issue.
type: boolean
required:
- compatible
- reg
......
......@@ -9200,6 +9200,12 @@ L: linux-input@vger.kernel.org
S: Maintained
F: drivers/hid/hid-pxrc.c
HID NVIDIA SHIELD DRIVER
M: Rahul Rameshbabu <rrameshbabu@nvidia.com>
L: linux-input@vger.kernel.org
S: Maintained
F: drivers/hid/hid-nvidia-shield.c
HID PLAYSTATION DRIVER
M: Roderick Colenbrander <roderick.colenbrander@sony.com>
L: linux-input@vger.kernel.org
......
......@@ -788,6 +788,24 @@ config HID_NTRIG
help
Support for N-Trig touch screen.
config HID_NVIDIA_SHIELD
tristate "NVIDIA SHIELD devices"
depends on USB_HID
depends on BT_HIDP
help
Support for NVIDIA SHIELD accessories.
Supported devices:
- Thunderstrike (NVIDIA SHIELD Controller 2017)
config NVIDIA_SHIELD_FF
bool "NVIDIA SHIELD force feedback support"
depends on HID_NVIDIA_SHIELD
select INPUT_FF_MEMLESS
help
Say Y here if you would like to enable force feedback support for
NVIDIA SHIELD accessories with haptics capabilities.
config HID_ORTEK
tristate "Ortek PKB-1700/WKB-2000/Skycable wireless keyboard and mouse trackpad"
help
......@@ -1285,7 +1303,7 @@ config HID_MCP2221
config HID_KUNIT_TEST
tristate "KUnit tests for HID" if !KUNIT_ALL_TESTS
depends on KUNIT=y
depends on KUNIT
depends on HID_BATTERY_STRENGTH
depends on HID_UCLOGIC
default KUNIT_ALL_TESTS
......
......@@ -87,6 +87,7 @@ obj-$(CONFIG_HID_MULTITOUCH) += hid-multitouch.o
obj-$(CONFIG_HID_NINTENDO) += hid-nintendo.o
obj-$(CONFIG_HID_NTI) += hid-nti.o
obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o
obj-$(CONFIG_HID_NVIDIA_SHIELD) += hid-nvidia-shield.o
obj-$(CONFIG_HID_ORTEK) += hid-ortek.o
obj-$(CONFIG_HID_PRODIKEYS) += hid-prodikeys.o
obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o
......
......@@ -215,7 +215,7 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
struct device *dev;
u32 feature_report_size;
u32 input_report_size;
int rc, i, status;
int rc, i;
u8 cl_idx;
req_list = &cl_data->req_list;
......@@ -286,56 +286,37 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
if (rc)
goto cleanup;
mp2_ops->start(privdata, info);
status = amd_sfh_wait_for_response
cl_data->sensor_sts[i] = amd_sfh_wait_for_response
(privdata, cl_data->sensor_idx[i], SENSOR_ENABLED);
if (status == SENSOR_ENABLED) {
}
for (i = 0; i < cl_data->num_hid_devices; i++) {
cl_data->cur_hid_dev = i;
if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {
cl_data->is_any_sensor_enabled = true;
cl_data->sensor_sts[i] = SENSOR_ENABLED;
rc = amdtp_hid_probe(cl_data->cur_hid_dev, cl_data);
if (rc) {
mp2_ops->stop(privdata, cl_data->sensor_idx[i]);
status = amd_sfh_wait_for_response
(privdata, cl_data->sensor_idx[i], SENSOR_DISABLED);
if (status != SENSOR_ENABLED)
cl_data->sensor_sts[i] = SENSOR_DISABLED;
dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n",
cl_data->sensor_idx[i],
get_sensor_name(cl_data->sensor_idx[i]),
cl_data->sensor_sts[i]);
rc = amdtp_hid_probe(i, cl_data);
if (rc)
goto cleanup;
}
} else {
cl_data->sensor_sts[i] = SENSOR_DISABLED;
dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n",
cl_data->sensor_idx[i],
get_sensor_name(cl_data->sensor_idx[i]),
cl_data->sensor_sts[i]);
}
dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n",
cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
cl_data->sensor_sts[i]);
}
if (!cl_data->is_any_sensor_enabled ||
(mp2_ops->discovery_status && mp2_ops->discovery_status(privdata) == 0)) {
amd_sfh_hid_client_deinit(privdata);
for (i = 0; i < cl_data->num_hid_devices; i++) {
devm_kfree(dev, cl_data->feature_report[i]);
devm_kfree(dev, in_data->input_report[i]);
devm_kfree(dev, cl_data->report_descr[i]);
}
dev_warn(dev, "Failed to discover, sensors not enabled is %d\n", cl_data->is_any_sensor_enabled);
return -EOPNOTSUPP;
rc = -EOPNOTSUPP;
goto cleanup;
}
schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));
return 0;
cleanup:
amd_sfh_hid_client_deinit(privdata);
for (i = 0; i < cl_data->num_hid_devices; i++) {
if (in_data->sensor_virt_addr[i]) {
dma_free_coherent(&privdata->pdev->dev, 8 * sizeof(int),
in_data->sensor_virt_addr[i],
cl_data->sensor_dma_addr[i]);
}
devm_kfree(dev, cl_data->feature_report[i]);
devm_kfree(dev, in_data->input_report[i]);
devm_kfree(dev, cl_data->report_descr[i]);
......
......@@ -168,29 +168,17 @@ static int amd_sfh1_1_hid_client_init(struct amd_mp2_dev *privdata)
status = amd_sfh_wait_for_response
(privdata, cl_data->sensor_idx[i], ENABLE_SENSOR);
status = (status == 0) ? SENSOR_ENABLED : SENSOR_DISABLED;
cl_data->sensor_sts[i] = (status == 0) ? SENSOR_ENABLED : SENSOR_DISABLED;
}
if (status == SENSOR_ENABLED) {
for (i = 0; i < cl_data->num_hid_devices; i++) {
cl_data->cur_hid_dev = i;
if (cl_data->sensor_sts[i] == SENSOR_ENABLED) {
cl_data->is_any_sensor_enabled = true;
cl_data->sensor_sts[i] = SENSOR_ENABLED;
rc = amdtp_hid_probe(i, cl_data);
if (rc) {
mp2_ops->stop(privdata, cl_data->sensor_idx[i]);
status = amd_sfh_wait_for_response
(privdata, cl_data->sensor_idx[i], DISABLE_SENSOR);
if (status == 0)
status = SENSOR_DISABLED;
if (status != SENSOR_ENABLED)
cl_data->sensor_sts[i] = SENSOR_DISABLED;
dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n",
cl_data->sensor_idx[i],
get_sensor_name(cl_data->sensor_idx[i]),
cl_data->sensor_sts[i]);
if (rc)
goto cleanup;
}
} else {
cl_data->sensor_sts[i] = SENSOR_DISABLED;
}
dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n",
cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
cl_data->sensor_sts[i]);
......
......@@ -58,7 +58,7 @@ static unsigned int swap_opt_cmd;
module_param(swap_opt_cmd, uint, 0644);
MODULE_PARM_DESC(swap_opt_cmd, "Swap the Option (\"Alt\") and Command (\"Flag\") keys. "
"(For people who want to keep Windows PC keyboard muscle memory. "
"[0] = as-is, Mac layout. 1 = swapped, Windows layout.)");
"[0] = as-is, Mac layout. 1 = swapped, Windows layout., 2 = swapped, Swap only left side)");
static unsigned int swap_ctrl_cmd;
module_param(swap_ctrl_cmd, uint, 0644);
......@@ -319,6 +319,12 @@ static const struct apple_key_translation swapped_option_cmd_keys[] = {
{ }
};
static const struct apple_key_translation swapped_option_cmd_left_keys[] = {
{ KEY_LEFTALT, KEY_LEFTMETA },
{ KEY_LEFTMETA, KEY_LEFTALT },
{ }
};
static const struct apple_key_translation swapped_ctrl_cmd_keys[] = {
{ KEY_LEFTCTRL, KEY_LEFTMETA },
{ KEY_LEFTMETA, KEY_LEFTCTRL },
......@@ -416,6 +422,9 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
}
if (swap_opt_cmd) {
if (swap_opt_cmd == 2)
trans = apple_find_translation(swapped_option_cmd_left_keys, code);
else
trans = apple_find_translation(swapped_option_cmd_keys, code);
if (trans)
......
......@@ -884,33 +884,20 @@ static int asus_input_mapping(struct hid_device *hdev,
case 0xb5: asus_map_key_clear(KEY_CALC); break;
case 0xc4: asus_map_key_clear(KEY_KBDILLUMUP); break;
case 0xc5: asus_map_key_clear(KEY_KBDILLUMDOWN); break;
case 0xc7: asus_map_key_clear(KEY_KBDILLUMTOGGLE); break;
/* ASUS touchpad toggle */
case 0x6b: asus_map_key_clear(KEY_F21); break;
case 0x6b: asus_map_key_clear(KEY_F21); break; /* ASUS touchpad toggle */
case 0x38: asus_map_key_clear(KEY_PROG1); break; /* ROG key */
case 0xba: asus_map_key_clear(KEY_PROG2); break; /* Fn+C ASUS Splendid */
case 0x5c: asus_map_key_clear(KEY_PROG3); break; /* Fn+Space Power4Gear */
case 0x99: asus_map_key_clear(KEY_PROG4); break; /* Fn+F5 "fan" symbol */
case 0xae: asus_map_key_clear(KEY_PROG4); break; /* Fn+F5 "fan" symbol */
case 0x92: asus_map_key_clear(KEY_CALC); break; /* Fn+Ret "Calc" symbol */
case 0xb2: asus_map_key_clear(KEY_PROG2); break; /* Fn+Left previous aura */
case 0xb3: asus_map_key_clear(KEY_PROG3); break; /* Fn+Left next aura */
case 0x6a: asus_map_key_clear(KEY_F13); break; /* Screenpad toggle */
case 0x4b: asus_map_key_clear(KEY_F14); break; /* Arrows/Pg-Up/Dn toggle */
/* ROG key */
case 0x38: asus_map_key_clear(KEY_PROG1); break;
/* Fn+C ASUS Splendid */
case 0xba: asus_map_key_clear(KEY_PROG2); break;
/* Fn+Space Power4Gear Hybrid */
case 0x5c: asus_map_key_clear(KEY_PROG3); break;
/* Fn+F5 "fan" symbol on FX503VD */
case 0x99: asus_map_key_clear(KEY_PROG4); break;
/* Fn+F5 "fan" symbol on N-Key keyboard */
case 0xae: asus_map_key_clear(KEY_PROG4); break;
/* Fn+Ret "Calc" symbol on N-Key keyboard */
case 0x92: asus_map_key_clear(KEY_CALC); break;
/* Fn+Left Aura mode previous on N-Key keyboard */
case 0xb2: asus_map_key_clear(KEY_PROG2); break;
/* Fn+Right Aura mode next on N-Key keyboard */
case 0xb3: asus_map_key_clear(KEY_PROG3); break;
default:
/* ASUS lazily declares 256 usages, ignore the rest,
......@@ -1268,6 +1255,9 @@ static const struct hid_device_id asus_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2),
QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD3),
QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_ROG_CLAYMORE_II_KEYBOARD),
QUIRK_ROG_CLAYMORE_II_KEYBOARD },
......
......@@ -2587,48 +2587,41 @@ bool hid_compare_device_paths(struct hid_device *hdev_a,
}
EXPORT_SYMBOL_GPL(hid_compare_device_paths);
static int hid_device_probe(struct device *dev)
static bool hid_check_device_match(struct hid_device *hdev,
struct hid_driver *hdrv,
const struct hid_device_id **id)
{
struct hid_driver *hdrv = to_hid_driver(dev->driver);
struct hid_device *hdev = to_hid_device(dev);
const struct hid_device_id *id;
int ret = 0;
if (down_interruptible(&hdev->driver_input_lock)) {
ret = -EINTR;
goto end;
}
hdev->io_started = false;
*id = hid_match_device(hdev, hdrv);
if (!*id)
return false;
clear_bit(ffs(HID_STAT_REPROBED), &hdev->status);
if (hdrv->match)
return hdrv->match(hdev, hid_ignore_special_drivers);
if (!hdev->driver) {
id = hid_match_device(hdev, hdrv);
if (id == NULL) {
ret = -ENODEV;
goto unlock;
}
if (hdrv->match) {
if (!hdrv->match(hdev, hid_ignore_special_drivers)) {
ret = -ENODEV;
goto unlock;
}
} else {
/*
* hid-generic implements .match(), so if
* hid_ignore_special_drivers is set, we can safely
* return.
* hid-generic implements .match(), so we must be dealing with a
* different HID driver here, and can simply check if
* hid_ignore_special_drivers is set or not.
*/
if (hid_ignore_special_drivers) {
ret = -ENODEV;
goto unlock;
}
}
return !hid_ignore_special_drivers;
}
static int __hid_device_probe(struct hid_device *hdev, struct hid_driver *hdrv)
{
const struct hid_device_id *id;
int ret;
if (!hid_check_device_match(hdev, hdrv, &id))
return -ENODEV;
hdev->devres_group_id = devres_open_group(&hdev->dev, NULL, GFP_KERNEL);
if (!hdev->devres_group_id)
return -ENOMEM;
/* reset the quirks that has been previously set */
hdev->quirks = hid_lookup_quirk(hdev);
hdev->driver = hdrv;
if (hdrv->probe) {
ret = hdrv->probe(hdev, id);
} else { /* default probe */
......@@ -2636,15 +2629,42 @@ static int hid_device_probe(struct device *dev)
if (!ret)
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
}
/*
* Note that we are not closing the devres group opened above so
* even resources that were attached to the device after probe is
* run are released when hid_device_remove() is executed. This is
* needed as some drivers would allocate additional resources,
* for example when updating firmware.
*/
if (ret) {
devres_release_group(&hdev->dev, hdev->devres_group_id);
hid_close_report(hdev);
hdev->driver = NULL;
}
}
unlock:
return ret;
}
static int hid_device_probe(struct device *dev)
{
struct hid_device *hdev = to_hid_device(dev);
struct hid_driver *hdrv = to_hid_driver(dev->driver);
int ret = 0;
if (down_interruptible(&hdev->driver_input_lock))
return -EINTR;
hdev->io_started = false;
clear_bit(ffs(HID_STAT_REPROBED), &hdev->status);
if (!hdev->driver)
ret = __hid_device_probe(hdev, hdrv);
if (!hdev->io_started)
up(&hdev->driver_input_lock);
end:
return ret;
}
......@@ -2662,6 +2682,10 @@ static void hid_device_remove(struct device *dev)
hdrv->remove(hdev);
else /* default remove */
hid_hw_stop(hdev);
/* Release all devres resources allocated by the driver */
devres_release_group(&hdev->dev, hdev->devres_group_id);
hid_close_report(hdev);
hdev->driver = NULL;
}
......
......@@ -207,6 +207,7 @@
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3 0x1822
#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD 0x1866
#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2 0x19b6
#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD3 0x1a30
#define USB_DEVICE_ID_ASUSTEK_ROG_CLAYMORE_II_KEYBOARD 0x196b
#define USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD 0x1869
......@@ -620,6 +621,7 @@
#define USB_DEVICE_ID_UGCI_FIGHTING 0x0030
#define USB_VENDOR_ID_HP 0x03f0
#define USB_PRODUCT_ID_HP_ELITE_PRESENTER_MOUSE_464A 0x464a
#define USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0A4A 0x0a4a
#define USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0B4A 0x0b4a
#define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE 0x134a
......@@ -934,7 +936,15 @@
#define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9
#define USB_DEVICE_ID_MS_POWER_COVER 0x07da
#define USB_DEVICE_ID_MS_SURFACE3_COVER 0x07de
#define USB_DEVICE_ID_MS_XBOX_ONE_S_CONTROLLER 0x02fd
/*
* For a description of the Xbox controller models, refer to:
* https://en.wikipedia.org/wiki/Xbox_Wireless_Controller#Summary
*/
#define USB_DEVICE_ID_MS_XBOX_CONTROLLER_MODEL_1708 0x02fd
#define USB_DEVICE_ID_MS_XBOX_CONTROLLER_MODEL_1708_BLE 0x0b20
#define USB_DEVICE_ID_MS_XBOX_CONTROLLER_MODEL_1914 0x0b13
#define USB_DEVICE_ID_MS_XBOX_CONTROLLER_MODEL_1797 0x0b05
#define USB_DEVICE_ID_MS_XBOX_CONTROLLER_MODEL_1797_BLE 0x0b22
#define USB_DEVICE_ID_MS_PIXART_MOUSE 0x00cb
#define USB_DEVICE_ID_8BITDO_SN30_PRO_PLUS 0x02e0
#define USB_DEVICE_ID_MS_MOUSE_0783 0x0783
......@@ -1005,6 +1015,9 @@
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_18 0x0014
#define USB_DEVICE_ID_NTRIG_DUOSENSE 0x1500
#define USB_VENDOR_ID_NVIDIA 0x0955
#define USB_DEVICE_ID_NVIDIA_THUNDERSTRIKE_CONTROLLER 0x7214
#define USB_VENDOR_ID_ONTRAK 0x0a07
#define USB_DEVICE_ID_ONTRAK_ADU100 0x0064
......
......@@ -4553,7 +4553,7 @@ static const struct hid_device_id hidpp_devices[] = {
{ /* wireless touchpad T651 */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_T651),
.driver_data = HIDPP_QUIRK_CLASS_WTP },
.driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT },
{ /* Mouse Logitech Anywhere MX */
LDJ_DEVICE(0x1017), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
{ /* Mouse logitech M560 */
......@@ -4608,6 +4608,8 @@ static const struct hid_device_id hidpp_devices[] = {
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC086) },
{ /* Logitech G903 Hero Gaming Mouse over USB */
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC091) },
{ /* Logitech G915 TKL Keyboard over USB */
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC343) },
{ /* Logitech G920 Wheel over USB */
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL),
.driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS},
......@@ -4630,6 +4632,8 @@ static const struct hid_device_id hidpp_devices[] = {
{ /* MX5500 keyboard over Bluetooth */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb30b),
.driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS },
{ /* Logitech G915 TKL keyboard over Bluetooth */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb35f) },
{ /* M-RCQ142 V470 Cordless Laser Mouse over Bluetooth */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb008) },
{ /* MX Master mouse over Bluetooth */
......
......@@ -446,7 +446,16 @@ static const struct hid_device_id ms_devices[] = {
.driver_data = MS_PRESENTER },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, 0x091B),
.driver_data = MS_SURFACE_DIAL },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_XBOX_ONE_S_CONTROLLER),
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_XBOX_CONTROLLER_MODEL_1708),
.driver_data = MS_QUIRK_FF },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_XBOX_CONTROLLER_MODEL_1708_BLE),
.driver_data = MS_QUIRK_FF },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_XBOX_CONTROLLER_MODEL_1914),
.driver_data = MS_QUIRK_FF },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_XBOX_CONTROLLER_MODEL_1797),
.driver_data = MS_QUIRK_FF },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_XBOX_CONTROLLER_MODEL_1797_BLE),
.driver_data = MS_QUIRK_FF },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_8BITDO_SN30_PRO_PLUS),
.driver_data = MS_QUIRK_FF },
......
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*
* HID driver for NVIDIA SHIELD peripherals.
*/
#include <linux/hid.h>
#include <linux/input-event-codes.h>
#include <linux/input.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include "hid-ids.h"
#define NOT_INIT_STR "NOT INITIALIZED"
#define android_map_key(c) hid_map_usage(hi, usage, bit, max, EV_KEY, (c))
enum {
HID_USAGE_ANDROID_PLAYPAUSE_BTN = 0xcd, /* Double-tap volume slider */
HID_USAGE_ANDROID_VOLUMEUP_BTN = 0xe9,
HID_USAGE_ANDROID_VOLUMEDOWN_BTN = 0xea,
HID_USAGE_ANDROID_SEARCH_BTN = 0x221, /* NVIDIA btn on Thunderstrike */
HID_USAGE_ANDROID_HOME_BTN = 0x223,
HID_USAGE_ANDROID_BACK_BTN = 0x224,
};
enum {
SHIELD_FW_VERSION_INITIALIZED = 0,
SHIELD_BOARD_INFO_INITIALIZED,
};
enum {
THUNDERSTRIKE_FW_VERSION_UPDATE = 0,
THUNDERSTRIKE_BOARD_INFO_UPDATE,
THUNDERSTRIKE_HAPTICS_UPDATE,
THUNDERSTRIKE_LED_UPDATE,
};
enum {
THUNDERSTRIKE_HOSTCMD_REPORT_SIZE = 33,
THUNDERSTRIKE_HOSTCMD_REQ_REPORT_ID = 0x4,
THUNDERSTRIKE_HOSTCMD_RESP_REPORT_ID = 0x3,
};
enum {
THUNDERSTRIKE_HOSTCMD_ID_FW_VERSION = 1,
THUNDERSTRIKE_HOSTCMD_ID_LED = 6,
THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO = 16,
THUNDERSTRIKE_HOSTCMD_ID_USB_INIT = 53,
THUNDERSTRIKE_HOSTCMD_ID_HAPTICS = 57,
THUNDERSTRIKE_HOSTCMD_ID_BLUETOOTH_INIT = 58,
};
enum thunderstrike_led_state {
THUNDERSTRIKE_LED_OFF = 1,
THUNDERSTRIKE_LED_ON = 8,
} __packed;
static_assert(sizeof(enum thunderstrike_led_state) == 1);
struct thunderstrike_hostcmd_board_info {
__le16 revision;
__le16 serial[7];
};
struct thunderstrike_hostcmd_haptics {
u8 motor_left;
u8 motor_right;
};
struct thunderstrike_hostcmd_resp_report {
u8 report_id; /* THUNDERSTRIKE_HOSTCMD_RESP_REPORT_ID */
u8 cmd_id;
u8 reserved_at_10;
union {
struct thunderstrike_hostcmd_board_info board_info;
struct thunderstrike_hostcmd_haptics motors;
__le16 fw_version;
enum thunderstrike_led_state led_state;
u8 payload[30];
};
} __packed;
static_assert(sizeof(struct thunderstrike_hostcmd_resp_report) ==
THUNDERSTRIKE_HOSTCMD_REPORT_SIZE);
struct thunderstrike_hostcmd_req_report {
u8 report_id; /* THUNDERSTRIKE_HOSTCMD_REQ_REPORT_ID */
u8 cmd_id;
u8 reserved_at_10;
union {
struct {
u8 update;
enum thunderstrike_led_state state;
} led;
struct {
u8 update;
struct thunderstrike_hostcmd_haptics motors;
} haptics;
};
u8 reserved_at_30[27];
} __packed;
static_assert(sizeof(struct thunderstrike_hostcmd_req_report) ==
THUNDERSTRIKE_HOSTCMD_REPORT_SIZE);
/* Common struct for shield accessories. */
struct shield_device {
struct hid_device *hdev;
unsigned long initialized_flags;
const char *codename;
u16 fw_version;
struct {
u16 revision;
char serial_number[15];
} board_info;
};
struct thunderstrike {
struct shield_device base;
/* Sub-devices */
struct input_dev *haptics_dev;
struct led_classdev led_dev;
/* Resources */
void *req_report_dmabuf;
unsigned long update_flags;
struct thunderstrike_hostcmd_haptics haptics_val;
spinlock_t haptics_update_lock;
u8 led_state : 1;
enum thunderstrike_led_state led_value;
struct work_struct hostcmd_req_work;
};
static inline void thunderstrike_hostcmd_req_report_init(
struct thunderstrike_hostcmd_req_report *report, u8 cmd_id)
{
memset(report, 0, sizeof(*report));
report->report_id = THUNDERSTRIKE_HOSTCMD_REQ_REPORT_ID;
report->cmd_id = cmd_id;
}
static inline void shield_strrev(char *dest, size_t len, u16 rev)
{
dest[0] = ('A' - 1) + (rev >> 8);
snprintf(&dest[1], len - 1, "%02X", 0xff & rev);
}
static struct input_dev *shield_allocate_input_dev(struct hid_device *hdev,
const char *name_suffix)
{
struct input_dev *idev;
idev = input_allocate_device();
if (!idev)
goto err_device;
idev->id.bustype = hdev->bus;
idev->id.vendor = hdev->vendor;
idev->id.product = hdev->product;
idev->id.version = hdev->version;
idev->uniq = hdev->uniq;
idev->name = devm_kasprintf(&idev->dev, GFP_KERNEL, "%s %s", hdev->name,
name_suffix);
if (!idev->name)
goto err_name;
input_set_drvdata(idev, hdev);
return idev;
err_name:
input_free_device(idev);
err_device:
return ERR_PTR(-ENOMEM);
}
static struct input_dev *shield_haptics_create(
struct shield_device *dev,
int (*play_effect)(struct input_dev *, void *, struct ff_effect *))
{
struct input_dev *haptics;
int ret;
if (!IS_ENABLED(CONFIG_NVIDIA_SHIELD_FF))
return NULL;
haptics = shield_allocate_input_dev(dev->hdev, "Haptics");
if (IS_ERR(haptics))
return haptics;
input_set_capability(haptics, EV_FF, FF_RUMBLE);
input_ff_create_memless(haptics, NULL, play_effect);
ret = input_register_device(haptics);
if (ret)
goto err;
return haptics;
err:
input_free_device(haptics);
return ERR_PTR(ret);
}
static inline void thunderstrike_send_hostcmd_request(struct thunderstrike *ts)
{
struct thunderstrike_hostcmd_req_report *report = ts->req_report_dmabuf;
struct shield_device *shield_dev = &ts->base;
int ret;
ret = hid_hw_raw_request(shield_dev->hdev, report->report_id,
ts->req_report_dmabuf,
THUNDERSTRIKE_HOSTCMD_REPORT_SIZE,
HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
if (ret < 0) {
hid_err(shield_dev->hdev,
"Failed to output Thunderstrike HOSTCMD request HID report due to %pe\n",
ERR_PTR(ret));
}
}
static void thunderstrike_hostcmd_req_work_handler(struct work_struct *work)
{
struct thunderstrike *ts =
container_of(work, struct thunderstrike, hostcmd_req_work);
struct thunderstrike_hostcmd_req_report *report;
unsigned long flags;
report = ts->req_report_dmabuf;
if (test_and_clear_bit(THUNDERSTRIKE_FW_VERSION_UPDATE, &ts->update_flags)) {
thunderstrike_hostcmd_req_report_init(
report, THUNDERSTRIKE_HOSTCMD_ID_FW_VERSION);
thunderstrike_send_hostcmd_request(ts);
}
if (test_and_clear_bit(THUNDERSTRIKE_LED_UPDATE, &ts->update_flags)) {
thunderstrike_hostcmd_req_report_init(report, THUNDERSTRIKE_HOSTCMD_ID_LED);
report->led.update = 1;
report->led.state = ts->led_value;
thunderstrike_send_hostcmd_request(ts);
}
if (test_and_clear_bit(THUNDERSTRIKE_BOARD_INFO_UPDATE, &ts->update_flags)) {
thunderstrike_hostcmd_req_report_init(
report, THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO);
thunderstrike_send_hostcmd_request(ts);
}
if (test_and_clear_bit(THUNDERSTRIKE_HAPTICS_UPDATE, &ts->update_flags)) {
thunderstrike_hostcmd_req_report_init(
report, THUNDERSTRIKE_HOSTCMD_ID_HAPTICS);
report->haptics.update = 1;
spin_lock_irqsave(&ts->haptics_update_lock, flags);
report->haptics.motors = ts->haptics_val;
spin_unlock_irqrestore(&ts->haptics_update_lock, flags);
thunderstrike_send_hostcmd_request(ts);
}
}
static inline void thunderstrike_request_firmware_version(struct thunderstrike *ts)
{
set_bit(THUNDERSTRIKE_FW_VERSION_UPDATE, &ts->update_flags);
schedule_work(&ts->hostcmd_req_work);
}
static inline void thunderstrike_request_board_info(struct thunderstrike *ts)
{
set_bit(THUNDERSTRIKE_BOARD_INFO_UPDATE, &ts->update_flags);
schedule_work(&ts->hostcmd_req_work);
}
static inline int
thunderstrike_update_haptics(struct thunderstrike *ts,
struct thunderstrike_hostcmd_haptics *motors)
{
unsigned long flags;
spin_lock_irqsave(&ts->haptics_update_lock, flags);
ts->haptics_val = *motors;
spin_unlock_irqrestore(&ts->haptics_update_lock, flags);
set_bit(THUNDERSTRIKE_HAPTICS_UPDATE, &ts->update_flags);
schedule_work(&ts->hostcmd_req_work);
return 0;
}
static int thunderstrike_play_effect(struct input_dev *idev, void *data,
struct ff_effect *effect)
{
struct hid_device *hdev = input_get_drvdata(idev);
struct thunderstrike_hostcmd_haptics motors;
struct shield_device *shield_dev;
struct thunderstrike *ts;
if (effect->type != FF_RUMBLE)
return 0;
shield_dev = hid_get_drvdata(hdev);
ts = container_of(shield_dev, struct thunderstrike, base);
/* Thunderstrike motor values range from 0 to 32 inclusively */
motors.motor_left = effect->u.rumble.strong_magnitude / 2047;
motors.motor_right = effect->u.rumble.weak_magnitude / 2047;
hid_dbg(hdev, "Thunderstrike FF_RUMBLE request, left: %u right: %u\n",
motors.motor_left, motors.motor_right);
return thunderstrike_update_haptics(ts, &motors);
}
static enum led_brightness
thunderstrike_led_get_brightness(struct led_classdev *led)
{
struct hid_device *hdev = to_hid_device(led->dev->parent);
struct shield_device *shield_dev = hid_get_drvdata(hdev);
struct thunderstrike *ts;
ts = container_of(shield_dev, struct thunderstrike, base);
return ts->led_state;
}
static void thunderstrike_led_set_brightness(struct led_classdev *led,
enum led_brightness value)
{
struct hid_device *hdev = to_hid_device(led->dev->parent);
struct shield_device *shield_dev = hid_get_drvdata(hdev);
struct thunderstrike *ts;
ts = container_of(shield_dev, struct thunderstrike, base);
switch (value) {
case LED_OFF:
ts->led_value = THUNDERSTRIKE_LED_OFF;
break;
default:
ts->led_value = THUNDERSTRIKE_LED_ON;
break;
}
set_bit(THUNDERSTRIKE_LED_UPDATE, &ts->update_flags);
schedule_work(&ts->hostcmd_req_work);
}
static void
thunderstrike_parse_fw_version_payload(struct shield_device *shield_dev,
__le16 fw_version)
{
shield_dev->fw_version = le16_to_cpu(fw_version);
set_bit(SHIELD_FW_VERSION_INITIALIZED, &shield_dev->initialized_flags);
hid_dbg(shield_dev->hdev, "Thunderstrike firmware version 0x%04X\n",
shield_dev->fw_version);
}
static void
thunderstrike_parse_board_info_payload(struct shield_device *shield_dev,
struct thunderstrike_hostcmd_board_info *board_info)
{
char board_revision_str[4];
int i;
shield_dev->board_info.revision = le16_to_cpu(board_info->revision);
for (i = 0; i < 7; ++i) {
u16 val = le16_to_cpu(board_info->serial[i]);
shield_dev->board_info.serial_number[2 * i] = val & 0xFF;
shield_dev->board_info.serial_number[2 * i + 1] = val >> 8;
}
shield_dev->board_info.serial_number[14] = '\0';
set_bit(SHIELD_BOARD_INFO_INITIALIZED, &shield_dev->initialized_flags);
shield_strrev(board_revision_str, 4, shield_dev->board_info.revision);
hid_dbg(shield_dev->hdev,
"Thunderstrike BOARD_REVISION_%s (0x%04X) S/N: %s\n",
board_revision_str, shield_dev->board_info.revision,
shield_dev->board_info.serial_number);
}
static inline void
thunderstrike_parse_haptics_payload(struct shield_device *shield_dev,
struct thunderstrike_hostcmd_haptics *haptics)
{
hid_dbg(shield_dev->hdev,
"Thunderstrike haptics HOSTCMD response, left: %u right: %u\n",
haptics->motor_left, haptics->motor_right);
}
static void
thunderstrike_parse_led_payload(struct shield_device *shield_dev,
enum thunderstrike_led_state led_state)
{
struct thunderstrike *ts = container_of(shield_dev, struct thunderstrike, base);
switch (led_state) {
case THUNDERSTRIKE_LED_OFF:
ts->led_state = 0;
break;
case THUNDERSTRIKE_LED_ON:
ts->led_state = 1;
break;
}
hid_dbg(shield_dev->hdev, "Thunderstrike led HOSTCMD response, 0x%02X\n", led_state);
}
static int thunderstrike_parse_report(struct shield_device *shield_dev,
struct hid_report *report, u8 *data,
int size)
{
struct thunderstrike_hostcmd_resp_report *hostcmd_resp_report;
struct thunderstrike *ts =
container_of(shield_dev, struct thunderstrike, base);
struct hid_device *hdev = shield_dev->hdev;
switch (report->id) {
case THUNDERSTRIKE_HOSTCMD_RESP_REPORT_ID:
if (size != THUNDERSTRIKE_HOSTCMD_REPORT_SIZE) {
hid_err(hdev,
"Encountered Thunderstrike HOSTCMD HID report with unexpected size %d\n",
size);
return -EINVAL;
}
hostcmd_resp_report =
(struct thunderstrike_hostcmd_resp_report *)data;
switch (hostcmd_resp_report->cmd_id) {
case THUNDERSTRIKE_HOSTCMD_ID_FW_VERSION:
thunderstrike_parse_fw_version_payload(
shield_dev, hostcmd_resp_report->fw_version);
break;
case THUNDERSTRIKE_HOSTCMD_ID_LED:
thunderstrike_parse_led_payload(shield_dev, hostcmd_resp_report->led_state);
break;
case THUNDERSTRIKE_HOSTCMD_ID_BOARD_INFO:
thunderstrike_parse_board_info_payload(
shield_dev, &hostcmd_resp_report->board_info);
break;
case THUNDERSTRIKE_HOSTCMD_ID_HAPTICS:
thunderstrike_parse_haptics_payload(
shield_dev, &hostcmd_resp_report->motors);
break;
case THUNDERSTRIKE_HOSTCMD_ID_USB_INIT:
case THUNDERSTRIKE_HOSTCMD_ID_BLUETOOTH_INIT:
/* May block HOSTCMD requests till received initially */
thunderstrike_request_firmware_version(ts);
thunderstrike_request_board_info(ts);
/* Only HOSTCMD that can be triggered without a request */
return 0;
default:
hid_warn(hdev,
"Unhandled Thunderstrike HOSTCMD id %d\n",
hostcmd_resp_report->cmd_id);
return -ENOENT;
}
break;
default:
return 0;
}
return 0;
}
static inline int thunderstrike_led_create(struct thunderstrike *ts)
{
struct led_classdev *led = &ts->led_dev;
led->name = "thunderstrike:blue:led";
led->max_brightness = 1;
led->flags = LED_CORE_SUSPENDRESUME;
led->brightness_get = &thunderstrike_led_get_brightness;
led->brightness_set = &thunderstrike_led_set_brightness;
return led_classdev_register(&ts->base.hdev->dev, led);
}
static struct shield_device *thunderstrike_create(struct hid_device *hdev)
{
struct shield_device *shield_dev;
struct thunderstrike *ts;
int ret;
ts = devm_kzalloc(&hdev->dev, sizeof(*ts), GFP_KERNEL);
if (!ts)
return ERR_PTR(-ENOMEM);
ts->req_report_dmabuf = devm_kzalloc(
&hdev->dev, THUNDERSTRIKE_HOSTCMD_REPORT_SIZE, GFP_KERNEL);
if (!ts->req_report_dmabuf)
return ERR_PTR(-ENOMEM);
shield_dev = &ts->base;
shield_dev->hdev = hdev;
shield_dev->codename = "Thunderstrike";
spin_lock_init(&ts->haptics_update_lock);
INIT_WORK(&ts->hostcmd_req_work, thunderstrike_hostcmd_req_work_handler);
hid_set_drvdata(hdev, shield_dev);
ret = thunderstrike_led_create(ts);
if (ret) {
hid_err(hdev, "Failed to create Thunderstrike LED instance\n");
return ERR_PTR(ret);
}
ts->haptics_dev = shield_haptics_create(shield_dev, thunderstrike_play_effect);
if (IS_ERR(ts->haptics_dev))
goto err;
hid_info(hdev, "Registered Thunderstrike controller\n");
return shield_dev;
err:
led_classdev_unregister(&ts->led_dev);
return ERR_CAST(ts->haptics_dev);
}
static int android_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field,
struct hid_usage *usage, unsigned long **bit,
int *max)
{
if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
return 0;
switch (usage->hid & HID_USAGE) {
case HID_USAGE_ANDROID_PLAYPAUSE_BTN:
android_map_key(KEY_PLAYPAUSE);
break;
case HID_USAGE_ANDROID_VOLUMEUP_BTN:
android_map_key(KEY_VOLUMEUP);
break;
case HID_USAGE_ANDROID_VOLUMEDOWN_BTN:
android_map_key(KEY_VOLUMEDOWN);
break;
case HID_USAGE_ANDROID_SEARCH_BTN:
android_map_key(BTN_Z);
break;
case HID_USAGE_ANDROID_HOME_BTN:
android_map_key(BTN_MODE);
break;
case HID_USAGE_ANDROID_BACK_BTN:
android_map_key(BTN_SELECT);
break;
default:
return 0;
}
return 1;
}
static ssize_t firmware_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hid_device *hdev = to_hid_device(dev);
struct shield_device *shield_dev;
int ret;
shield_dev = hid_get_drvdata(hdev);
if (test_bit(SHIELD_FW_VERSION_INITIALIZED, &shield_dev->initialized_flags))
ret = sysfs_emit(buf, "0x%04X\n", shield_dev->fw_version);
else
ret = sysfs_emit(buf, NOT_INIT_STR "\n");
return ret;
}
static DEVICE_ATTR_RO(firmware_version);
static ssize_t hardware_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hid_device *hdev = to_hid_device(dev);
struct shield_device *shield_dev;
char board_revision_str[4];
int ret;
shield_dev = hid_get_drvdata(hdev);
if (test_bit(SHIELD_BOARD_INFO_INITIALIZED, &shield_dev->initialized_flags)) {
shield_strrev(board_revision_str, 4, shield_dev->board_info.revision);
ret = sysfs_emit(buf, "%s BOARD_REVISION_%s (0x%04X)\n",
shield_dev->codename, board_revision_str,
shield_dev->board_info.revision);
} else
ret = sysfs_emit(buf, NOT_INIT_STR "\n");
return ret;
}
static DEVICE_ATTR_RO(hardware_version);
static ssize_t serial_number_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hid_device *hdev = to_hid_device(dev);
struct shield_device *shield_dev;
int ret;
shield_dev = hid_get_drvdata(hdev);
if (test_bit(SHIELD_BOARD_INFO_INITIALIZED, &shield_dev->initialized_flags))
ret = sysfs_emit(buf, "%s\n", shield_dev->board_info.serial_number);
else
ret = sysfs_emit(buf, NOT_INIT_STR "\n");
return ret;
}
static DEVICE_ATTR_RO(serial_number);
static struct attribute *shield_device_attrs[] = {
&dev_attr_firmware_version.attr,
&dev_attr_hardware_version.attr,
&dev_attr_serial_number.attr,
NULL,
};
ATTRIBUTE_GROUPS(shield_device);
static int shield_raw_event(struct hid_device *hdev, struct hid_report *report,
u8 *data, int size)
{
struct shield_device *dev = hid_get_drvdata(hdev);
return thunderstrike_parse_report(dev, report, data, size);
}
static int shield_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
struct shield_device *shield_dev = NULL;
struct thunderstrike *ts;
int ret;
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "Parse failed\n");
return ret;
}
switch (id->product) {
case USB_DEVICE_ID_NVIDIA_THUNDERSTRIKE_CONTROLLER:
shield_dev = thunderstrike_create(hdev);
break;
}
if (unlikely(!shield_dev)) {
hid_err(hdev, "Failed to identify SHIELD device\n");
return -ENODEV;
}
if (IS_ERR(shield_dev)) {
hid_err(hdev, "Failed to create SHIELD device\n");
return PTR_ERR(shield_dev);
}
ts = container_of(shield_dev, struct thunderstrike, base);
ret = hid_hw_start(hdev, HID_CONNECT_HIDINPUT);
if (ret) {
hid_err(hdev, "Failed to start HID device\n");
goto err_haptics;
}
ret = hid_hw_open(hdev);
if (ret) {
hid_err(hdev, "Failed to open HID device\n");
goto err_stop;
}
thunderstrike_request_firmware_version(ts);
thunderstrike_request_board_info(ts);
return ret;
err_stop:
hid_hw_stop(hdev);
err_haptics:
if (ts->haptics_dev)
input_unregister_device(ts->haptics_dev);
return ret;
}
static void shield_remove(struct hid_device *hdev)
{
struct shield_device *dev = hid_get_drvdata(hdev);
struct thunderstrike *ts;
ts = container_of(dev, struct thunderstrike, base);
hid_hw_close(hdev);
led_classdev_unregister(&ts->led_dev);
if (ts->haptics_dev)
input_unregister_device(ts->haptics_dev);
cancel_work_sync(&ts->hostcmd_req_work);
hid_hw_stop(hdev);
}
static const struct hid_device_id shield_devices[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NVIDIA,
USB_DEVICE_ID_NVIDIA_THUNDERSTRIKE_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_NVIDIA,
USB_DEVICE_ID_NVIDIA_THUNDERSTRIKE_CONTROLLER) },
{ }
};
MODULE_DEVICE_TABLE(hid, shield_devices);
static struct hid_driver shield_driver = {
.name = "shield",
.id_table = shield_devices,
.input_mapping = android_input_mapping,
.probe = shield_probe,
.remove = shield_remove,
.raw_event = shield_raw_event,
.driver = {
.dev_groups = shield_device_groups,
},
};
module_hid_driver(shield_driver);
MODULE_AUTHOR("Rahul Rameshbabu <rrameshbabu@nvidia.com>");
MODULE_DESCRIPTION("HID Driver for NVIDIA SHIELD peripherals.");
MODULE_LICENSE("GPL");
......@@ -96,6 +96,7 @@ static const struct hid_device_id hid_quirks[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD_A096), HID_QUIRK_NO_INIT_REPORTS },
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD_A293), HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0A4A), HID_QUIRK_ALWAYS_POLL },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_ELITE_PRESENTER_MOUSE_464A), HID_QUIRK_MULTI_INPUT },
{ HID_USB_DEVICE(USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0B4A), HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE), HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE_094A), HID_QUIRK_ALWAYS_POLL },
......
......@@ -272,7 +272,12 @@ static int hidraw_open(struct inode *inode, struct file *file)
goto out;
}
down_read(&minors_rwsem);
/*
* Technically not writing to the hidraw_table but a write lock is
* required to protect the device refcount. This is symmetrical to
* hidraw_release().
*/
down_write(&minors_rwsem);
if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
err = -ENODEV;
goto out_unlock;
......@@ -301,7 +306,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:
up_read(&minors_rwsem);
up_write(&minors_rwsem);
out:
if (err < 0)
kfree(list);
......
......@@ -118,7 +118,7 @@ static struct i2c_driver i2c_hid_acpi_driver = {
.acpi_match_table = i2c_hid_acpi_match,
},
.probe_new = i2c_hid_acpi_probe,
.probe = i2c_hid_acpi_probe,
.remove = i2c_hid_core_remove,
.shutdown = i2c_hid_core_shutdown,
};
......
......@@ -118,7 +118,7 @@ static struct i2c_driver elan_i2c_hid_ts_driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = of_match_ptr(elan_i2c_hid_of_match),
},
.probe_new = i2c_hid_of_elan_probe,
.probe = i2c_hid_of_elan_probe,
.remove = i2c_hid_core_remove,
.shutdown = i2c_hid_core_shutdown,
};
......
......@@ -28,6 +28,7 @@ struct i2c_hid_of_goodix {
struct regulator *vdd;
struct regulator *vddio;
struct gpio_desc *reset_gpio;
bool no_reset_during_suspend;
const struct goodix_i2c_hid_timing_data *timings;
};
......@@ -37,6 +38,14 @@ static int goodix_i2c_hid_power_up(struct i2chid_ops *ops)
container_of(ops, struct i2c_hid_of_goodix, ops);
int ret;
/*
* We assert reset GPIO here (instead of during power-down) to ensure
* the device will have a clean state after powering up, just like the
* normal scenarios will have.
*/
if (ihid_goodix->no_reset_during_suspend)
gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 1);
ret = regulator_enable(ihid_goodix->vdd);
if (ret)
return ret;
......@@ -60,7 +69,9 @@ static void goodix_i2c_hid_power_down(struct i2chid_ops *ops)
struct i2c_hid_of_goodix *ihid_goodix =
container_of(ops, struct i2c_hid_of_goodix, ops);
if (!ihid_goodix->no_reset_during_suspend)
gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 1);
regulator_disable(ihid_goodix->vddio);
regulator_disable(ihid_goodix->vdd);
}
......@@ -91,6 +102,9 @@ static int i2c_hid_of_goodix_probe(struct i2c_client *client)
if (IS_ERR(ihid_goodix->vddio))
return PTR_ERR(ihid_goodix->vddio);
ihid_goodix->no_reset_during_suspend =
of_property_read_bool(client->dev.of_node, "goodix,no-reset-during-suspend");
ihid_goodix->timings = device_get_match_data(&client->dev);
return i2c_hid_core_probe(client, &ihid_goodix->ops, 0x0001, 0);
......@@ -114,7 +128,7 @@ static struct i2c_driver goodix_i2c_hid_ts_driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = of_match_ptr(goodix_i2c_hid_of_match),
},
.probe_new = i2c_hid_of_goodix_probe,
.probe = i2c_hid_of_goodix_probe,
.remove = i2c_hid_core_remove,
.shutdown = i2c_hid_core_shutdown,
};
......
......@@ -157,7 +157,7 @@ static struct i2c_driver i2c_hid_of_driver = {
.of_match_table = of_match_ptr(i2c_hid_of_match),
},
.probe_new = i2c_hid_of_probe,
.probe = i2c_hid_of_probe,
.remove = i2c_hid_core_remove,
.shutdown = i2c_hid_core_shutdown,
.id_table = i2c_hid_of_id_table,
......
......@@ -33,6 +33,7 @@
#define ADL_N_DEVICE_ID 0x54FC
#define RPL_S_DEVICE_ID 0x7A78
#define MTL_P_DEVICE_ID 0x7E45
#define ARL_H_DEVICE_ID 0x7745
#define REVISION_ID_CHT_A0 0x6
#define REVISION_ID_CHT_Ax_SI 0x0
......
......@@ -44,6 +44,7 @@ static const struct pci_device_id ish_pci_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, ADL_N_DEVICE_ID)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, RPL_S_DEVICE_ID)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MTL_P_DEVICE_ID)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, ARL_H_DEVICE_ID)},
{0, }
};
MODULE_DEVICE_TABLE(pci, ish_pci_tbl);
......
......@@ -1314,7 +1314,7 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom)
struct input_dev *pen_input = wacom->pen_input;
unsigned char *data = wacom->data;
int number_of_valid_frames = 0;
int time_interval = 15000000;
ktime_t time_interval = 15000000;
ktime_t time_packet_received = ktime_get();
int i;
......@@ -1348,7 +1348,7 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom)
if (number_of_valid_frames) {
if (wacom->hid_data.time_delayed)
time_interval = ktime_get() - wacom->hid_data.time_delayed;
time_interval /= number_of_valid_frames;
time_interval = div_u64(time_interval, number_of_valid_frames);
wacom->hid_data.time_delayed = time_packet_received;
}
......@@ -1359,7 +1359,7 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom)
bool range = frame[0] & 0x20;
bool invert = frame[0] & 0x10;
int frames_number_reversed = number_of_valid_frames - i - 1;
int event_timestamp = time_packet_received - frames_number_reversed * time_interval;
ktime_t event_timestamp = time_packet_received - frames_number_reversed * time_interval;
if (!valid)
continue;
......
......@@ -324,7 +324,7 @@ struct hid_data {
int ps_connected;
bool pad_input_event_flag;
unsigned short sequence_number;
int time_delayed;
ktime_t time_delayed;
};
struct wacom_remote_data {
......
......@@ -597,6 +597,7 @@ struct hid_device { /* device report descriptor */
struct semaphore driver_input_lock; /* protects the current driver */
struct device dev; /* device */
struct hid_driver *driver;
void *devres_group_id; /* ID of probe devres group */
const struct hid_ll_driver *ll_driver;
struct mutex ll_open_lock;
......
......@@ -31,6 +31,7 @@ from enum import Enum
from hidtools.hut import HUT
from hidtools.hid import HidUnit
from . import base
from . import test_multitouch
import libevdev
import pytest
......@@ -517,7 +518,7 @@ class BaseTest:
for usage in get_report_usages(report):
yield usage
def assertName(self, uhdev):
def assertName(self, uhdev, type):
"""
Assert that the name is as we expect.
......@@ -526,7 +527,7 @@ class BaseTest:
this assertion from the base class to work properly.
"""
evdev = uhdev.get_evdev()
expected_name = uhdev.name + " Pen"
expected_name = uhdev.name + type
if "wacom" not in expected_name.lower():
expected_name = "Wacom " + expected_name
assert evdev.name == expected_name
......@@ -549,6 +550,12 @@ class BaseTest:
usage_id("Generic Desktop", "Y"): PhysRange(
PhysRange.CENTIMETER, 5, 150
),
usage_id("Digitizers", "Width"): PhysRange(
PhysRange.CENTIMETER, 5, 150
),
usage_id("Digitizers", "Height"): PhysRange(
PhysRange.CENTIMETER, 5, 150
),
usage_id("Digitizers", "X Tilt"): PhysRange(PhysRange.DEGREE, 90, 180),
usage_id("Digitizers", "Y Tilt"): PhysRange(PhysRange.DEGREE, 90, 180),
usage_id("Digitizers", "Twist"): PhysRange(PhysRange.DEGREE, 358, 360),
......@@ -603,7 +610,17 @@ class BaseTest:
pass
class TestOpaqueTablet(BaseTest.TestTablet):
class PenTabletTest(BaseTest.TestTablet):
def assertName(self, uhdev):
super().assertName(uhdev, " Pen")
class TouchTabletTest(BaseTest.TestTablet):
def assertName(self, uhdev):
super().assertName(uhdev, " Finger")
class TestOpaqueTablet(PenTabletTest):
def create_device(self):
return OpaqueTablet()
......@@ -842,3 +859,64 @@ class TestPTHX60_Pen(TestOpaqueCTLTablet):
libevdev.InputEvent(libevdev.EV_KEY.BTN_0, 0),
],
)
class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest):
def create_device(self):
return test_multitouch.Digitizer(
"DTH 2452",
rdesc="05 0d 09 04 a1 01 85 0c 95 01 75 08 15 00 26 ff 00 81 03 09 54 81 02 09 22 a1 02 05 0d 95 01 75 01 25 01 09 42 81 02 81 03 09 47 81 02 95 05 81 03 09 51 26 ff 00 75 10 95 01 81 02 35 00 65 11 55 0e 05 01 09 30 26 a0 44 46 96 14 81 42 09 31 26 9a 26 46 95 0b 81 42 05 0d 75 08 95 01 15 00 09 48 26 5f 00 46 7c 14 81 02 09 49 25 35 46 7d 0b 81 02 45 00 65 00 55 00 c0 05 0d 09 22 a1 02 05 0d 95 01 75 01 25 01 09 42 81 02 81 03 09 47 81 02 95 05 81 03 09 51 26 ff 00 75 10 95 01 81 02 35 00 65 11 55 0e 05 01 09 30 26 a0 44 46 96 14 81 42 09 31 26 9a 26 46 95 0b 81 42 05 0d 75 08 95 01 15 00 09 48 26 5f 00 46 7c 14 81 02 09 49 25 35 46 7d 0b 81 02 45 00 65 00 55 00 c0 05 0d 09 22 a1 02 05 0d 95 01 75 01 25 01 09 42 81 02 81 03 09 47 81 02 95 05 81 03 09 51 26 ff 00 75 10 95 01 81 02 35 00 65 11 55 0e 05 01 09 30 26 a0 44 46 96 14 81 42 09 31 26 9a 26 46 95 0b 81 42 05 0d 75 08 95 01 15 00 09 48 26 5f 00 46 7c 14 81 02 09 49 25 35 46 7d 0b 81 02 45 00 65 00 55 00 c0 05 0d 09 22 a1 02 05 0d 95 01 75 01 25 01 09 42 81 02 81 03 09 47 81 02 95 05 81 03 09 51 26 ff 00 75 10 95 01 81 02 35 00 65 11 55 0e 05 01 09 30 26 a0 44 46 96 14 81 42 09 31 26 9a 26 46 95 0b 81 42 05 0d 75 08 95 01 15 00 09 48 26 5f 00 46 7c 14 81 02 09 49 25 35 46 7d 0b 81 02 45 00 65 00 55 00 c0 05 0d 09 22 a1 02 05 0d 95 01 75 01 25 01 09 42 81 02 81 03 09 47 81 02 95 05 81 03 09 51 26 ff 00 75 10 95 01 81 02 35 00 65 11 55 0e 05 01 09 30 26 a0 44 46 96 14 81 42 09 31 26 9a 26 46 95 0b 81 42 05 0d 75 08 95 01 15 00 09 48 26 5f 00 46 7c 14 81 02 09 49 25 35 46 7d 0b 81 02 45 00 65 00 55 00 c0 05 0d 27 ff ff 00 00 75 10 95 01 09 56 81 02 75 08 95 0e 81 03 09 55 26 ff 00 75 08 b1 02 85 0a 06 00 ff 09 c5 96 00 01 b1 02 c0 06 00 ff 09 01 a1 01 09 01 85 13 15 00 26 ff 00 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0",
input_info=(0x3, 0x056A, 0x0383),
)
def test_contact_id_0(self):
"""
Bring a finger in contact with the tablet, then hold it down and remove it.
Ensure that even with contact ID = 0 which is usually given as an invalid
touch event by most tablets with the exception of a few, that given the
confidence bit is set to 1 it should process it as a valid touch to cover
the few tablets using contact ID = 0 as a valid touch value.
"""
uhdev = self.uhdev
evdev = uhdev.get_evdev()
t0 = test_multitouch.Touch(0, 50, 100)
r = uhdev.event([t0])
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
slot = self.get_slot(uhdev, t0, 0)
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events
assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0
assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50
assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100
t0.tipswitch = False
if uhdev.quirks is None or "VALID_IS_INRANGE" not in uhdev.quirks:
t0.inrange = False
r = uhdev.event([t0])
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events
assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1
def test_confidence_false(self):
"""
Bring a finger in contact with the tablet with confidence set to false.
Ensure that the confidence bit being set to false should not result in a touch event.
"""
uhdev = self.uhdev
evdev = uhdev.get_evdev()
t0 = test_multitouch.Touch(1, 50, 100)
t0.confidence = False
r = uhdev.event([t0])
events = uhdev.next_sync_events()
self.debug_reports(r, uhdev, events)
slot = self.get_slot(uhdev, t0, 0)
assert not events
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment