diff options
author | Linus Torvalds | 2023-06-29 10:24:40 -0700 |
---|---|---|
committer | Linus Torvalds | 2023-06-29 10:24:40 -0700 |
commit | 0e382fa72bbf0610be40af9af9b03b0cd149df82 (patch) | |
tree | c5469baa5c37252461bf47efccce3812db0e702f | |
parent | 675285ad819293844018aa8096ba9a6d7c77b90b (diff) | |
parent | b571809ec3b0af1a93d46bb09fece4ddeafe5a97 (diff) |
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
...
25 files changed, 1035 insertions, 144 deletions
diff --git a/Documentation/devicetree/bindings/input/goodix,gt7375p.yaml b/Documentation/devicetree/bindings/input/goodix,gt7375p.yaml index ce18d7dadae2..1edad1da1196 100644 --- a/Documentation/devicetree/bindings/input/goodix,gt7375p.yaml +++ b/Documentation/devicetree/bindings/input/goodix,gt7375p.yaml @@ -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 diff --git a/MAINTAINERS b/MAINTAINERS index 5e793e8f9157..7b1fab537be7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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 diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 4ce012f83253..e11c1c803676 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -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 diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 5d37cacbde33..7a9e160158f7 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -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 diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_client.c b/drivers/hid/amd-sfh-hid/amd_sfh_client.c index d9b7b01900b5..bdb578e0899f 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_client.c +++ b/drivers/hid/amd-sfh-hid/amd_sfh_client.c @@ -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]); diff --git a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.c b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.c index bb8bd7892b67..e9c6413af24a 100644 --- a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.c +++ b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.c @@ -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]), diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index cc535d2d6e8c..d7b932925730 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -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; diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index 01a27579ec02..fd61dba88233 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -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, @@ -1269,6 +1256,9 @@ static const struct hid_device_id asus_devices[] = { 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 }, { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, @@ -1310,4 +1300,4 @@ static struct hid_driver asus_driver = { }; module_hid_driver(asus_driver); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL");
\ No newline at end of file diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 22623eb4f72f..8992e3c1e769 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -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 (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; - } - } + if (!hdev->driver) + ret = __hid_device_probe(hdev, hdrv); - /* 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; } diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 5d29abac2300..8a310f8ff20f 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -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 diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 5e1a412fd28f..dfe8e09a18de 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -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 */ diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index 071fd093a5f4..9345e2bfd56e 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -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 }, diff --git a/drivers/hid/hid-nvidia-shield.c b/drivers/hid/hid-nvidia-shield.c new file mode 100644 index 000000000000..85700cec5eac --- /dev/null +++ b/drivers/hid/hid-nvidia-shield.c @@ -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"); diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index 804fc03600cc..3983b4f282f8 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -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 }, diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 93e62b161501..e63c56a0d57f 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -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); diff --git a/drivers/hid/i2c-hid/i2c-hid-acpi.c b/drivers/hid/i2c-hid/i2c-hid-acpi.c index 3a7e2bcb5412..ac918a9ea8d3 100644 --- a/drivers/hid/i2c-hid/i2c-hid-acpi.c +++ b/drivers/hid/i2c-hid/i2c-hid-acpi.c @@ -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, }; diff --git a/drivers/hid/i2c-hid/i2c-hid-of-elan.c b/drivers/hid/i2c-hid/i2c-hid-of-elan.c index 76ddc8be1cbb..029045d9661c 100644 --- a/drivers/hid/i2c-hid/i2c-hid-of-elan.c +++ b/drivers/hid/i2c-hid/i2c-hid-of-elan.c @@ -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, }; diff --git a/drivers/hid/i2c-hid/i2c-hid-of-goodix.c b/drivers/hid/i2c-hid/i2c-hid-of-goodix.c index 0060e3dcd775..f1597ad67e7c 100644 --- a/drivers/hid/i2c-hid/i2c-hid-of-goodix.c +++ b/drivers/hid/i2c-hid/i2c-hid-of-goodix.c @@ -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, }; diff --git a/drivers/hid/i2c-hid/i2c-hid-of.c b/drivers/hid/i2c-hid/i2c-hid-of.c index 855f53092f4e..c4e1fa0273c8 100644 --- a/drivers/hid/i2c-hid/i2c-hid-of.c +++ b/drivers/hid/i2c-hid/i2c-hid-of.c @@ -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, diff --git a/drivers/hid/intel-ish-hid/ipc/hw-ish.h b/drivers/hid/intel-ish-hid/ipc/hw-ish.h index fc108f19a64c..e99f3a3c65e1 100644 --- a/drivers/hid/intel-ish-hid/ipc/hw-ish.h +++ b/drivers/hid/intel-ish-hid/ipc/hw-ish.h @@ -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 diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c b/drivers/hid/intel-ish-hid/ipc/pci-ish.c index 7120b30ac51d..55cb25038e63 100644 --- a/drivers/hid/intel-ish-hid/ipc/pci-ish.c +++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c @@ -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); diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 2ccf83837134..174bf03908d7 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -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; diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index 1a40bb8c5810..ee21bb260f22 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -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 { diff --git a/include/linux/hid.h b/include/linux/hid.h index 4e4c4fe36911..39e21e3815ad 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -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; diff --git a/tools/testing/selftests/hid/tests/test_wacom_generic.py b/tools/testing/selftests/hid/tests/test_wacom_generic.py index b1eb2bc787fc..f92fe8e02c1b 100644 --- a/tools/testing/selftests/hid/tests/test_wacom_generic.py +++ b/tools/testing/selftests/hid/tests/test_wacom_generic.py @@ -31,6 +31,7 @@ from enum import Enum from hidtools.hut import HUT from hidtools.hid import HidUnit from . import base +from . import test_multitouch import libevdev import pytest @@ -517,7 +518,7 @@ class BaseTest: for usage in get_report_usages(report): yield usage - def assertName(self, uhdev): + def assertName(self, uhdev, type): """ Assert that the name is as we expect. @@ -526,7 +527,7 @@ class BaseTest: this assertion from the base class to work properly. """ evdev = uhdev.get_evdev() - expected_name = uhdev.name + " Pen" + expected_name = uhdev.name + type if "wacom" not in expected_name.lower(): expected_name = "Wacom " + expected_name assert evdev.name == expected_name @@ -549,6 +550,12 @@ class BaseTest: usage_id("Generic Desktop", "Y"): PhysRange( PhysRange.CENTIMETER, 5, 150 ), + usage_id("Digitizers", "Width"): PhysRange( + PhysRange.CENTIMETER, 5, 150 + ), + usage_id("Digitizers", "Height"): PhysRange( + PhysRange.CENTIMETER, 5, 150 + ), usage_id("Digitizers", "X Tilt"): PhysRange(PhysRange.DEGREE, 90, 180), usage_id("Digitizers", "Y Tilt"): PhysRange(PhysRange.DEGREE, 90, 180), usage_id("Digitizers", "Twist"): PhysRange(PhysRange.DEGREE, 358, 360), @@ -603,7 +610,17 @@ class BaseTest: pass -class TestOpaqueTablet(BaseTest.TestTablet): +class PenTabletTest(BaseTest.TestTablet): + def assertName(self, uhdev): + super().assertName(uhdev, " Pen") + + +class TouchTabletTest(BaseTest.TestTablet): + def assertName(self, uhdev): + super().assertName(uhdev, " Finger") + + +class TestOpaqueTablet(PenTabletTest): def create_device(self): return OpaqueTablet() @@ -842,3 +859,64 @@ class TestPTHX60_Pen(TestOpaqueCTLTablet): libevdev.InputEvent(libevdev.EV_KEY.BTN_0, 0), ], ) + + +class TestDTH2452Tablet(test_multitouch.BaseTest.TestMultitouch, TouchTabletTest): + def create_device(self): + return test_multitouch.Digitizer( + "DTH 2452", + rdesc="05 0d 09 04 a1 01 85 0c 95 01 75 08 15 00 26 ff 00 81 03 09 54 81 02 09 22 a1 02 05 0d 95 01 75 01 25 01 09 42 81 02 81 03 09 47 81 02 95 05 81 03 09 51 26 ff 00 75 10 95 01 81 02 35 00 65 11 55 0e 05 01 09 30 26 a0 44 46 96 14 81 42 09 31 26 9a 26 46 95 0b 81 42 05 0d 75 08 95 01 15 00 09 48 26 5f 00 46 7c 14 81 02 09 49 25 35 46 7d 0b 81 02 45 00 65 00 55 00 c0 05 0d 09 22 a1 02 05 0d 95 01 75 01 25 01 09 42 81 02 81 03 09 47 81 02 95 05 81 03 09 51 26 ff 00 75 10 95 01 81 02 35 00 65 11 55 0e 05 01 09 30 26 a0 44 46 96 14 81 42 09 31 26 9a 26 46 95 0b 81 42 05 0d 75 08 95 01 15 00 09 48 26 5f 00 46 7c 14 81 02 09 49 25 35 46 7d 0b 81 02 45 00 65 00 55 00 c0 05 0d 09 22 a1 02 05 0d 95 01 75 01 25 01 09 42 81 02 81 03 09 47 81 02 95 05 81 03 09 51 26 ff 00 75 10 95 01 81 02 35 00 65 11 55 0e 05 01 09 30 26 a0 44 46 96 14 81 42 09 31 26 9a 26 46 95 0b 81 42 05 0d 75 08 95 01 15 00 09 48 26 5f 00 46 7c 14 81 02 09 49 25 35 46 7d 0b 81 02 45 00 65 00 55 00 c0 05 0d 09 22 a1 02 05 0d 95 01 75 01 25 01 09 42 81 02 81 03 09 47 81 02 95 05 81 03 09 51 26 ff 00 75 10 95 01 81 02 35 00 65 11 55 0e 05 01 09 30 26 a0 44 46 96 14 81 42 09 31 26 9a 26 46 95 0b 81 42 05 0d 75 08 95 01 15 00 09 48 26 5f 00 46 7c 14 81 02 09 49 25 35 46 7d 0b 81 02 45 00 65 00 55 00 c0 05 0d 09 22 a1 02 05 0d 95 01 75 01 25 01 09 42 81 02 81 03 09 47 81 02 95 05 81 03 09 51 26 ff 00 75 10 95 01 81 02 35 00 65 11 55 0e 05 01 09 30 26 a0 44 46 96 14 81 42 09 31 26 9a 26 46 95 0b 81 42 05 0d 75 08 95 01 15 00 09 48 26 5f 00 46 7c 14 81 02 09 49 25 35 46 7d 0b 81 02 45 00 65 00 55 00 c0 05 0d 27 ff ff 00 00 75 10 95 01 09 56 81 02 75 08 95 0e 81 03 09 55 26 ff 00 75 08 b1 02 85 0a 06 00 ff 09 c5 96 00 01 b1 02 c0 06 00 ff 09 01 a1 01 09 01 85 13 15 00 26 ff 00 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0", + input_info=(0x3, 0x056A, 0x0383), + ) + + def test_contact_id_0(self): + """ + Bring a finger in contact with the tablet, then hold it down and remove it. + + Ensure that even with contact ID = 0 which is usually given as an invalid + touch event by most tablets with the exception of a few, that given the + confidence bit is set to 1 it should process it as a valid touch to cover + the few tablets using contact ID = 0 as a valid touch value. + """ + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + t0 = test_multitouch.Touch(0, 50, 100) + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + slot = self.get_slot(uhdev, t0, 0) + + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 1) in events + assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == 0 + assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_POSITION_X] == 50 + assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_POSITION_Y] == 100 + + t0.tipswitch = False + if uhdev.quirks is None or "VALID_IS_INRANGE" not in uhdev.quirks: + t0.inrange = False + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + assert libevdev.InputEvent(libevdev.EV_KEY.BTN_TOUCH, 0) in events + assert evdev.slots[slot][libevdev.EV_ABS.ABS_MT_TRACKING_ID] == -1 + + def test_confidence_false(self): + """ + Bring a finger in contact with the tablet with confidence set to false. + + Ensure that the confidence bit being set to false should not result in a touch event. + """ + uhdev = self.uhdev + evdev = uhdev.get_evdev() + + t0 = test_multitouch.Touch(1, 50, 100) + t0.confidence = False + r = uhdev.event([t0]) + events = uhdev.next_sync_events() + self.debug_reports(r, uhdev, events) + + slot = self.get_slot(uhdev, t0, 0) + + assert not events
\ No newline at end of file |