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: ...@@ -43,6 +43,15 @@ properties:
itself as long as it allows the main board to make signals compatible itself as long as it allows the main board to make signals compatible
with what the touchscreen is expecting for its IO rails. 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: required:
- compatible - compatible
- reg - reg
......
...@@ -9200,6 +9200,12 @@ L: linux-input@vger.kernel.org ...@@ -9200,6 +9200,12 @@ L: linux-input@vger.kernel.org
S: Maintained S: Maintained
F: drivers/hid/hid-pxrc.c 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 HID PLAYSTATION DRIVER
M: Roderick Colenbrander <roderick.colenbrander@sony.com> M: Roderick Colenbrander <roderick.colenbrander@sony.com>
L: linux-input@vger.kernel.org L: linux-input@vger.kernel.org
......
...@@ -788,6 +788,24 @@ config HID_NTRIG ...@@ -788,6 +788,24 @@ config HID_NTRIG
help help
Support for N-Trig touch screen. 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 config HID_ORTEK
tristate "Ortek PKB-1700/WKB-2000/Skycable wireless keyboard and mouse trackpad" tristate "Ortek PKB-1700/WKB-2000/Skycable wireless keyboard and mouse trackpad"
help help
...@@ -1285,7 +1303,7 @@ config HID_MCP2221 ...@@ -1285,7 +1303,7 @@ config HID_MCP2221
config HID_KUNIT_TEST config HID_KUNIT_TEST
tristate "KUnit tests for HID" if !KUNIT_ALL_TESTS tristate "KUnit tests for HID" if !KUNIT_ALL_TESTS
depends on KUNIT=y depends on KUNIT
depends on HID_BATTERY_STRENGTH depends on HID_BATTERY_STRENGTH
depends on HID_UCLOGIC depends on HID_UCLOGIC
default KUNIT_ALL_TESTS default KUNIT_ALL_TESTS
......
...@@ -87,6 +87,7 @@ obj-$(CONFIG_HID_MULTITOUCH) += hid-multitouch.o ...@@ -87,6 +87,7 @@ obj-$(CONFIG_HID_MULTITOUCH) += hid-multitouch.o
obj-$(CONFIG_HID_NINTENDO) += hid-nintendo.o obj-$(CONFIG_HID_NINTENDO) += hid-nintendo.o
obj-$(CONFIG_HID_NTI) += hid-nti.o obj-$(CONFIG_HID_NTI) += hid-nti.o
obj-$(CONFIG_HID_NTRIG) += hid-ntrig.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_ORTEK) += hid-ortek.o
obj-$(CONFIG_HID_PRODIKEYS) += hid-prodikeys.o obj-$(CONFIG_HID_PRODIKEYS) += hid-prodikeys.o
obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o
......
...@@ -215,7 +215,7 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata) ...@@ -215,7 +215,7 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
struct device *dev; struct device *dev;
u32 feature_report_size; u32 feature_report_size;
u32 input_report_size; u32 input_report_size;
int rc, i, status; int rc, i;
u8 cl_idx; u8 cl_idx;
req_list = &cl_data->req_list; req_list = &cl_data->req_list;
...@@ -286,56 +286,37 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata) ...@@ -286,56 +286,37 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
if (rc) if (rc)
goto cleanup; goto cleanup;
mp2_ops->start(privdata, info); 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); (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->is_any_sensor_enabled = true;
cl_data->sensor_sts[i] = SENSOR_ENABLED; rc = amdtp_hid_probe(i, cl_data);
rc = amdtp_hid_probe(cl_data->cur_hid_dev, cl_data); if (rc)
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]);
goto cleanup; goto cleanup;
}
} else { } else {
cl_data->sensor_sts[i] = SENSOR_DISABLED; 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", 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_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
cl_data->sensor_sts[i]); cl_data->sensor_sts[i]);
} }
if (!cl_data->is_any_sensor_enabled || if (!cl_data->is_any_sensor_enabled ||
(mp2_ops->discovery_status && mp2_ops->discovery_status(privdata) == 0)) { (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); 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)); schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));
return 0; return 0;
cleanup: cleanup:
amd_sfh_hid_client_deinit(privdata);
for (i = 0; i < cl_data->num_hid_devices; i++) { 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, cl_data->feature_report[i]);
devm_kfree(dev, in_data->input_report[i]); devm_kfree(dev, in_data->input_report[i]);
devm_kfree(dev, cl_data->report_descr[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) ...@@ -168,29 +168,17 @@ static int amd_sfh1_1_hid_client_init(struct amd_mp2_dev *privdata)
status = amd_sfh_wait_for_response status = amd_sfh_wait_for_response
(privdata, cl_data->sensor_idx[i], ENABLE_SENSOR); (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->is_any_sensor_enabled = true;
cl_data->sensor_sts[i] = SENSOR_ENABLED;
rc = amdtp_hid_probe(i, cl_data); rc = amdtp_hid_probe(i, cl_data);
if (rc) { 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]);
goto cleanup; goto cleanup;
} }
} else {
cl_data->sensor_sts[i] = SENSOR_DISABLED;
}
dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n", 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_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
cl_data->sensor_sts[i]); cl_data->sensor_sts[i]);
......
...@@ -58,7 +58,7 @@ static unsigned int swap_opt_cmd; ...@@ -58,7 +58,7 @@ static unsigned int swap_opt_cmd;
module_param(swap_opt_cmd, uint, 0644); module_param(swap_opt_cmd, uint, 0644);
MODULE_PARM_DESC(swap_opt_cmd, "Swap the Option (\"Alt\") and Command (\"Flag\") keys. " 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. " "(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; static unsigned int swap_ctrl_cmd;
module_param(swap_ctrl_cmd, uint, 0644); module_param(swap_ctrl_cmd, uint, 0644);
...@@ -319,6 +319,12 @@ static const struct apple_key_translation swapped_option_cmd_keys[] = { ...@@ -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[] = { static const struct apple_key_translation swapped_ctrl_cmd_keys[] = {
{ KEY_LEFTCTRL, KEY_LEFTMETA }, { KEY_LEFTCTRL, KEY_LEFTMETA },
{ KEY_LEFTMETA, KEY_LEFTCTRL }, { KEY_LEFTMETA, KEY_LEFTCTRL },
...@@ -416,6 +422,9 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, ...@@ -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) {
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); trans = apple_find_translation(swapped_option_cmd_keys, code);
if (trans) if (trans)
......
...@@ -884,33 +884,20 @@ static int asus_input_mapping(struct hid_device *hdev, ...@@ -884,33 +884,20 @@ static int asus_input_mapping(struct hid_device *hdev,
case 0xb5: asus_map_key_clear(KEY_CALC); break; case 0xb5: asus_map_key_clear(KEY_CALC); break;
case 0xc4: asus_map_key_clear(KEY_KBDILLUMUP); break; case 0xc4: asus_map_key_clear(KEY_KBDILLUMUP); break;
case 0xc5: asus_map_key_clear(KEY_KBDILLUMDOWN); 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; /* ASUS touchpad toggle */
case 0x6b: asus_map_key_clear(KEY_F21); break; 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: default:
/* ASUS lazily declares 256 usages, ignore the rest, /* ASUS lazily declares 256 usages, ignore the rest,
...@@ -1268,6 +1255,9 @@ static const struct hid_device_id asus_devices[] = { ...@@ -1268,6 +1255,9 @@ static const struct hid_device_id asus_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2), USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD2),
QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, 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, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
USB_DEVICE_ID_ASUSTEK_ROG_CLAYMORE_II_KEYBOARD), USB_DEVICE_ID_ASUSTEK_ROG_CLAYMORE_II_KEYBOARD),
QUIRK_ROG_CLAYMORE_II_KEYBOARD }, QUIRK_ROG_CLAYMORE_II_KEYBOARD },
......
...@@ -2587,48 +2587,41 @@ bool hid_compare_device_paths(struct hid_device *hdev_a, ...@@ -2587,48 +2587,41 @@ bool hid_compare_device_paths(struct hid_device *hdev_a,
} }
EXPORT_SYMBOL_GPL(hid_compare_device_paths); 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); *id = hid_match_device(hdev, hdrv);
struct hid_device *hdev = to_hid_device(dev); if (!*id)
const struct hid_device_id *id; return false;
int ret = 0;
if (down_interruptible(&hdev->driver_input_lock)) {
ret = -EINTR;
goto end;
}
hdev->io_started = 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-generic implements .match(), so we must be dealing with a
* hid_ignore_special_drivers is set, we can safely * different HID driver here, and can simply check if
* return. * hid_ignore_special_drivers is set or not.
*/ */
if (hid_ignore_special_drivers) { return !hid_ignore_special_drivers;
ret = -ENODEV; }
goto unlock;
} 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 */ /* reset the quirks that has been previously set */
hdev->quirks = hid_lookup_quirk(hdev); hdev->quirks = hid_lookup_quirk(hdev);
hdev->driver = hdrv; hdev->driver = hdrv;
if (hdrv->probe) { if (hdrv->probe) {
ret = hdrv->probe(hdev, id); ret = hdrv->probe(hdev, id);
} else { /* default probe */ } else { /* default probe */
...@@ -2636,15 +2629,42 @@ static int hid_device_probe(struct device *dev) ...@@ -2636,15 +2629,42 @@ static int hid_device_probe(struct device *dev)
if (!ret) if (!ret)
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 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) { if (ret) {
devres_release_group(&hdev->dev, hdev->devres_group_id);
hid_close_report(hdev); hid_close_report(hdev);
hdev->driver = NULL; 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) if (!hdev->io_started)
up(&hdev->driver_input_lock); up(&hdev->driver_input_lock);
end:
return ret; return ret;
} }
...@@ -2662,6 +2682,10 @@ static void hid_device_remove(struct device *dev) ...@@ -2662,6 +2682,10 @@ static void hid_device_remove(struct device *dev)
hdrv->remove(hdev); hdrv->remove(hdev);
else /* default remove */ else /* default remove */
hid_hw_stop(hdev); 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); hid_close_report(hdev);
hdev->driver = NULL; hdev->driver = NULL;
} }
......
...@@ -207,6 +207,7 @@ ...@@ -207,6 +207,7 @@
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3 0x1822 #define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3 0x1822
#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_KEYBOARD 0x1866 #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_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_ROG_CLAYMORE_II_KEYBOARD 0x196b
#define USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD 0x1869 #define USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD 0x1869
...@@ -620,6 +621,7 @@ ...@@ -620,6 +621,7 @@
#define USB_DEVICE_ID_UGCI_FIGHTING 0x0030 #define USB_DEVICE_ID_UGCI_FIGHTING 0x0030
#define USB_VENDOR_ID_HP 0x03f0 #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_0A4A 0x0a4a
#define USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0B4A 0x0b4a #define USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0B4A 0x0b4a
#define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE 0x134a #define USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE 0x134a
...@@ -934,7 +936,15 @@ ...@@ -934,7 +936,15 @@
#define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9 #define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9
#define USB_DEVICE_ID_MS_POWER_COVER 0x07da #define USB_DEVICE_ID_MS_POWER_COVER 0x07da
#define USB_DEVICE_ID_MS_SURFACE3_COVER 0x07de #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_MS_PIXART_MOUSE 0x00cb
#define USB_DEVICE_ID_8BITDO_SN30_PRO_PLUS 0x02e0 #define USB_DEVICE_ID_8BITDO_SN30_PRO_PLUS 0x02e0
#define USB_DEVICE_ID_MS_MOUSE_0783 0x0783 #define USB_DEVICE_ID_MS_MOUSE_0783 0x0783
...@@ -1005,6 +1015,9 @@ ...@@ -1005,6 +1015,9 @@
#define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_18 0x0014 #define USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_18 0x0014
#define USB_DEVICE_ID_NTRIG_DUOSENSE 0x1500 #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_VENDOR_ID_ONTRAK 0x0a07
#define USB_DEVICE_ID_ONTRAK_ADU100 0x0064 #define USB_DEVICE_ID_ONTRAK_ADU100 0x0064
......
...@@ -4553,7 +4553,7 @@ static const struct hid_device_id hidpp_devices[] = { ...@@ -4553,7 +4553,7 @@ static const struct hid_device_id hidpp_devices[] = {
{ /* wireless touchpad T651 */ { /* wireless touchpad T651 */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_T651), 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 */ { /* Mouse Logitech Anywhere MX */
LDJ_DEVICE(0x1017), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 }, LDJ_DEVICE(0x1017), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
{ /* Mouse logitech M560 */ { /* Mouse logitech M560 */
...@@ -4608,6 +4608,8 @@ static const struct hid_device_id hidpp_devices[] = { ...@@ -4608,6 +4608,8 @@ static const struct hid_device_id hidpp_devices[] = {
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC086) }, HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC086) },
{ /* Logitech G903 Hero Gaming Mouse over USB */ { /* Logitech G903 Hero Gaming Mouse over USB */
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC091) }, 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 */ { /* Logitech G920 Wheel over USB */
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL), HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL),
.driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS}, .driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS},
...@@ -4630,6 +4632,8 @@ static const struct hid_device_id hidpp_devices[] = { ...@@ -4630,6 +4632,8 @@ static const struct hid_device_id hidpp_devices[] = {
{ /* MX5500 keyboard over Bluetooth */ { /* MX5500 keyboard over Bluetooth */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb30b), HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb30b),
.driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS }, .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 */ { /* M-RCQ142 V470 Cordless Laser Mouse over Bluetooth */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb008) }, HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb008) },
{ /* MX Master mouse over Bluetooth */ { /* MX Master mouse over Bluetooth */
......
...@@ -446,7 +446,16 @@ static const struct hid_device_id ms_devices[] = { ...@@ -446,7 +446,16 @@ static const struct hid_device_id ms_devices[] = {
.driver_data = MS_PRESENTER }, .driver_data = MS_PRESENTER },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, 0x091B), { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, 0x091B),
.driver_data = MS_SURFACE_DIAL }, .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 }, .driver_data = MS_QUIRK_FF },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_8BITDO_SN30_PRO_PLUS), { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_8BITDO_SN30_PRO_PLUS),
.driver_data = MS_QUIRK_FF }, .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[] = { ...@@ -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_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_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_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_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), 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 }, { 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) ...@@ -272,7 +272,12 @@ static int hidraw_open(struct inode *inode, struct file *file)
goto out; 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) { if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
err = -ENODEV; err = -ENODEV;
goto out_unlock; goto out_unlock;
...@@ -301,7 +306,7 @@ static int hidraw_open(struct inode *inode, struct file *file) ...@@ -301,7 +306,7 @@ static int hidraw_open(struct inode *inode, struct file *file)
spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags); spin_unlock_irqrestore(&hidraw_table[minor]->list_lock, flags);
file->private_data = list; file->private_data = list;
out_unlock: out_unlock:
up_read(&minors_rwsem); up_write(&minors_rwsem);
out: out:
if (err < 0) if (err < 0)
kfree(list); kfree(list);
......
...@@ -118,7 +118,7 @@ static struct i2c_driver i2c_hid_acpi_driver = { ...@@ -118,7 +118,7 @@ static struct i2c_driver i2c_hid_acpi_driver = {
.acpi_match_table = i2c_hid_acpi_match, .acpi_match_table = i2c_hid_acpi_match,
}, },
.probe_new = i2c_hid_acpi_probe, .probe = i2c_hid_acpi_probe,
.remove = i2c_hid_core_remove, .remove = i2c_hid_core_remove,
.shutdown = i2c_hid_core_shutdown, .shutdown = i2c_hid_core_shutdown,
}; };
......
...@@ -118,7 +118,7 @@ static struct i2c_driver elan_i2c_hid_ts_driver = { ...@@ -118,7 +118,7 @@ static struct i2c_driver elan_i2c_hid_ts_driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS, .probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = of_match_ptr(elan_i2c_hid_of_match), .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, .remove = i2c_hid_core_remove,
.shutdown = i2c_hid_core_shutdown, .shutdown = i2c_hid_core_shutdown,
}; };
......
...@@ -28,6 +28,7 @@ struct i2c_hid_of_goodix { ...@@ -28,6 +28,7 @@ struct i2c_hid_of_goodix {
struct regulator *vdd; struct regulator *vdd;
struct regulator *vddio; struct regulator *vddio;
struct gpio_desc *reset_gpio; struct gpio_desc *reset_gpio;
bool no_reset_during_suspend;
const struct goodix_i2c_hid_timing_data *timings; const struct goodix_i2c_hid_timing_data *timings;
}; };
...@@ -37,6 +38,14 @@ static int goodix_i2c_hid_power_up(struct i2chid_ops *ops) ...@@ -37,6 +38,14 @@ static int goodix_i2c_hid_power_up(struct i2chid_ops *ops)
container_of(ops, struct i2c_hid_of_goodix, ops); container_of(ops, struct i2c_hid_of_goodix, ops);
int ret; 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); ret = regulator_enable(ihid_goodix->vdd);
if (ret) if (ret)
return ret; return ret;
...@@ -60,7 +69,9 @@ static void goodix_i2c_hid_power_down(struct i2chid_ops *ops) ...@@ -60,7 +69,9 @@ static void goodix_i2c_hid_power_down(struct i2chid_ops *ops)
struct i2c_hid_of_goodix *ihid_goodix = struct i2c_hid_of_goodix *ihid_goodix =
container_of(ops, struct i2c_hid_of_goodix, ops); 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); gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 1);
regulator_disable(ihid_goodix->vddio); regulator_disable(ihid_goodix->vddio);
regulator_disable(ihid_goodix->vdd); regulator_disable(ihid_goodix->vdd);
} }
...@@ -91,6 +102,9 @@ static int i2c_hid_of_goodix_probe(struct i2c_client *client) ...@@ -91,6 +102,9 @@ static int i2c_hid_of_goodix_probe(struct i2c_client *client)
if (IS_ERR(ihid_goodix->vddio)) if (IS_ERR(ihid_goodix->vddio))
return PTR_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); ihid_goodix->timings = device_get_match_data(&client->dev);
return i2c_hid_core_probe(client, &ihid_goodix->ops, 0x0001, 0); return i2c_hid_core_probe(client, &ihid_goodix->ops, 0x0001, 0);
...@@ -114,7 +128,7 @@ static struct i2c_driver goodix_i2c_hid_ts_driver = { ...@@ -114,7 +128,7 @@ static struct i2c_driver goodix_i2c_hid_ts_driver = {
.probe_type = PROBE_PREFER_ASYNCHRONOUS, .probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = of_match_ptr(goodix_i2c_hid_of_match), .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, .remove = i2c_hid_core_remove,
.shutdown = i2c_hid_core_shutdown, .shutdown = i2c_hid_core_shutdown,
}; };
......
...@@ -157,7 +157,7 @@ static struct i2c_driver i2c_hid_of_driver = { ...@@ -157,7 +157,7 @@ static struct i2c_driver i2c_hid_of_driver = {
.of_match_table = of_match_ptr(i2c_hid_of_match), .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, .remove = i2c_hid_core_remove,
.shutdown = i2c_hid_core_shutdown, .shutdown = i2c_hid_core_shutdown,
.id_table = i2c_hid_of_id_table, .id_table = i2c_hid_of_id_table,
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#define ADL_N_DEVICE_ID 0x54FC #define ADL_N_DEVICE_ID 0x54FC
#define RPL_S_DEVICE_ID 0x7A78 #define RPL_S_DEVICE_ID 0x7A78
#define MTL_P_DEVICE_ID 0x7E45 #define MTL_P_DEVICE_ID 0x7E45
#define ARL_H_DEVICE_ID 0x7745
#define REVISION_ID_CHT_A0 0x6 #define REVISION_ID_CHT_A0 0x6
#define REVISION_ID_CHT_Ax_SI 0x0 #define REVISION_ID_CHT_Ax_SI 0x0
......
...@@ -44,6 +44,7 @@ static const struct pci_device_id ish_pci_tbl[] = { ...@@ -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, ADL_N_DEVICE_ID)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, RPL_S_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, MTL_P_DEVICE_ID)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, ARL_H_DEVICE_ID)},
{0, } {0, }
}; };
MODULE_DEVICE_TABLE(pci, ish_pci_tbl); MODULE_DEVICE_TABLE(pci, ish_pci_tbl);
......
...@@ -1314,7 +1314,7 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom) ...@@ -1314,7 +1314,7 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom)
struct input_dev *pen_input = wacom->pen_input; struct input_dev *pen_input = wacom->pen_input;
unsigned char *data = wacom->data; unsigned char *data = wacom->data;
int number_of_valid_frames = 0; int number_of_valid_frames = 0;
int time_interval = 15000000; ktime_t time_interval = 15000000;
ktime_t time_packet_received = ktime_get(); ktime_t time_packet_received = ktime_get();
int i; int i;
...@@ -1348,7 +1348,7 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom) ...@@ -1348,7 +1348,7 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom)
if (number_of_valid_frames) { if (number_of_valid_frames) {
if (wacom->hid_data.time_delayed) if (wacom->hid_data.time_delayed)
time_interval = ktime_get() - 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; wacom->hid_data.time_delayed = time_packet_received;
} }
...@@ -1359,7 +1359,7 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom) ...@@ -1359,7 +1359,7 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom)
bool range = frame[0] & 0x20; bool range = frame[0] & 0x20;
bool invert = frame[0] & 0x10; bool invert = frame[0] & 0x10;
int frames_number_reversed = number_of_valid_frames - i - 1; 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) if (!valid)
continue; continue;
......
...@@ -324,7 +324,7 @@ struct hid_data { ...@@ -324,7 +324,7 @@ struct hid_data {
int ps_connected; int ps_connected;
bool pad_input_event_flag; bool pad_input_event_flag;
unsigned short sequence_number; unsigned short sequence_number;
int time_delayed; ktime_t time_delayed;
}; };
struct wacom_remote_data { struct wacom_remote_data {
......
...@@ -597,6 +597,7 @@ struct hid_device { /* device report descriptor */ ...@@ -597,6 +597,7 @@ struct hid_device { /* device report descriptor */
struct semaphore driver_input_lock; /* protects the current driver */ struct semaphore driver_input_lock; /* protects the current driver */
struct device dev; /* device */ struct device dev; /* device */
struct hid_driver *driver; struct hid_driver *driver;
void *devres_group_id; /* ID of probe devres group */
const struct hid_ll_driver *ll_driver; const struct hid_ll_driver *ll_driver;
struct mutex ll_open_lock; struct mutex ll_open_lock;
......
...@@ -31,6 +31,7 @@ from enum import Enum ...@@ -31,6 +31,7 @@ from enum import Enum
from hidtools.hut import HUT from hidtools.hut import HUT
from hidtools.hid import HidUnit from hidtools.hid import HidUnit
from . import base from . import base
from . import test_multitouch
import libevdev import libevdev
import pytest import pytest
...@@ -517,7 +518,7 @@ class BaseTest: ...@@ -517,7 +518,7 @@ class BaseTest:
for usage in get_report_usages(report): for usage in get_report_usages(report):
yield usage yield usage
def assertName(self, uhdev): def assertName(self, uhdev, type):
""" """
Assert that the name is as we expect. Assert that the name is as we expect.
...@@ -526,7 +527,7 @@ class BaseTest: ...@@ -526,7 +527,7 @@ class BaseTest:
this assertion from the base class to work properly. this assertion from the base class to work properly.
""" """
evdev = uhdev.get_evdev() evdev = uhdev.get_evdev()
expected_name = uhdev.name + " Pen" expected_name = uhdev.name + type
if "wacom" not in expected_name.lower(): if "wacom" not in expected_name.lower():
expected_name = "Wacom " + expected_name expected_name = "Wacom " + expected_name
assert evdev.name == expected_name assert evdev.name == expected_name
...@@ -549,6 +550,12 @@ class BaseTest: ...@@ -549,6 +550,12 @@ class BaseTest:
usage_id("Generic Desktop", "Y"): PhysRange( usage_id("Generic Desktop", "Y"): PhysRange(
PhysRange.CENTIMETER, 5, 150 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", "X Tilt"): PhysRange(PhysRange.DEGREE, 90, 180),
usage_id("Digitizers", "Y 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), usage_id("Digitizers", "Twist"): PhysRange(PhysRange.DEGREE, 358, 360),
...@@ -603,7 +610,17 @@ class BaseTest: ...@@ -603,7 +610,17 @@ class BaseTest:
pass 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): def create_device(self):
return OpaqueTablet() return OpaqueTablet()
...@@ -842,3 +859,64 @@ class TestPTHX60_Pen(TestOpaqueCTLTablet): ...@@ -842,3 +859,64 @@ class TestPTHX60_Pen(TestOpaqueCTLTablet):
libevdev.InputEvent(libevdev.EV_KEY.BTN_0, 0), 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