for-linus-2023062701
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIVAwUAZJtLRKZi849r7WBJAQIMXQ/+KULDCg9ipbPeYSGbEbZLCfuZyqqM77/C zRRBlwk2mAE63j2wNzu70f707W2cfm2njGtbGHhJBETr3lZeP4JTNSBspwnWpjLs naMj82f9wrmEn8lDc5PYicltZVtJWN6liqo/KQ4wIs84WU1TgU89xMhm1iuaGTOu dSXzFHBPiss9CgxUUQJ7gbbe2ZFu/bW0MNWd2a1Fd4g0Uz38yioCDTB4L+zseklA WtA4R8ZZVXzPs3k/VJk7CfyU9gJnBppkEg2ZsNu55R/h2etIdAI/BI/DgQFcCdHw 4F0h32sGbTN8tOqmXq0LyJ9cUOeI2lSTxyE0lt2gRLD/Cdmm/ViPxoyzrhFb+GZO 9d/8GjPjumd5dukMmlw4P5aToD/zbeBk+oZuAYIwYMNLfKGrNRW3NgjRIvN7Lafv XhfMZSQ3Nkjl+ckdZB6XxkczBRbYSMFpReGxAOWowyEzJCfXf50oPTne0uRU6dce lLWmtNXf+5CbfDZClZ/qbR3cwN1yk4DoL1i6znASJom6X6AJ4q6A3/202YJC/0NR Z/DDyaFZZBrMLhIwNTj5bxqfJ+I6CWiuvKKWYVtM0cIwCu+9d1TgIISKOT0r/j6c OqSYiGnUbnwKftXxNpIuKgQ/g6/m47uqOdpkKQyd8aUS/GCriB/31a9ItdI7cU8r 8Y73DkPjPT0= =gUhj -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
Коммит
0e382fa72b
|
@ -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
|
||||
(privdata, cl_data->sensor_idx[i], SENSOR_ENABLED);
|
||||
if (status == SENSOR_ENABLED) {
|
||||
cl_data->sensor_sts[i] = amd_sfh_wait_for_response
|
||||
(privdata, cl_data->sensor_idx[i], 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,28 +168,16 @@ 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]),
|
||||
|
|
|
@ -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,7 +422,10 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input,
|
|||
}
|
||||
|
||||
if (swap_opt_cmd) {
|
||||
trans = apple_find_translation(swapped_option_cmd_keys, code);
|
||||
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)
|
||||
code = trans->to;
|
||||
|
|
|
@ -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 },
|
||||
|
@ -1310,4 +1300,4 @@ static struct hid_driver asus_driver = {
|
|||
};
|
||||
module_hid_driver(asus_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -2587,64 +2587,84 @@ bool hid_compare_device_paths(struct hid_device *hdev_a,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(hid_compare_device_paths);
|
||||
|
||||
static bool hid_check_device_match(struct hid_device *hdev,
|
||||
struct hid_driver *hdrv,
|
||||
const struct hid_device_id **id)
|
||||
{
|
||||
*id = hid_match_device(hdev, hdrv);
|
||||
if (!*id)
|
||||
return false;
|
||||
|
||||
if (hdrv->match)
|
||||
return hdrv->match(hdev, hid_ignore_special_drivers);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
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 */
|
||||
ret = hid_open_report(hdev);
|
||||
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;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hid_device_probe(struct device *dev)
|
||||
{
|
||||
struct hid_driver *hdrv = to_hid_driver(dev->driver);
|
||||
struct hid_device *hdev = to_hid_device(dev);
|
||||
const struct hid_device_id *id;
|
||||
struct hid_driver *hdrv = to_hid_driver(dev->driver);
|
||||
int ret = 0;
|
||||
|
||||
if (down_interruptible(&hdev->driver_input_lock)) {
|
||||
ret = -EINTR;
|
||||
goto end;
|
||||
}
|
||||
hdev->io_started = false;
|
||||
if (down_interruptible(&hdev->driver_input_lock))
|
||||
return -EINTR;
|
||||
|
||||
hdev->io_started = false;
|
||||
clear_bit(ffs(HID_STAT_REPROBED), &hdev->status);
|
||||
|
||||
if (!hdev->driver) {
|
||||
id = hid_match_device(hdev, hdrv);
|
||||
if (id == NULL) {
|
||||
ret = -ENODEV;
|
||||
goto unlock;
|
||||
}
|
||||
if (!hdev->driver)
|
||||
ret = __hid_device_probe(hdev, hdrv);
|
||||
|
||||
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.
|
||||
*/
|
||||
if (hid_ignore_special_drivers) {
|
||||
ret = -ENODEV;
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
ret = hid_open_report(hdev);
|
||||
if (!ret)
|
||||
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
||||
}
|
||||
if (ret) {
|
||||
hid_close_report(hdev);
|
||||
hdev->driver = NULL;
|
||||
}
|
||||
}
|
||||
unlock:
|
||||
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 },
|
||||
|
|
|
@ -0,0 +1,738 @@
|
|||
// 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);
|
||||
|
||||
gpiod_set_value_cansleep(ihid_goodix->reset_gpio, 1);
|
||||
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
|
Загрузка…
Ссылка в новой задаче