diff options
Diffstat (limited to 'drivers/hid')
216 files changed, 22758 insertions, 2884 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 4c682c650704..dfc245867a46 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -213,13 +213,16 @@ config HID_CHICONY config HID_CORSAIR tristate "Corsair devices" depends on USB_HID && LEDS_CLASS + select POWER_SUPPLY help Support for Corsair devices that are not fully compliant with the HID standard. + Support for Corsair Void headsets. Supported devices: - Vengeance K90 - Scimitar PRO RGB + - Corsair Void headsets config HID_COUGAR tristate "Cougar devices" @@ -404,6 +407,12 @@ config HID_VIVALDI_COMMON option so that drivers can use common code to parse the HID descriptors for vivaldi function row keymap. +config HID_GOODIX_SPI + tristate "Goodix GT7986U SPI HID touchscreen" + depends on SPI_MASTER + help + Support for Goodix GT7986U SPI HID touchscreen device. + config HID_GOOGLE_HAMMER tristate "Google Hammer Keyboard" select HID_VIVALDI_COMMON @@ -459,6 +468,15 @@ config HID_KYE - MousePen i608X tablet - EasyPen M610X tablet +config HID_KYSONA + tristate "Kysona devices" + depends on USB_HID + help + Support for Kysona mice. + + Say Y here if you have a Kysona M600 mouse + and want to be able to read its battery capacity. + config HID_UCLOGIC tristate "UC-Logic" depends on USB_HID @@ -552,6 +570,8 @@ config HID_LED config HID_LENOVO tristate "Lenovo / Thinkpad devices" + depends on ACPI + select ACPI_PLATFORM_PROFILE select NEW_LEDS select LEDS_CLASS help @@ -769,7 +789,7 @@ config HID_NINTENDO Adds support for the Nintendo Switch Joy-Cons, NSO, Pro Controller. All controllers support bluetooth, and the Pro Controller also supports its USB mode. This also includes support for the Nintendo Switch Online - Controllers which include the Genesis, SNES, and N64 controllers. + Controllers which include the NES, Genesis, SNES, and N64 controllers. To compile this driver as a module, choose M here: the module will be called hid-nintendo. @@ -1090,6 +1110,7 @@ config HID_RMI select RMI4_F11 select RMI4_F12 select RMI4_F30 + select RMI4_F3A help Support for Synaptics RMI4 touchpads. Say Y here if you have a Synaptics RMI4 touchpads over i2c-hid or usbhid @@ -1148,7 +1169,8 @@ config HID_TOPRE tristate "Topre REALFORCE keyboards" depends on HID help - Say Y for N-key rollover support on Topre REALFORCE R2 108/87 key keyboards. + Say Y for N-key rollover support on Topre REALFORCE R2 108/87 key and + Topre REALFORCE R3S 87 key keyboards. config HID_THINGM tristate "ThingM blink(1) USB RGB LED" @@ -1236,6 +1258,22 @@ config HID_WIIMOTE To compile this driver as a module, choose M here: the module will be called hid-wiimote. +config HID_WINWING + tristate "WinWing Orion2 throttle support" + depends on USB_HID + depends on NEW_LEDS + depends on LEDS_CLASS + help + Support for WinWing Orion2 throttle base with the following grips: + + * TGRIP-16EX + * TGRIP-18 + + This driver enables all buttons and switches on the throttle base. + + To compile this driver as a module, choose M here: the + module will be called hid-winwing. + config HID_XINMO tristate "Xin-Mo non-fully compliant devices" help @@ -1339,10 +1377,6 @@ endmenu source "drivers/hid/bpf/Kconfig" -endif # HID - -source "drivers/hid/usbhid/Kconfig" - source "drivers/hid/i2c-hid/Kconfig" source "drivers/hid/intel-ish-hid/Kconfig" @@ -1351,4 +1385,12 @@ source "drivers/hid/amd-sfh-hid/Kconfig" source "drivers/hid/surface-hid/Kconfig" +source "drivers/hid/intel-thc-hid/Kconfig" + +endif # HID + +# USB support may be used with HID disabled + +source "drivers/hid/usbhid/Kconfig" + endif # HID_SUPPORT diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 082a728eac60..482b096eea28 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -38,7 +38,7 @@ obj-$(CONFIG_HID_BIGBEN_FF) += hid-bigbenff.o obj-$(CONFIG_HID_CHERRY) += hid-cherry.o obj-$(CONFIG_HID_CHICONY) += hid-chicony.o obj-$(CONFIG_HID_CMEDIA) += hid-cmedia.o -obj-$(CONFIG_HID_CORSAIR) += hid-corsair.o +obj-$(CONFIG_HID_CORSAIR) += hid-corsair.o hid-corsair-void.o obj-$(CONFIG_HID_COUGAR) += hid-cougar.o obj-$(CONFIG_HID_CP2112) += hid-cp2112.o obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o @@ -54,6 +54,7 @@ obj-$(CONFIG_HID_GEMBIRD) += hid-gembird.o obj-$(CONFIG_HID_GFRM) += hid-gfrm.o obj-$(CONFIG_HID_GLORIOUS) += hid-glorious.o obj-$(CONFIG_HID_VIVALDI_COMMON) += hid-vivaldi-common.o +obj-$(CONFIG_HID_GOODIX_SPI) += hid-goodix-spi.o obj-$(CONFIG_HID_GOOGLE_HAMMER) += hid-google-hammer.o obj-$(CONFIG_HID_GOOGLE_STADIA_FF) += hid-google-stadiaff.o obj-$(CONFIG_HID_VIVALDI) += hid-vivaldi.o @@ -69,6 +70,7 @@ obj-$(CONFIG_HID_JABRA) += hid-jabra.o obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o obj-$(CONFIG_HID_KEYTOUCH) += hid-keytouch.o obj-$(CONFIG_HID_KYE) += hid-kye.o +obj-$(CONFIG_HID_KYSONA) += hid-kysona.o obj-$(CONFIG_HID_LCPOWER) += hid-lcpower.o obj-$(CONFIG_HID_LENOVO) += hid-lenovo.o obj-$(CONFIG_HID_LETSKETCH) += hid-letsketch.o @@ -150,13 +152,12 @@ wacom-objs := wacom_wac.o wacom_sys.o obj-$(CONFIG_HID_WACOM) += wacom.o obj-$(CONFIG_HID_WALTOP) += hid-waltop.o obj-$(CONFIG_HID_WIIMOTE) += hid-wiimote.o +obj-$(CONFIG_HID_WINWING) += hid-winwing.o obj-$(CONFIG_HID_SENSOR_HUB) += hid-sensor-hub.o obj-$(CONFIG_HID_SENSOR_CUSTOM_SENSOR) += hid-sensor-custom.o -hid-uclogic-test-objs := hid-uclogic-rdesc.o \ - hid-uclogic-params.o \ - hid-uclogic-rdesc-test.o -obj-$(CONFIG_HID_KUNIT_TEST) += hid-uclogic-test.o +hid-uclogic-test-objs := hid-uclogic-rdesc-test.o +obj-$(CONFIG_HID_KUNIT_TEST) += hid-uclogic.o hid-uclogic-test.o obj-$(CONFIG_USB_HID) += usbhid/ obj-$(CONFIG_USB_MOUSE) += usbhid/ @@ -170,3 +171,5 @@ obj-$(INTEL_ISH_FIRMWARE_DOWNLOADER) += intel-ish-hid/ obj-$(CONFIG_AMD_SFH_HID) += amd-sfh-hid/ obj-$(CONFIG_SURFACE_HID_CORE) += surface-hid/ + +obj-$(CONFIG_INTEL_THC_HID) += intel-thc-hid/ diff --git a/drivers/hid/amd-sfh-hid/Kconfig b/drivers/hid/amd-sfh-hid/Kconfig index 329de5e12c1a..3291786a5ee6 100644 --- a/drivers/hid/amd-sfh-hid/Kconfig +++ b/drivers/hid/amd-sfh-hid/Kconfig @@ -5,7 +5,6 @@ menu "AMD SFH HID Support" config AMD_SFH_HID tristate "AMD Sensor Fusion Hub" - depends on HID depends on X86 help If you say yes to this option, support will be included for the diff --git a/drivers/hid/amd-sfh-hid/Makefile b/drivers/hid/amd-sfh-hid/Makefile index 0222170ab7ad..106514b54d16 100644 --- a/drivers/hid/amd-sfh-hid/Makefile +++ b/drivers/hid/amd-sfh-hid/Makefile @@ -13,4 +13,4 @@ amd_sfh-objs += sfh1_1/amd_sfh_init.o amd_sfh-objs += sfh1_1/amd_sfh_interface.o amd_sfh-objs += sfh1_1/amd_sfh_desc.o -ccflags-y += -I $(srctree)/$(src)/ +ccflags-y += -I $(src)/ diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_client.c b/drivers/hid/amd-sfh-hid/amd_sfh_client.c index bdb578e0899f..3438d392920f 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_client.c +++ b/drivers/hid/amd-sfh-hid/amd_sfh_client.c @@ -236,9 +236,9 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata) cl_data->in_data = in_data; for (i = 0; i < cl_data->num_hid_devices; i++) { - in_data->sensor_virt_addr[i] = dma_alloc_coherent(dev, sizeof(int) * 8, - &cl_data->sensor_dma_addr[i], - GFP_KERNEL); + in_data->sensor_virt_addr[i] = dmam_alloc_coherent(dev, sizeof(int) * 8, + &cl_data->sensor_dma_addr[i], + GFP_KERNEL); if (!in_data->sensor_virt_addr[i]) { rc = -ENOMEM; goto cleanup; @@ -288,12 +288,22 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata) mp2_ops->start(privdata, info); cl_data->sensor_sts[i] = amd_sfh_wait_for_response (privdata, cl_data->sensor_idx[i], SENSOR_ENABLED); + + if (cl_data->sensor_sts[i] == SENSOR_ENABLED) + cl_data->is_any_sensor_enabled = true; + } + + if (!cl_data->is_any_sensor_enabled || + (mp2_ops->discovery_status && mp2_ops->discovery_status(privdata) == 0)) { + dev_warn(dev, "Failed to discover, sensors not enabled is %d\n", + cl_data->is_any_sensor_enabled); + rc = -EOPNOTSUPP; + goto cleanup; } 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; rc = amdtp_hid_probe(i, cl_data); if (rc) goto cleanup; @@ -305,12 +315,6 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata) cl_data->sensor_sts[i]); } - if (!cl_data->is_any_sensor_enabled || - (mp2_ops->discovery_status && mp2_ops->discovery_status(privdata) == 0)) { - dev_warn(dev, "Failed to discover, sensors not enabled is %d\n", cl_data->is_any_sensor_enabled); - rc = -EOPNOTSUPP; - goto cleanup; - } schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP)); return 0; @@ -327,7 +331,6 @@ cleanup: int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata) { struct amdtp_cl_data *cl_data = privdata->cl_data; - struct amd_input_data *in_data = cl_data->in_data; int i, status; for (i = 0; i < cl_data->num_hid_devices; i++) { @@ -347,12 +350,5 @@ int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata) cancel_delayed_work_sync(&cl_data->work_buffer); amdtp_hid_remove(cl_data); - 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]); - } - } return 0; } diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_common.h b/drivers/hid/amd-sfh-hid/amd_sfh_common.h index e5620d7db569..799b8686a88a 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_common.h +++ b/drivers/hid/amd-sfh-hid/amd_sfh_common.h @@ -43,6 +43,7 @@ struct amd_mp2_sensor_info { struct sfh_dev_status { bool is_hpd_present; bool is_als_present; + bool is_sra_present; }; struct amd_mp2_dev { diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_hid.c b/drivers/hid/amd-sfh-hid/amd_sfh_hid.c index 705b52337068..81f3024b7b1b 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_hid.c +++ b/drivers/hid/amd-sfh-hid/amd_sfh_hid.c @@ -171,11 +171,13 @@ err_hid_data: void amdtp_hid_remove(struct amdtp_cl_data *cli_data) { int i; + struct amdtp_hid_data *hid_data; for (i = 0; i < cli_data->num_hid_devices; ++i) { if (cli_data->hid_sensor_hubs[i]) { - kfree(cli_data->hid_sensor_hubs[i]->driver_data); + hid_data = cli_data->hid_sensor_hubs[i]->driver_data; hid_destroy_device(cli_data->hid_sensor_hubs[i]); + kfree(hid_data); cli_data->hid_sensor_hubs[i] = NULL; } } diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_hid.h b/drivers/hid/amd-sfh-hid/amd_sfh_hid.h index 97296f587bc7..1c91be8daedd 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_hid.h +++ b/drivers/hid/amd-sfh-hid/amd_sfh_hid.h @@ -73,8 +73,6 @@ struct amdtp_hid_data { }; /* Interface functions between HID LL driver and AMD SFH client */ -void hid_amdtp_set_feature(struct hid_device *hid, char *buf, u32 len, int report_id); -void hid_amdtp_get_report(struct hid_device *hid, int report_id, int report_type); int amdtp_hid_probe(u32 cur_hid_dev, struct amdtp_cl_data *cli_data); void amdtp_hid_remove(struct amdtp_cl_data *cli_data); int amd_sfh_get_report(struct hid_device *hid, int report_id, int report_type); diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c index 9e97c26c4482..48cfd0c58241 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c +++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c @@ -122,7 +122,7 @@ int amd_sfh_irq_init_v2(struct amd_mp2_dev *privdata) { int rc; - pci_intx(privdata->pdev, true); + pcim_intx(privdata->pdev, true); rc = devm_request_irq(&privdata->pdev->dev, privdata->pdev->irq, amd_sfh_irq_handler, 0, DRIVER_NAME, privdata); @@ -248,7 +248,7 @@ static void amd_mp2_pci_remove(void *privdata) struct amd_mp2_dev *mp2 = privdata; amd_sfh_hid_client_deinit(privdata); mp2->mp2_ops->stop_all(mp2); - pci_intx(mp2->pdev, false); + pcim_intx(mp2->pdev, false); amd_sfh_clear_intr(mp2); } @@ -333,14 +333,11 @@ static const struct dmi_system_id dmi_nodevs[] = { static void sfh1_1_init_work(struct work_struct *work) { struct amd_mp2_dev *mp2 = container_of(work, struct amd_mp2_dev, work); - struct pci_dev *pdev = mp2->pdev; int rc; rc = mp2->sfh1_1_ops->init(mp2); - if (rc) { - dev_err(&pdev->dev, "sfh1_1_init failed err %d\n", rc); + if (rc) return; - } amd_sfh_clear_intr(mp2); mp2->init_done = 1; 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 5b24d5f63701..e9929c4aa72e 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 @@ -30,6 +30,7 @@ static int amd_sfh_get_sensor_num(struct amd_mp2_dev *mp2, u8 *sensor_id) case ACCEL_IDX: case GYRO_IDX: case MAG_IDX: + case SRA_IDX: case ALS_IDX: case HPD_IDX: if (BIT(i) & slist->sl.sensors) @@ -58,6 +59,8 @@ static const char *get_sensor_name(int idx) return "gyroscope"; case MAG_IDX: return "magnetometer"; + case SRA_IDX: + return "SRA"; case ALS_IDX: return "ALS"; case HPD_IDX: @@ -130,6 +133,23 @@ static int amd_sfh1_1_hid_client_init(struct amd_mp2_dev *privdata) for (i = 0; i < cl_data->num_hid_devices; i++) { cl_data->sensor_sts[i] = SENSOR_DISABLED; + + if (cl_data->num_hid_devices == 1 && cl_data->sensor_idx[0] == SRA_IDX) + break; + + if (cl_data->sensor_idx[i] == SRA_IDX) { + info.sensor_idx = cl_data->sensor_idx[i]; + writel(0, privdata->mmio + amd_get_p2c_val(privdata, 0)); + mp2_ops->start(privdata, info); + status = amd_sfh_wait_for_response + (privdata, cl_data->sensor_idx[i], ENABLE_SENSOR); + + cl_data->sensor_sts[i] = (status == 0) ? SENSOR_ENABLED : SENSOR_DISABLED; + if (cl_data->sensor_sts[i] == SENSOR_ENABLED) + privdata->dev_en.is_sra_present = true; + continue; + } + cl_data->sensor_requested_cnt[i] = 0; cl_data->cur_hid_dev = i; cl_idx = cl_data->sensor_idx[i]; @@ -181,6 +201,8 @@ static int amd_sfh1_1_hid_client_init(struct amd_mp2_dev *privdata) } for (i = 0; i < cl_data->num_hid_devices; i++) { + if (cl_data->sensor_idx[i] == SRA_IDX) + continue; cl_data->cur_hid_dev = i; if (cl_data->sensor_sts[i] == SENSOR_ENABLED) { cl_data->is_any_sensor_enabled = true; @@ -202,7 +224,7 @@ static int amd_sfh1_1_hid_client_init(struct amd_mp2_dev *privdata) } if (!cl_data->is_any_sensor_enabled) { - dev_warn(dev, "Failed to discover, sensors not enabled is %d\n", + dev_warn(dev, "No sensor registered, sensors not enabled is %d\n", cl_data->is_any_sensor_enabled); rc = -EOPNOTSUPP; goto cleanup; @@ -227,6 +249,11 @@ static void amd_sfh_resume(struct amd_mp2_dev *mp2) struct amd_mp2_sensor_info info; int i, status; + if (!cl_data->is_any_sensor_enabled) { + amd_sfh_clear_intr(mp2); + return; + } + for (i = 0; i < cl_data->num_hid_devices; i++) { if (cl_data->sensor_sts[i] == SENSOR_DISABLED) { info.sensor_idx = cl_data->sensor_idx[i]; @@ -252,6 +279,11 @@ static void amd_sfh_suspend(struct amd_mp2_dev *mp2) struct amdtp_cl_data *cl_data = mp2->cl_data; int i, status; + if (!cl_data->is_any_sensor_enabled) { + amd_sfh_clear_intr(mp2); + return; + } + for (i = 0; i < cl_data->num_hid_devices; i++) { if (cl_data->sensor_idx[i] != HPD_IDX && cl_data->sensor_sts[i] == SENSOR_ENABLED) { @@ -279,7 +311,7 @@ static void amd_mp2_pci_remove(void *privdata) sfh_deinit_emp2(); amd_sfh_hid_client_deinit(privdata); mp2->mp2_ops->stop_all(mp2); - pci_intx(mp2->pdev, false); + pcim_intx(mp2->pdev, false); amd_sfh_clear_intr(mp2); } @@ -289,8 +321,8 @@ static void amd_sfh_set_ops(struct amd_mp2_dev *mp2) sfh_interface_init(mp2); mp2_ops = mp2->mp2_ops; - mp2_ops->clear_intr = amd_sfh_clear_intr_v2, - mp2_ops->init_intr = amd_sfh_irq_init_v2, + mp2_ops->clear_intr = amd_sfh_clear_intr_v2; + mp2_ops->init_intr = amd_sfh_irq_init_v2; mp2_ops->suspend = amd_sfh_suspend; mp2_ops->resume = amd_sfh_resume; mp2_ops->remove = amd_mp2_pci_remove; @@ -320,7 +352,7 @@ int amd_sfh1_1_init(struct amd_mp2_dev *mp2) memcpy_fromio(&binfo, mp2->vsbase, sizeof(struct sfh_base_info)); if (binfo.sbase.fw_info.fw_ver == 0 || binfo.sbase.s_list.sl.sensors == 0) { - dev_dbg(dev, "failed to get sensors\n"); + dev_dbg(dev, "No sensor registered\n"); return -EOPNOTSUPP; } dev_dbg(dev, "firmware version 0x%x\n", binfo.sbase.fw_info.fw_ver); @@ -337,7 +369,8 @@ int amd_sfh1_1_init(struct amd_mp2_dev *mp2) rc = amd_sfh1_1_hid_client_init(mp2); if (rc) { sfh_deinit_emp2(); - dev_err(dev, "amd_sfh1_1_hid_client_init failed\n"); + if ((rc != -ENODEV) && (rc != -EOPNOTSUPP)) + dev_err(dev, "amd_sfh1_1_hid_client_init failed\n"); return rc; } diff --git a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.c b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.c index 2de2668a0277..ffb98b4c36cb 100644 --- a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.c +++ b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.c @@ -87,6 +87,41 @@ void sfh_interface_init(struct amd_mp2_dev *mp2) emp2 = mp2; } +static int amd_sfh_mode_info(u32 *platform_type, u32 *laptop_placement) +{ + struct sfh_op_mode mode; + + if (!platform_type || !laptop_placement) + return -EINVAL; + + if (!emp2 || !emp2->dev_en.is_sra_present) + return -ENODEV; + + mode.val = readl(emp2->mmio + amd_get_c2p_val(emp2, 3)); + + *platform_type = mode.op_mode.devicemode; + + if (mode.op_mode.ontablestate == 1) { + *laptop_placement = ON_TABLE; + } else if (mode.op_mode.ontablestate == 2) { + *laptop_placement = ON_LAP_MOTION; + } else if (mode.op_mode.inbagstate == 1) { + *laptop_placement = IN_BAG; + } else if (mode.op_mode.outbagstate == 1) { + *laptop_placement = OUT_OF_BAG; + } else if (mode.op_mode.ontablestate == 0 || mode.op_mode.inbagstate == 0 || + mode.op_mode.outbagstate == 0) { + *laptop_placement = LP_UNKNOWN; + pr_warn_once("Unknown laptop placement\n"); + } else if (mode.op_mode.ontablestate == 3 || mode.op_mode.inbagstate == 3 || + mode.op_mode.outbagstate == 3) { + *laptop_placement = LP_UNDEFINED; + pr_warn_once("Undefined laptop placement\n"); + } + + return 0; +} + static int amd_sfh_hpd_info(u8 *user_present) { struct hpd_status hpdstatus; @@ -97,7 +132,7 @@ static int amd_sfh_hpd_info(u8 *user_present) if (!emp2 || !emp2->dev_en.is_hpd_present) return -ENODEV; - hpdstatus.val = readl(emp2->mmio + AMD_C2P_MSG(4)); + hpdstatus.val = readl(emp2->mmio + amd_get_c2p_val(emp2, 4)); *user_present = hpdstatus.shpd.presence; return 0; @@ -131,6 +166,9 @@ int amd_get_sfh_info(struct amd_sfh_info *sfh_info, enum sfh_message_type op) return amd_sfh_hpd_info(&sfh_info->user_present); case MT_ALS: return amd_sfh_als_info(&sfh_info->ambient_light); + case MT_SRA: + return amd_sfh_mode_info(&sfh_info->platform_type, + &sfh_info->laptop_placement); } } return -EINVAL; diff --git a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.h b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.h index 2c211d28764d..665c99ad779f 100644 --- a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.h +++ b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.h @@ -22,8 +22,9 @@ enum sensor_index { ACCEL_IDX, GYRO_IDX, MAG_IDX, - ALS_IDX = 4, - HPD_IDX = 5, + SRA_IDX, + ALS_IDX, + HPD_IDX, MAX_IDX = 15, }; @@ -164,6 +165,25 @@ struct hpd_status { }; }; +struct sfh_op_mode { + union { + u32 val; + struct { + u32 mode : 3; + u32 lidstatus : 1; + u32 angle : 10; + u32 inbagstatedbg : 2; + u32 ontablestate : 2; + u32 inbagstate : 2; + u32 outbagstate : 2; + u32 inbagmlcstate : 1; + u32 powerstate : 2; + u32 data : 3; + u32 devicemode : 4; + } op_mode; + }; +}; + void sfh_interface_init(struct amd_mp2_dev *mp2); void sfh_deinit_emp2(void); void amd_sfh1_1_set_desc_ops(struct amd_mp2_ops *mp2_ops); diff --git a/drivers/hid/bpf/Kconfig b/drivers/hid/bpf/Kconfig index 83214bae6768..d65482e02a6c 100644 --- a/drivers/hid/bpf/Kconfig +++ b/drivers/hid/bpf/Kconfig @@ -3,7 +3,7 @@ menu "HID-BPF support" config HID_BPF bool "HID-BPF support" - depends on BPF + depends on BPF_JIT depends on BPF_SYSCALL depends on DYNAMIC_FTRACE_WITH_DIRECT_CALLS help diff --git a/drivers/hid/bpf/Makefile b/drivers/hid/bpf/Makefile index cf55120cf7d6..d1f2b81788ca 100644 --- a/drivers/hid/bpf/Makefile +++ b/drivers/hid/bpf/Makefile @@ -8,4 +8,4 @@ LIBBPF_INCLUDE = $(srctree)/tools/lib obj-$(CONFIG_HID_BPF) += hid_bpf.o CFLAGS_hid_bpf_dispatch.o += -I$(LIBBPF_INCLUDE) CFLAGS_hid_bpf_jmp_table.o += -I$(LIBBPF_INCLUDE) -hid_bpf-objs += hid_bpf_dispatch.o hid_bpf_jmp_table.o +hid_bpf-objs += hid_bpf_dispatch.o hid_bpf_struct_ops.o diff --git a/drivers/hid/bpf/entrypoints/README b/drivers/hid/bpf/entrypoints/README deleted file mode 100644 index 147e0d41509f..000000000000 --- a/drivers/hid/bpf/entrypoints/README +++ /dev/null @@ -1,4 +0,0 @@ -WARNING: -If you change "entrypoints.bpf.c" do "make -j" in this directory to rebuild "entrypoints.skel.h". -Make sure to have clang 10 installed. -See Documentation/bpf/bpf_devel_QA.rst diff --git a/drivers/hid/bpf/entrypoints/entrypoints.bpf.c b/drivers/hid/bpf/entrypoints/entrypoints.bpf.c deleted file mode 100644 index c22921125a1a..000000000000 --- a/drivers/hid/bpf/entrypoints/entrypoints.bpf.c +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Copyright (c) 2022 Benjamin Tissoires */ - -#include ".output/vmlinux.h" -#include <bpf/bpf_helpers.h> -#include <bpf/bpf_tracing.h> - -#define HID_BPF_MAX_PROGS 1024 - -struct { - __uint(type, BPF_MAP_TYPE_PROG_ARRAY); - __uint(max_entries, HID_BPF_MAX_PROGS); - __uint(key_size, sizeof(__u32)); - __uint(value_size, sizeof(__u32)); -} hid_jmp_table SEC(".maps"); - -SEC("fmod_ret/__hid_bpf_tail_call") -int BPF_PROG(hid_tail_call, struct hid_bpf_ctx *hctx) -{ - bpf_tail_call(ctx, &hid_jmp_table, hctx->index); - - return 0; -} - -char LICENSE[] SEC("license") = "GPL"; diff --git a/drivers/hid/bpf/entrypoints/entrypoints.lskel.h b/drivers/hid/bpf/entrypoints/entrypoints.lskel.h deleted file mode 100644 index 35618051598c..000000000000 --- a/drivers/hid/bpf/entrypoints/entrypoints.lskel.h +++ /dev/null @@ -1,248 +0,0 @@ -/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ -/* THIS FILE IS AUTOGENERATED BY BPFTOOL! */ -#ifndef __ENTRYPOINTS_BPF_SKEL_H__ -#define __ENTRYPOINTS_BPF_SKEL_H__ - -#include <bpf/skel_internal.h> - -struct entrypoints_bpf { - struct bpf_loader_ctx ctx; - struct { - struct bpf_map_desc hid_jmp_table; - } maps; - struct { - struct bpf_prog_desc hid_tail_call; - } progs; - struct { - int hid_tail_call_fd; - } links; -}; - -static inline int -entrypoints_bpf__hid_tail_call__attach(struct entrypoints_bpf *skel) -{ - int prog_fd = skel->progs.hid_tail_call.prog_fd; - int fd = skel_raw_tracepoint_open(NULL, prog_fd); - - if (fd > 0) - skel->links.hid_tail_call_fd = fd; - return fd; -} - -static inline int -entrypoints_bpf__attach(struct entrypoints_bpf *skel) -{ - int ret = 0; - - ret = ret < 0 ? ret : entrypoints_bpf__hid_tail_call__attach(skel); - return ret < 0 ? ret : 0; -} - -static inline void -entrypoints_bpf__detach(struct entrypoints_bpf *skel) -{ - skel_closenz(skel->links.hid_tail_call_fd); -} -static void -entrypoints_bpf__destroy(struct entrypoints_bpf *skel) -{ - if (!skel) - return; - entrypoints_bpf__detach(skel); - skel_closenz(skel->progs.hid_tail_call.prog_fd); - skel_closenz(skel->maps.hid_jmp_table.map_fd); - skel_free(skel); -} -static inline struct entrypoints_bpf * -entrypoints_bpf__open(void) -{ - struct entrypoints_bpf *skel; - - skel = skel_alloc(sizeof(*skel)); - if (!skel) - goto cleanup; - skel->ctx.sz = (void *)&skel->links - (void *)skel; - return skel; -cleanup: - entrypoints_bpf__destroy(skel); - return NULL; -} - -static inline int -entrypoints_bpf__load(struct entrypoints_bpf *skel) -{ - struct bpf_load_and_run_opts opts = {}; - int err; - - opts.ctx = (struct bpf_loader_ctx *)skel; - opts.data_sz = 2856; - opts.data = (void *)"\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x9f\xeb\x01\0\ -\x18\0\0\0\0\0\0\0\x60\x02\0\0\x60\x02\0\0\x12\x02\0\0\0\0\0\0\0\0\0\x02\x03\0\ -\0\0\x01\0\0\0\0\0\0\x01\x04\0\0\0\x20\0\0\x01\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\ -\0\0\x04\0\0\0\x03\0\0\0\x05\0\0\0\0\0\0\x01\x04\0\0\0\x20\0\0\0\0\0\0\0\0\0\0\ -\x02\x06\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\0\x04\0\0\0\0\0\0\ -\0\0\0\x02\x08\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x02\0\0\0\x04\0\0\0\x04\0\0\0\0\ -\0\0\0\x04\0\0\x04\x20\0\0\0\x19\0\0\0\x01\0\0\0\0\0\0\0\x1e\0\0\0\x05\0\0\0\ -\x40\0\0\0\x2a\0\0\0\x07\0\0\0\x80\0\0\0\x33\0\0\0\x07\0\0\0\xc0\0\0\0\x3e\0\0\ -\0\0\0\0\x0e\x09\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\x02\x0c\0\0\0\x4c\0\0\0\0\0\0\ -\x01\x08\0\0\0\x40\0\0\0\0\0\0\0\x01\0\0\x0d\x02\0\0\0\x5f\0\0\0\x0b\0\0\0\x63\ -\0\0\0\x01\0\0\x0c\x0d\0\0\0\x09\x01\0\0\x05\0\0\x04\x20\0\0\0\x15\x01\0\0\x10\ -\0\0\0\0\0\0\0\x1b\x01\0\0\x12\0\0\0\x40\0\0\0\x1f\x01\0\0\x10\0\0\0\x80\0\0\0\ -\x2e\x01\0\0\x14\0\0\0\xa0\0\0\0\0\0\0\0\x15\0\0\0\xc0\0\0\0\x3a\x01\0\0\0\0\0\ -\x08\x11\0\0\0\x40\x01\0\0\0\0\0\x01\x04\0\0\0\x20\0\0\0\0\0\0\0\0\0\0\x02\x13\ -\0\0\0\0\0\0\0\0\0\0\x0a\x1c\0\0\0\x4d\x01\0\0\x04\0\0\x06\x04\0\0\0\x5d\x01\0\ -\0\0\0\0\0\x6e\x01\0\0\x01\0\0\0\x80\x01\0\0\x02\0\0\0\x93\x01\0\0\x03\0\0\0\0\ -\0\0\0\x02\0\0\x05\x04\0\0\0\xa4\x01\0\0\x16\0\0\0\0\0\0\0\xab\x01\0\0\x16\0\0\ -\0\0\0\0\0\xb0\x01\0\0\0\0\0\x08\x02\0\0\0\xec\x01\0\0\0\0\0\x01\x01\0\0\0\x08\ -\0\0\x01\0\0\0\0\0\0\0\x03\0\0\0\0\x17\0\0\0\x04\0\0\0\x04\0\0\0\xf1\x01\0\0\0\ -\0\0\x0e\x18\0\0\0\x01\0\0\0\xf9\x01\0\0\x01\0\0\x0f\x20\0\0\0\x0a\0\0\0\0\0\0\ -\0\x20\0\0\0\xff\x01\0\0\x01\0\0\x0f\x04\0\0\0\x19\0\0\0\0\0\0\0\x04\0\0\0\x07\ -\x02\0\0\0\0\0\x07\0\0\0\0\0\x69\x6e\x74\0\x5f\x5f\x41\x52\x52\x41\x59\x5f\x53\ -\x49\x5a\x45\x5f\x54\x59\x50\x45\x5f\x5f\0\x74\x79\x70\x65\0\x6d\x61\x78\x5f\ -\x65\x6e\x74\x72\x69\x65\x73\0\x6b\x65\x79\x5f\x73\x69\x7a\x65\0\x76\x61\x6c\ -\x75\x65\x5f\x73\x69\x7a\x65\0\x68\x69\x64\x5f\x6a\x6d\x70\x5f\x74\x61\x62\x6c\ -\x65\0\x75\x6e\x73\x69\x67\x6e\x65\x64\x20\x6c\x6f\x6e\x67\x20\x6c\x6f\x6e\x67\ -\0\x63\x74\x78\0\x68\x69\x64\x5f\x74\x61\x69\x6c\x5f\x63\x61\x6c\x6c\0\x66\x6d\ -\x6f\x64\x5f\x72\x65\x74\x2f\x5f\x5f\x68\x69\x64\x5f\x62\x70\x66\x5f\x74\x61\ -\x69\x6c\x5f\x63\x61\x6c\x6c\0\x2f\x68\x6f\x6d\x65\x2f\x62\x74\x69\x73\x73\x6f\ -\x69\x72\x2f\x53\x72\x63\x2f\x68\x69\x64\x2f\x64\x72\x69\x76\x65\x72\x73\x2f\ -\x68\x69\x64\x2f\x62\x70\x66\x2f\x65\x6e\x74\x72\x79\x70\x6f\x69\x6e\x74\x73\ -\x2f\x65\x6e\x74\x72\x79\x70\x6f\x69\x6e\x74\x73\x2e\x62\x70\x66\x2e\x63\0\x69\ -\x6e\x74\x20\x42\x50\x46\x5f\x50\x52\x4f\x47\x28\x68\x69\x64\x5f\x74\x61\x69\ -\x6c\x5f\x63\x61\x6c\x6c\x2c\x20\x73\x74\x72\x75\x63\x74\x20\x68\x69\x64\x5f\ -\x62\x70\x66\x5f\x63\x74\x78\x20\x2a\x68\x63\x74\x78\x29\0\x68\x69\x64\x5f\x62\ -\x70\x66\x5f\x63\x74\x78\0\x69\x6e\x64\x65\x78\0\x68\x69\x64\0\x61\x6c\x6c\x6f\ -\x63\x61\x74\x65\x64\x5f\x73\x69\x7a\x65\0\x72\x65\x70\x6f\x72\x74\x5f\x74\x79\ -\x70\x65\0\x5f\x5f\x75\x33\x32\0\x75\x6e\x73\x69\x67\x6e\x65\x64\x20\x69\x6e\ -\x74\0\x68\x69\x64\x5f\x72\x65\x70\x6f\x72\x74\x5f\x74\x79\x70\x65\0\x48\x49\ -\x44\x5f\x49\x4e\x50\x55\x54\x5f\x52\x45\x50\x4f\x52\x54\0\x48\x49\x44\x5f\x4f\ -\x55\x54\x50\x55\x54\x5f\x52\x45\x50\x4f\x52\x54\0\x48\x49\x44\x5f\x46\x45\x41\ -\x54\x55\x52\x45\x5f\x52\x45\x50\x4f\x52\x54\0\x48\x49\x44\x5f\x52\x45\x50\x4f\ -\x52\x54\x5f\x54\x59\x50\x45\x53\0\x72\x65\x74\x76\x61\x6c\0\x73\x69\x7a\x65\0\ -\x5f\x5f\x73\x33\x32\0\x30\x3a\x30\0\x09\x62\x70\x66\x5f\x74\x61\x69\x6c\x5f\ -\x63\x61\x6c\x6c\x28\x63\x74\x78\x2c\x20\x26\x68\x69\x64\x5f\x6a\x6d\x70\x5f\ -\x74\x61\x62\x6c\x65\x2c\x20\x68\x63\x74\x78\x2d\x3e\x69\x6e\x64\x65\x78\x29\ -\x3b\0\x63\x68\x61\x72\0\x4c\x49\x43\x45\x4e\x53\x45\0\x2e\x6d\x61\x70\x73\0\ -\x6c\x69\x63\x65\x6e\x73\x65\0\x68\x69\x64\x5f\x64\x65\x76\x69\x63\x65\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x8a\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\ -\0\0\0\x04\0\0\0\x04\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x68\x69\x64\x5f\ -\x6a\x6d\x70\x5f\x74\x61\x62\x6c\x65\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\x47\x50\x4c\0\0\0\0\0\x79\x12\0\0\0\0\0\0\x61\x23\0\0\0\0\ -\0\0\x18\x52\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x85\0\0\0\x0c\0\0\0\xb7\0\0\0\0\0\0\0\ -\x95\0\0\0\0\0\0\0\0\0\0\0\x0e\0\0\0\0\0\0\0\x8e\0\0\0\xd3\0\0\0\x05\x48\0\0\ -\x01\0\0\0\x8e\0\0\0\xba\x01\0\0\x02\x50\0\0\x05\0\0\0\x8e\0\0\0\xd3\0\0\0\x05\ -\x48\0\0\x08\0\0\0\x0f\0\0\0\xb6\x01\0\0\0\0\0\0\x1a\0\0\0\x07\0\0\0\0\0\0\0\0\ -\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x68\x69\ -\x64\x5f\x74\x61\x69\x6c\x5f\x63\x61\x6c\x6c\0\0\0\0\0\0\0\x1a\0\0\0\0\0\0\0\ -\x08\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x10\0\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\x01\0\ -\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\x5f\ -\x5f\x68\x69\x64\x5f\x62\x70\x66\x5f\x74\x61\x69\x6c\x5f\x63\x61\x6c\x6c\0\0\0\ -\0\0"; - opts.insns_sz = 1192; - opts.insns = (void *)"\ -\xbf\x16\0\0\0\0\0\0\xbf\xa1\0\0\0\0\0\0\x07\x01\0\0\x78\xff\xff\xff\xb7\x02\0\ -\0\x88\0\0\0\xb7\x03\0\0\0\0\0\0\x85\0\0\0\x71\0\0\0\x05\0\x11\0\0\0\0\0\x61\ -\xa1\x78\xff\0\0\0\0\xd5\x01\x01\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa1\x7c\xff\ -\0\0\0\0\xd5\x01\x01\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa1\x80\xff\0\0\0\0\xd5\ -\x01\x01\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x61\ -\x01\0\0\0\0\0\0\xd5\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\ -\xbf\x70\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\x61\x60\x08\0\0\0\0\0\x18\x61\0\0\0\0\0\ -\0\0\0\0\0\xa8\x09\0\0\x63\x01\0\0\0\0\0\0\x61\x60\x0c\0\0\0\0\0\x18\x61\0\0\0\ -\0\0\0\0\0\0\0\xa4\x09\0\0\x63\x01\0\0\0\0\0\0\x79\x60\x10\0\0\0\0\0\x18\x61\0\ -\0\0\0\0\0\0\0\0\0\x98\x09\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\ -\0\x05\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x90\x09\0\0\x7b\x01\0\0\0\0\0\0\xb7\x01\ -\0\0\x12\0\0\0\x18\x62\0\0\0\0\0\0\0\0\0\0\x90\x09\0\0\xb7\x03\0\0\x1c\0\0\0\ -\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\xd7\xff\0\0\0\0\x63\x7a\x78\ -\xff\0\0\0\0\x61\x60\x1c\0\0\0\0\0\x15\0\x03\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\ -\0\0\xbc\x09\0\0\x63\x01\0\0\0\0\0\0\xb7\x01\0\0\0\0\0\0\x18\x62\0\0\0\0\0\0\0\ -\0\0\0\xb0\x09\0\0\xb7\x03\0\0\x48\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\0\0\0\0\0\ -\0\xc5\x07\xca\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x63\x71\0\0\0\0\ -\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xf8\x09\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x90\ -\x0a\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\x0a\0\0\x18\x61\0\0\ -\0\0\0\0\0\0\0\0\x88\x0a\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\ -\x38\x0a\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xd0\x0a\0\0\x7b\x01\0\0\0\0\0\0\x18\ -\x60\0\0\0\0\0\0\0\0\0\0\x40\x0a\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xe0\x0a\0\0\ -\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x70\x0a\0\0\x18\x61\0\0\0\0\0\ -\0\0\0\0\0\0\x0b\0\0\x7b\x01\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ -\x18\x61\0\0\0\0\0\0\0\0\0\0\xf8\x0a\0\0\x7b\x01\0\0\0\0\0\0\x61\x60\x08\0\0\0\ -\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x98\x0a\0\0\x63\x01\0\0\0\0\0\0\x61\x60\x0c\0\ -\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x9c\x0a\0\0\x63\x01\0\0\0\0\0\0\x79\x60\ -\x10\0\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xa0\x0a\0\0\x7b\x01\0\0\0\0\0\0\x61\ -\xa0\x78\xff\0\0\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\xc8\x0a\0\0\x63\x01\0\0\0\0\0\ -\0\x18\x61\0\0\0\0\0\0\0\0\0\0\x10\x0b\0\0\xb7\x02\0\0\x14\0\0\0\xb7\x03\0\0\ -\x0c\0\0\0\xb7\x04\0\0\0\0\0\0\x85\0\0\0\xa7\0\0\0\xbf\x07\0\0\0\0\0\0\xc5\x07\ -\x91\xff\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\x80\x0a\0\0\x63\x70\x6c\0\0\0\0\0\ -\x77\x07\0\0\x20\0\0\0\x63\x70\x70\0\0\0\0\0\xb7\x01\0\0\x05\0\0\0\x18\x62\0\0\ -\0\0\0\0\0\0\0\0\x80\x0a\0\0\xb7\x03\0\0\x8c\0\0\0\x85\0\0\0\xa6\0\0\0\xbf\x07\ -\0\0\0\0\0\0\x18\x60\0\0\0\0\0\0\0\0\0\0\xf0\x0a\0\0\x61\x01\0\0\0\0\0\0\xd5\ -\x01\x02\0\0\0\0\0\xbf\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\xc5\x07\x7f\xff\0\0\ -\0\0\x63\x7a\x80\xff\0\0\0\0\x61\xa1\x78\xff\0\0\0\0\xd5\x01\x02\0\0\0\0\0\xbf\ -\x19\0\0\0\0\0\0\x85\0\0\0\xa8\0\0\0\x61\xa0\x80\xff\0\0\0\0\x63\x06\x28\0\0\0\ -\0\0\x18\x61\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x61\x10\0\0\0\0\0\0\x63\x06\x18\0\0\0\ -\0\0\xb7\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0"; - err = bpf_load_and_run(&opts); - if (err < 0) - return err; - return 0; -} - -static inline struct entrypoints_bpf * -entrypoints_bpf__open_and_load(void) -{ - struct entrypoints_bpf *skel; - - skel = entrypoints_bpf__open(); - if (!skel) - return NULL; - if (entrypoints_bpf__load(skel)) { - entrypoints_bpf__destroy(skel); - return NULL; - } - return skel; -} - -__attribute__((unused)) static void -entrypoints_bpf__assert(struct entrypoints_bpf *s __attribute__((unused))) -{ -#ifdef __cplusplus -#define _Static_assert static_assert -#endif -#ifdef __cplusplus -#undef _Static_assert -#endif -} - -#endif /* __ENTRYPOINTS_BPF_SKEL_H__ */ diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index e630caf644e8..2e96ec6a3073 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -3,7 +3,7 @@ /* * HID-BPF support for Linux * - * Copyright (c) 2022 Benjamin Tissoires + * Copyright (c) 2022-2024 Benjamin Tissoires */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -17,47 +17,25 @@ #include <linux/kfifo.h> #include <linux/minmax.h> #include <linux/module.h> -#include <linux/workqueue.h> #include "hid_bpf_dispatch.h" -#include "entrypoints/entrypoints.lskel.h" -struct hid_bpf_ops *hid_bpf_ops; -EXPORT_SYMBOL(hid_bpf_ops); - -/** - * hid_bpf_device_event - Called whenever an event is coming in from the device - * - * @ctx: The HID-BPF context - * - * @return %0 on success and keep processing; a positive value to change the - * incoming size buffer; a negative error code to interrupt the processing - * of this event - * - * Declare an %fmod_ret tracing bpf program to this function and attach this - * program through hid_bpf_attach_prog() to have this helper called for - * any incoming event from the device itself. - * - * The function is called while on IRQ context, so we can not sleep. - */ -/* never used by the kernel but declared so we can load and attach a tracepoint */ -__weak noinline int hid_bpf_device_event(struct hid_bpf_ctx *ctx) -{ - return 0; -} +const struct hid_ops *hid_ops; +EXPORT_SYMBOL(hid_ops); u8 * dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type, u8 *data, - u32 *size, int interrupt) + u32 *size, int interrupt, u64 source, bool from_bpf) { struct hid_bpf_ctx_kern ctx_kern = { .ctx = { .hid = hdev, - .report_type = type, .allocated_size = hdev->bpf.allocated_data, .size = *size, }, .data = hdev->bpf.device_data, + .from_bpf = from_bpf, }; + struct hid_bpf_ops *e; int ret; if (type >= HID_REPORT_TYPES) @@ -70,10 +48,22 @@ dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type memset(ctx_kern.data, 0, hdev->bpf.allocated_data); memcpy(ctx_kern.data, data, *size); - ret = hid_bpf_prog_run(hdev, HID_BPF_PROG_TYPE_DEVICE_EVENT, &ctx_kern); - if (ret < 0) - return ERR_PTR(ret); + rcu_read_lock(); + list_for_each_entry_rcu(e, &hdev->bpf.prog_list, list) { + if (e->hid_device_event) { + ret = e->hid_device_event(&ctx_kern.ctx, type, source); + if (ret < 0) { + rcu_read_unlock(); + return ERR_PTR(ret); + } + + if (ret) + ctx_kern.ctx.size = ret; + } + } + rcu_read_unlock(); + ret = ctx_kern.ctx.size; if (ret) { if (ret > ctx_kern.ctx.allocated_size) return ERR_PTR(-EINVAL); @@ -85,27 +75,80 @@ dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type } EXPORT_SYMBOL_GPL(dispatch_hid_bpf_device_event); -/** - * hid_bpf_rdesc_fixup - Called when the probe function parses the report - * descriptor of the HID device - * - * @ctx: The HID-BPF context - * - * @return 0 on success and keep processing; a positive value to change the - * incoming size buffer; a negative error code to interrupt the processing - * of this event - * - * Declare an %fmod_ret tracing bpf program to this function and attach this - * program through hid_bpf_attach_prog() to have this helper called before any - * parsing of the report descriptor by HID. - */ -/* never used by the kernel but declared so we can load and attach a tracepoint */ -__weak noinline int hid_bpf_rdesc_fixup(struct hid_bpf_ctx *ctx) +int dispatch_hid_bpf_raw_requests(struct hid_device *hdev, + unsigned char reportnum, u8 *buf, + u32 size, enum hid_report_type rtype, + enum hid_class_request reqtype, + u64 source, bool from_bpf) { - return 0; + struct hid_bpf_ctx_kern ctx_kern = { + .ctx = { + .hid = hdev, + .allocated_size = size, + .size = size, + }, + .data = buf, + .from_bpf = from_bpf, + }; + struct hid_bpf_ops *e; + int ret, idx; + + if (rtype >= HID_REPORT_TYPES) + return -EINVAL; + + idx = srcu_read_lock(&hdev->bpf.srcu); + list_for_each_entry_srcu(e, &hdev->bpf.prog_list, list, + srcu_read_lock_held(&hdev->bpf.srcu)) { + if (!e->hid_hw_request) + continue; + + ret = e->hid_hw_request(&ctx_kern.ctx, reportnum, rtype, reqtype, source); + if (ret) + goto out; + } + ret = 0; + +out: + srcu_read_unlock(&hdev->bpf.srcu, idx); + return ret; } +EXPORT_SYMBOL_GPL(dispatch_hid_bpf_raw_requests); -u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size) +int dispatch_hid_bpf_output_report(struct hid_device *hdev, + __u8 *buf, u32 size, u64 source, + bool from_bpf) +{ + struct hid_bpf_ctx_kern ctx_kern = { + .ctx = { + .hid = hdev, + .allocated_size = size, + .size = size, + }, + .data = buf, + .from_bpf = from_bpf, + }; + struct hid_bpf_ops *e; + int ret, idx; + + idx = srcu_read_lock(&hdev->bpf.srcu); + list_for_each_entry_srcu(e, &hdev->bpf.prog_list, list, + srcu_read_lock_held(&hdev->bpf.srcu)) { + if (!e->hid_hw_output_report) + continue; + + ret = e->hid_hw_output_report(&ctx_kern.ctx, source); + if (ret) + goto out; + } + ret = 0; + +out: + srcu_read_unlock(&hdev->bpf.srcu, idx); + return ret; +} +EXPORT_SYMBOL_GPL(dispatch_hid_bpf_output_report); + +const u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, unsigned int *size) { int ret; struct hid_bpf_ctx_kern ctx_kern = { @@ -116,13 +159,16 @@ u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *s }, }; + if (!hdev->bpf.rdesc_ops) + goto ignore_bpf; + ctx_kern.data = kzalloc(ctx_kern.ctx.allocated_size, GFP_KERNEL); if (!ctx_kern.data) goto ignore_bpf; memcpy(ctx_kern.data, rdesc, min_t(unsigned int, *size, HID_MAX_DESCRIPTOR_SIZE)); - ret = hid_bpf_prog_run(hdev, HID_BPF_PROG_TYPE_RDESC_FIXUP, &ctx_kern); + ret = hdev->bpf.rdesc_ops->hid_rdesc_fixup(&ctx_kern.ctx); if (ret < 0) goto ignore_bpf; @@ -133,63 +179,38 @@ u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *s *size = ret; } - rdesc = krealloc(ctx_kern.data, *size, GFP_KERNEL); - - return rdesc; + return krealloc(ctx_kern.data, *size, GFP_KERNEL); ignore_bpf: kfree(ctx_kern.data); - return kmemdup(rdesc, *size, GFP_KERNEL); + return rdesc; } EXPORT_SYMBOL_GPL(call_hid_bpf_rdesc_fixup); -/* Disables missing prototype warnings */ -__bpf_kfunc_start_defs(); - -/** - * hid_bpf_get_data - Get the kernel memory pointer associated with the context @ctx - * - * @ctx: The HID-BPF context - * @offset: The offset within the memory - * @rdwr_buf_size: the const size of the buffer - * - * @returns %NULL on error, an %__u8 memory pointer on success - */ -__bpf_kfunc __u8 * -hid_bpf_get_data(struct hid_bpf_ctx *ctx, unsigned int offset, const size_t rdwr_buf_size) +static int device_match_id(struct device *dev, const void *id) { - struct hid_bpf_ctx_kern *ctx_kern; - - if (!ctx) - return NULL; + struct hid_device *hdev = to_hid_device(dev); - ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx); + return hdev->id == *(int *)id; +} - if (rdwr_buf_size + offset > ctx->allocated_size) - return NULL; +struct hid_device *hid_get_device(unsigned int hid_id) +{ + struct device *dev; - return ctx_kern->data + offset; -} -__bpf_kfunc_end_defs(); + if (!hid_ops) + return ERR_PTR(-EINVAL); -/* - * The following set contains all functions we agree BPF programs - * can use. - */ -BTF_KFUNCS_START(hid_bpf_kfunc_ids) -BTF_ID_FLAGS(func, hid_bpf_get_data, KF_RET_NULL) -BTF_KFUNCS_END(hid_bpf_kfunc_ids) + dev = bus_find_device(hid_ops->bus_type, NULL, &hid_id, device_match_id); + if (!dev) + return ERR_PTR(-EINVAL); -static const struct btf_kfunc_id_set hid_bpf_kfunc_set = { - .owner = THIS_MODULE, - .set = &hid_bpf_kfunc_ids, -}; + return to_hid_device(dev); +} -static int device_match_id(struct device *dev, const void *id) +void hid_put_device(struct hid_device *hid) { - struct hid_device *hdev = to_hid_device(dev); - - return hdev->id == *(int *)id; + put_device(&hid->dev); } static int __hid_bpf_allocate_data(struct hid_device *hdev, u8 **data, u32 *size) @@ -228,7 +249,7 @@ static int __hid_bpf_allocate_data(struct hid_device *hdev, u8 **data, u32 *size return 0; } -static int hid_bpf_allocate_event_data(struct hid_device *hdev) +int hid_bpf_allocate_event_data(struct hid_device *hdev) { /* hdev->bpf.device_data is already allocated, abort */ if (hdev->bpf.device_data) @@ -239,103 +260,41 @@ static int hid_bpf_allocate_event_data(struct hid_device *hdev) int hid_bpf_reconnect(struct hid_device *hdev) { - if (!test_and_set_bit(ffs(HID_STAT_REPROBED), &hdev->status)) + if (!test_and_set_bit(ffs(HID_STAT_REPROBED), &hdev->status)) { + /* trigger call to call_hid_bpf_rdesc_fixup() during the next probe */ + hdev->bpf_rsize = 0; return device_reprobe(&hdev->dev); - - return 0; -} - -static int do_hid_bpf_attach_prog(struct hid_device *hdev, int prog_fd, struct bpf_prog *prog, - __u32 flags) -{ - int fd, err, prog_type; - - prog_type = hid_bpf_get_prog_attach_type(prog); - if (prog_type < 0) - return prog_type; - - if (prog_type >= HID_BPF_PROG_TYPE_MAX) - return -EINVAL; - - if (prog_type == HID_BPF_PROG_TYPE_DEVICE_EVENT) { - err = hid_bpf_allocate_event_data(hdev); - if (err) - return err; - } - - fd = __hid_bpf_attach_prog(hdev, prog_type, prog_fd, prog, flags); - if (fd < 0) - return fd; - - if (prog_type == HID_BPF_PROG_TYPE_RDESC_FIXUP) { - err = hid_bpf_reconnect(hdev); - if (err) { - close_fd(fd); - return err; - } } - return fd; + return 0; } /* Disables missing prototype warnings */ __bpf_kfunc_start_defs(); /** - * hid_bpf_attach_prog - Attach the given @prog_fd to the given HID device + * hid_bpf_get_data - Get the kernel memory pointer associated with the context @ctx * - * @hid_id: the system unique identifier of the HID device - * @prog_fd: an fd in the user process representing the program to attach - * @flags: any logical OR combination of &enum hid_bpf_attach_flags + * @ctx: The HID-BPF context + * @offset: The offset within the memory + * @rdwr_buf_size: the const size of the buffer * - * @returns an fd of a bpf_link object on success (> %0), an error code otherwise. - * Closing this fd will detach the program from the HID device (unless the bpf_link - * is pinned to the BPF file system). + * @returns %NULL on error, an %__u8 memory pointer on success */ -/* called from syscall */ -__bpf_kfunc int -hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags) +__bpf_kfunc __u8 * +hid_bpf_get_data(struct hid_bpf_ctx *ctx, unsigned int offset, const size_t rdwr_buf_size) { - struct hid_device *hdev; - struct bpf_prog *prog; - struct device *dev; - int err, fd; - - if (!hid_bpf_ops) - return -EINVAL; - - if ((flags & ~HID_BPF_FLAG_MASK)) - return -EINVAL; - - dev = bus_find_device(hid_bpf_ops->bus_type, NULL, &hid_id, device_match_id); - if (!dev) - return -EINVAL; - - hdev = to_hid_device(dev); + struct hid_bpf_ctx_kern *ctx_kern; - /* - * take a ref on the prog itself, it will be released - * on errors or when it'll be detached - */ - prog = bpf_prog_get(prog_fd); - if (IS_ERR(prog)) { - err = PTR_ERR(prog); - goto out_dev_put; - } + if (!ctx) + return NULL; - fd = do_hid_bpf_attach_prog(hdev, prog_fd, prog, flags); - if (fd < 0) { - err = fd; - goto out_prog_put; - } + ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx); - return fd; + if (rdwr_buf_size + offset > ctx->allocated_size) + return NULL; - out_prog_put: - bpf_prog_put(prog); - out_dev_put: - put_device(dev); - return err; + return ctx_kern->data + offset; } /** @@ -350,20 +309,14 @@ hid_bpf_allocate_context(unsigned int hid_id) { struct hid_device *hdev; struct hid_bpf_ctx_kern *ctx_kern = NULL; - struct device *dev; - - if (!hid_bpf_ops) - return NULL; - dev = bus_find_device(hid_bpf_ops->bus_type, NULL, &hid_id, device_match_id); - if (!dev) + hdev = hid_get_device(hid_id); + if (IS_ERR(hdev)) return NULL; - hdev = to_hid_device(dev); - ctx_kern = kzalloc(sizeof(*ctx_kern), GFP_KERNEL); if (!ctx_kern) { - put_device(dev); + hid_put_device(hdev); return NULL; } @@ -390,7 +343,44 @@ hid_bpf_release_context(struct hid_bpf_ctx *ctx) kfree(ctx_kern); /* get_device() is called by bus_find_device() */ - put_device(&hid->dev); + hid_put_device(hid); +} + +static int +__hid_bpf_hw_check_params(struct hid_bpf_ctx *ctx, __u8 *buf, size_t *buf__sz, + enum hid_report_type rtype) +{ + struct hid_report_enum *report_enum; + struct hid_report *report; + u32 report_len; + + /* check arguments */ + if (!ctx || !hid_ops || !buf) + return -EINVAL; + + switch (rtype) { + case HID_INPUT_REPORT: + case HID_OUTPUT_REPORT: + case HID_FEATURE_REPORT: + break; + default: + return -EINVAL; + } + + if (*buf__sz < 1) + return -EINVAL; + + report_enum = ctx->hid->report_enum + rtype; + report = hid_ops->hid_get_report(report_enum, buf); + if (!report) + return -EINVAL; + + report_len = hid_report_len(report); + + if (*buf__sz > report_len) + *buf__sz = report_len; + + return 0; } /** @@ -408,25 +398,20 @@ __bpf_kfunc int hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz, enum hid_report_type rtype, enum hid_class_request reqtype) { - struct hid_device *hdev; - struct hid_report *report; - struct hid_report_enum *report_enum; + struct hid_bpf_ctx_kern *ctx_kern; + size_t size = buf__sz; u8 *dma_data; - u32 report_len; int ret; - /* check arguments */ - if (!ctx || !hid_bpf_ops || !buf) - return -EINVAL; + ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx); - switch (rtype) { - case HID_INPUT_REPORT: - case HID_OUTPUT_REPORT: - case HID_FEATURE_REPORT: - break; - default: - return -EINVAL; - } + if (ctx_kern->from_bpf) + return -EDEADLOCK; + + /* check arguments */ + ret = __hid_bpf_hw_check_params(ctx, buf, &size, rtype); + if (ret) + return ret; switch (reqtype) { case HID_REQ_GET_REPORT: @@ -440,31 +425,18 @@ hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz, return -EINVAL; } - if (buf__sz < 1) - return -EINVAL; - - hdev = (struct hid_device *)ctx->hid; /* discard const */ - - report_enum = hdev->report_enum + rtype; - report = hid_bpf_ops->hid_get_report(report_enum, buf); - if (!report) - return -EINVAL; - - report_len = hid_report_len(report); - - if (buf__sz > report_len) - buf__sz = report_len; - - dma_data = kmemdup(buf, buf__sz, GFP_KERNEL); + dma_data = kmemdup(buf, size, GFP_KERNEL); if (!dma_data) return -ENOMEM; - ret = hid_bpf_ops->hid_hw_raw_request(hdev, + ret = hid_ops->hid_hw_raw_request(ctx->hid, dma_data[0], dma_data, - buf__sz, + size, rtype, - reqtype); + reqtype, + (u64)(long)ctx, + true); /* prevent infinite recursions */ if (ret > 0) memcpy(buf, dma_data, ret); @@ -472,26 +444,144 @@ hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz, kfree(dma_data); return ret; } + +/** + * hid_bpf_hw_output_report - Send an output report to a HID device + * + * @ctx: the HID-BPF context previously allocated in hid_bpf_allocate_context() + * @buf: a %PTR_TO_MEM buffer + * @buf__sz: the size of the data to transfer + * + * Returns the number of bytes transferred on success, a negative error code otherwise. + */ +__bpf_kfunc int +hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz) +{ + struct hid_bpf_ctx_kern *ctx_kern; + size_t size = buf__sz; + u8 *dma_data; + int ret; + + ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx); + if (ctx_kern->from_bpf) + return -EDEADLOCK; + + /* check arguments */ + ret = __hid_bpf_hw_check_params(ctx, buf, &size, HID_OUTPUT_REPORT); + if (ret) + return ret; + + dma_data = kmemdup(buf, size, GFP_KERNEL); + if (!dma_data) + return -ENOMEM; + + ret = hid_ops->hid_hw_output_report(ctx->hid, dma_data, size, (u64)(long)ctx, true); + + kfree(dma_data); + return ret; +} + +static int +__hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf, + size_t size, bool lock_already_taken) +{ + struct hid_bpf_ctx_kern *ctx_kern; + int ret; + + ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx); + if (ctx_kern->from_bpf) + return -EDEADLOCK; + + /* check arguments */ + ret = __hid_bpf_hw_check_params(ctx, buf, &size, type); + if (ret) + return ret; + + return hid_ops->hid_input_report(ctx->hid, type, buf, size, 0, (u64)(long)ctx, true, + lock_already_taken); +} + +/** + * hid_bpf_try_input_report - Inject a HID report in the kernel from a HID device + * + * @ctx: the HID-BPF context previously allocated in hid_bpf_allocate_context() + * @type: the type of the report (%HID_INPUT_REPORT, %HID_FEATURE_REPORT, %HID_OUTPUT_REPORT) + * @buf: a %PTR_TO_MEM buffer + * @buf__sz: the size of the data to transfer + * + * Returns %0 on success, a negative error code otherwise. This function will immediately + * fail if the device is not available, thus can be safely used in IRQ context. + */ +__bpf_kfunc int +hid_bpf_try_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf, + const size_t buf__sz) +{ + struct hid_bpf_ctx_kern *ctx_kern; + bool from_hid_event_hook; + + ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx); + from_hid_event_hook = ctx_kern->data && ctx_kern->data == ctx->hid->bpf.device_data; + + return __hid_bpf_input_report(ctx, type, buf, buf__sz, from_hid_event_hook); +} + +/** + * hid_bpf_input_report - Inject a HID report in the kernel from a HID device + * + * @ctx: the HID-BPF context previously allocated in hid_bpf_allocate_context() + * @type: the type of the report (%HID_INPUT_REPORT, %HID_FEATURE_REPORT, %HID_OUTPUT_REPORT) + * @buf: a %PTR_TO_MEM buffer + * @buf__sz: the size of the data to transfer + * + * Returns %0 on success, a negative error code otherwise. This function will wait for the + * device to be available before injecting the event, thus needs to be called in sleepable + * context. + */ +__bpf_kfunc int +hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf, + const size_t buf__sz) +{ + int ret; + + ret = down_interruptible(&ctx->hid->driver_input_lock); + if (ret) + return ret; + + /* check arguments */ + ret = __hid_bpf_input_report(ctx, type, buf, buf__sz, true /* lock_already_taken */); + + up(&ctx->hid->driver_input_lock); + + return ret; +} __bpf_kfunc_end_defs(); -/* our HID-BPF entrypoints */ -BTF_SET8_START(hid_bpf_fmodret_ids) -BTF_ID_FLAGS(func, hid_bpf_device_event) -BTF_ID_FLAGS(func, hid_bpf_rdesc_fixup) -BTF_ID_FLAGS(func, __hid_bpf_tail_call) -BTF_SET8_END(hid_bpf_fmodret_ids) +/* + * The following set contains all functions we agree BPF programs + * can use. + */ +BTF_KFUNCS_START(hid_bpf_kfunc_ids) +BTF_ID_FLAGS(func, hid_bpf_get_data, KF_RET_NULL) +BTF_ID_FLAGS(func, hid_bpf_allocate_context, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE) +BTF_ID_FLAGS(func, hid_bpf_release_context, KF_RELEASE | KF_SLEEPABLE) +BTF_ID_FLAGS(func, hid_bpf_hw_request, KF_SLEEPABLE) +BTF_ID_FLAGS(func, hid_bpf_hw_output_report, KF_SLEEPABLE) +BTF_ID_FLAGS(func, hid_bpf_input_report, KF_SLEEPABLE) +BTF_ID_FLAGS(func, hid_bpf_try_input_report) +BTF_KFUNCS_END(hid_bpf_kfunc_ids) -static const struct btf_kfunc_id_set hid_bpf_fmodret_set = { +static const struct btf_kfunc_id_set hid_bpf_kfunc_set = { .owner = THIS_MODULE, - .set = &hid_bpf_fmodret_ids, + .set = &hid_bpf_kfunc_ids, }; /* for syscall HID-BPF */ BTF_KFUNCS_START(hid_bpf_syscall_kfunc_ids) -BTF_ID_FLAGS(func, hid_bpf_attach_prog) BTF_ID_FLAGS(func, hid_bpf_allocate_context, KF_ACQUIRE | KF_RET_NULL) BTF_ID_FLAGS(func, hid_bpf_release_context, KF_RELEASE) BTF_ID_FLAGS(func, hid_bpf_hw_request) +BTF_ID_FLAGS(func, hid_bpf_hw_output_report) +BTF_ID_FLAGS(func, hid_bpf_input_report) BTF_KFUNCS_END(hid_bpf_syscall_kfunc_ids) static const struct btf_kfunc_id_set hid_bpf_syscall_kfunc_set = { @@ -501,14 +591,20 @@ static const struct btf_kfunc_id_set hid_bpf_syscall_kfunc_set = { int hid_bpf_connect_device(struct hid_device *hdev) { - struct hid_bpf_prog_list *prog_list; + bool need_to_allocate = false; + struct hid_bpf_ops *e; rcu_read_lock(); - prog_list = rcu_dereference(hdev->bpf.progs[HID_BPF_PROG_TYPE_DEVICE_EVENT]); + list_for_each_entry_rcu(e, &hdev->bpf.prog_list, list) { + if (e->hid_device_event) { + need_to_allocate = true; + break; + } + } rcu_read_unlock(); /* only allocate BPF data if there are programs attached */ - if (!prog_list) + if (!need_to_allocate) return 0; return hid_bpf_allocate_event_data(hdev); @@ -531,13 +627,18 @@ void hid_bpf_destroy_device(struct hid_device *hdev) /* mark the device as destroyed in bpf so we don't reattach it */ hdev->bpf.destroyed = true; - __hid_bpf_destroy_device(hdev); + __hid_bpf_ops_destroy_device(hdev); + + synchronize_srcu(&hdev->bpf.srcu); + cleanup_srcu_struct(&hdev->bpf.srcu); } EXPORT_SYMBOL_GPL(hid_bpf_destroy_device); -void hid_bpf_device_init(struct hid_device *hdev) +int hid_bpf_device_init(struct hid_device *hdev) { - spin_lock_init(&hdev->bpf.progs_lock); + INIT_LIST_HEAD(&hdev->bpf.prog_list); + mutex_init(&hdev->bpf.prog_list_lock); + return init_srcu_struct(&hdev->bpf.srcu); } EXPORT_SYMBOL_GPL(hid_bpf_device_init); @@ -548,30 +649,15 @@ static int __init hid_bpf_init(void) /* Note: if we exit with an error any time here, we would entirely break HID, which * is probably not something we want. So we log an error and return success. * - * This is not a big deal: the syscall allowing to attach a BPF program to a HID device - * will not be available, so nobody will be able to use the functionality. + * This is not a big deal: nobody will be able to use the functionality. */ - err = register_btf_fmodret_id_set(&hid_bpf_fmodret_set); - if (err) { - pr_warn("error while registering fmodret entrypoints: %d", err); - return 0; - } - - err = hid_bpf_preload_skel(); - if (err) { - pr_warn("error while preloading HID BPF dispatcher: %d", err); - return 0; - } - - /* register tracing kfuncs after we are sure we can load our preloaded bpf program */ - err = register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &hid_bpf_kfunc_set); + err = register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &hid_bpf_kfunc_set); if (err) { pr_warn("error while setting HID BPF tracing kfuncs: %d", err); return 0; } - /* register syscalls after we are sure we can load our preloaded bpf program */ err = register_btf_kfunc_id_set(BPF_PROG_TYPE_SYSCALL, &hid_bpf_syscall_kfunc_set); if (err) { pr_warn("error while setting HID BPF syscall kfuncs: %d", err); @@ -581,15 +667,6 @@ static int __init hid_bpf_init(void) return 0; } -static void __exit hid_bpf_exit(void) -{ - /* HID depends on us, so if we hit that code, we are guaranteed that hid - * has been removed and thus we do not need to clear the HID devices - */ - hid_bpf_free_links_and_skel(); -} - late_initcall(hid_bpf_init); -module_exit(hid_bpf_exit); MODULE_AUTHOR("Benjamin Tissoires"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/bpf/hid_bpf_dispatch.h b/drivers/hid/bpf/hid_bpf_dispatch.h index fbe0639d09f2..44c6ea22233f 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.h +++ b/drivers/hid/bpf/hid_bpf_dispatch.h @@ -8,16 +8,13 @@ struct hid_bpf_ctx_kern { struct hid_bpf_ctx ctx; u8 *data; + bool from_bpf; }; -int hid_bpf_preload_skel(void); -void hid_bpf_free_links_and_skel(void); -int hid_bpf_get_prog_attach_type(struct bpf_prog *prog); -int __hid_bpf_attach_prog(struct hid_device *hdev, enum hid_bpf_prog_type prog_type, int prog_fd, - struct bpf_prog *prog, __u32 flags); -void __hid_bpf_destroy_device(struct hid_device *hdev); -int hid_bpf_prog_run(struct hid_device *hdev, enum hid_bpf_prog_type type, - struct hid_bpf_ctx_kern *ctx_kern); +struct hid_device *hid_get_device(unsigned int hid_id); +void hid_put_device(struct hid_device *hid); +int hid_bpf_allocate_event_data(struct hid_device *hdev); +void __hid_bpf_ops_destroy_device(struct hid_device *hdev); int hid_bpf_reconnect(struct hid_device *hdev); struct bpf_prog; diff --git a/drivers/hid/bpf/hid_bpf_jmp_table.c b/drivers/hid/bpf/hid_bpf_jmp_table.c deleted file mode 100644 index aa8e1c79cdf5..000000000000 --- a/drivers/hid/bpf/hid_bpf_jmp_table.c +++ /dev/null @@ -1,565 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only - -/* - * HID-BPF support for Linux - * - * Copyright (c) 2022 Benjamin Tissoires - */ - -#include <linux/bitops.h> -#include <linux/btf.h> -#include <linux/btf_ids.h> -#include <linux/circ_buf.h> -#include <linux/filter.h> -#include <linux/hid.h> -#include <linux/hid_bpf.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/workqueue.h> -#include "hid_bpf_dispatch.h" -#include "entrypoints/entrypoints.lskel.h" - -#define HID_BPF_MAX_PROGS 1024 /* keep this in sync with preloaded bpf, - * needs to be a power of 2 as we use it as - * a circular buffer - */ - -#define NEXT(idx) (((idx) + 1) & (HID_BPF_MAX_PROGS - 1)) -#define PREV(idx) (((idx) - 1) & (HID_BPF_MAX_PROGS - 1)) - -/* - * represents one attached program stored in the hid jump table - */ -struct hid_bpf_prog_entry { - struct bpf_prog *prog; - struct hid_device *hdev; - enum hid_bpf_prog_type type; - u16 idx; -}; - -struct hid_bpf_jmp_table { - struct bpf_map *map; - struct hid_bpf_prog_entry entries[HID_BPF_MAX_PROGS]; /* compacted list, circular buffer */ - int tail, head; - struct bpf_prog *progs[HID_BPF_MAX_PROGS]; /* idx -> progs mapping */ - unsigned long enabled[BITS_TO_LONGS(HID_BPF_MAX_PROGS)]; -}; - -#define FOR_ENTRIES(__i, __start, __end) \ - for (__i = __start; CIRC_CNT(__end, __i, HID_BPF_MAX_PROGS); __i = NEXT(__i)) - -static struct hid_bpf_jmp_table jmp_table; - -static DEFINE_MUTEX(hid_bpf_attach_lock); /* held when attaching/detaching programs */ - -static void hid_bpf_release_progs(struct work_struct *work); - -static DECLARE_WORK(release_work, hid_bpf_release_progs); - -BTF_ID_LIST(hid_bpf_btf_ids) -BTF_ID(func, hid_bpf_device_event) /* HID_BPF_PROG_TYPE_DEVICE_EVENT */ -BTF_ID(func, hid_bpf_rdesc_fixup) /* HID_BPF_PROG_TYPE_RDESC_FIXUP */ - -static int hid_bpf_max_programs(enum hid_bpf_prog_type type) -{ - switch (type) { - case HID_BPF_PROG_TYPE_DEVICE_EVENT: - return HID_BPF_MAX_PROGS_PER_DEV; - case HID_BPF_PROG_TYPE_RDESC_FIXUP: - return 1; - default: - return -EINVAL; - } -} - -static int hid_bpf_program_count(struct hid_device *hdev, - struct bpf_prog *prog, - enum hid_bpf_prog_type type) -{ - int i, n = 0; - - if (type >= HID_BPF_PROG_TYPE_MAX) - return -EINVAL; - - FOR_ENTRIES(i, jmp_table.tail, jmp_table.head) { - struct hid_bpf_prog_entry *entry = &jmp_table.entries[i]; - - if (type != HID_BPF_PROG_TYPE_UNDEF && entry->type != type) - continue; - - if (hdev && entry->hdev != hdev) - continue; - - if (prog && entry->prog != prog) - continue; - - n++; - } - - return n; -} - -__weak noinline int __hid_bpf_tail_call(struct hid_bpf_ctx *ctx) -{ - return 0; -} - -int hid_bpf_prog_run(struct hid_device *hdev, enum hid_bpf_prog_type type, - struct hid_bpf_ctx_kern *ctx_kern) -{ - struct hid_bpf_prog_list *prog_list; - int i, idx, err = 0; - - rcu_read_lock(); - prog_list = rcu_dereference(hdev->bpf.progs[type]); - - if (!prog_list) - goto out_unlock; - - for (i = 0; i < prog_list->prog_cnt; i++) { - idx = prog_list->prog_idx[i]; - - if (!test_bit(idx, jmp_table.enabled)) - continue; - - ctx_kern->ctx.index = idx; - err = __hid_bpf_tail_call(&ctx_kern->ctx); - if (err < 0) - break; - if (err) - ctx_kern->ctx.retval = err; - } - - out_unlock: - rcu_read_unlock(); - - return err; -} - -/* - * assign the list of programs attached to a given hid device. - */ -static void __hid_bpf_set_hdev_progs(struct hid_device *hdev, struct hid_bpf_prog_list *new_list, - enum hid_bpf_prog_type type) -{ - struct hid_bpf_prog_list *old_list; - - spin_lock(&hdev->bpf.progs_lock); - old_list = rcu_dereference_protected(hdev->bpf.progs[type], - lockdep_is_held(&hdev->bpf.progs_lock)); - rcu_assign_pointer(hdev->bpf.progs[type], new_list); - spin_unlock(&hdev->bpf.progs_lock); - synchronize_rcu(); - - kfree(old_list); -} - -/* - * allocate and populate the list of programs attached to a given hid device. - * - * Must be called under lock. - */ -static int hid_bpf_populate_hdev(struct hid_device *hdev, enum hid_bpf_prog_type type) -{ - struct hid_bpf_prog_list *new_list; - int i; - - if (type >= HID_BPF_PROG_TYPE_MAX || !hdev) - return -EINVAL; - - if (hdev->bpf.destroyed) - return 0; - - new_list = kzalloc(sizeof(*new_list), GFP_KERNEL); - if (!new_list) - return -ENOMEM; - - FOR_ENTRIES(i, jmp_table.tail, jmp_table.head) { - struct hid_bpf_prog_entry *entry = &jmp_table.entries[i]; - - if (entry->type == type && entry->hdev == hdev && - test_bit(entry->idx, jmp_table.enabled)) - new_list->prog_idx[new_list->prog_cnt++] = entry->idx; - } - - __hid_bpf_set_hdev_progs(hdev, new_list, type); - - return 0; -} - -static void __hid_bpf_do_release_prog(int map_fd, unsigned int idx) -{ - skel_map_delete_elem(map_fd, &idx); - jmp_table.progs[idx] = NULL; -} - -static void hid_bpf_release_progs(struct work_struct *work) -{ - int i, j, n, map_fd = -1; - bool hdev_destroyed; - - if (!jmp_table.map) - return; - - /* retrieve a fd of our prog_array map in BPF */ - map_fd = skel_map_get_fd_by_id(jmp_table.map->id); - if (map_fd < 0) - return; - - mutex_lock(&hid_bpf_attach_lock); /* protects against attaching new programs */ - - /* detach unused progs from HID devices */ - FOR_ENTRIES(i, jmp_table.tail, jmp_table.head) { - struct hid_bpf_prog_entry *entry = &jmp_table.entries[i]; - enum hid_bpf_prog_type type; - struct hid_device *hdev; - - if (test_bit(entry->idx, jmp_table.enabled)) - continue; - - /* we have an attached prog */ - if (entry->hdev) { - hdev = entry->hdev; - type = entry->type; - /* - * hdev is still valid, even if we are called after hid_destroy_device(): - * when hid_bpf_attach() gets called, it takes a ref on the dev through - * bus_find_device() - */ - hdev_destroyed = hdev->bpf.destroyed; - - hid_bpf_populate_hdev(hdev, type); - - /* mark all other disabled progs from hdev of the given type as detached */ - FOR_ENTRIES(j, i, jmp_table.head) { - struct hid_bpf_prog_entry *next; - - next = &jmp_table.entries[j]; - - if (test_bit(next->idx, jmp_table.enabled)) - continue; - - if (next->hdev == hdev && next->type == type) { - /* - * clear the hdev reference and decrement the device ref - * that was taken during bus_find_device() while calling - * hid_bpf_attach() - */ - next->hdev = NULL; - put_device(&hdev->dev); - } - } - - /* if type was rdesc fixup and the device is not gone, reconnect device */ - if (type == HID_BPF_PROG_TYPE_RDESC_FIXUP && !hdev_destroyed) - hid_bpf_reconnect(hdev); - } - } - - /* remove all unused progs from the jump table */ - FOR_ENTRIES(i, jmp_table.tail, jmp_table.head) { - struct hid_bpf_prog_entry *entry = &jmp_table.entries[i]; - - if (test_bit(entry->idx, jmp_table.enabled)) - continue; - - if (entry->prog) - __hid_bpf_do_release_prog(map_fd, entry->idx); - } - - /* compact the entry list */ - n = jmp_table.tail; - FOR_ENTRIES(i, jmp_table.tail, jmp_table.head) { - struct hid_bpf_prog_entry *entry = &jmp_table.entries[i]; - - if (!test_bit(entry->idx, jmp_table.enabled)) - continue; - - jmp_table.entries[n] = jmp_table.entries[i]; - n = NEXT(n); - } - - jmp_table.head = n; - - mutex_unlock(&hid_bpf_attach_lock); - - if (map_fd >= 0) - close_fd(map_fd); -} - -static void hid_bpf_release_prog_at(int idx) -{ - int map_fd = -1; - - /* retrieve a fd of our prog_array map in BPF */ - map_fd = skel_map_get_fd_by_id(jmp_table.map->id); - if (map_fd < 0) - return; - - __hid_bpf_do_release_prog(map_fd, idx); - - close(map_fd); -} - -/* - * Insert the given BPF program represented by its fd in the jmp table. - * Returns the index in the jump table or a negative error. - */ -static int hid_bpf_insert_prog(int prog_fd, struct bpf_prog *prog) -{ - int i, index = -1, map_fd = -1, err = -EINVAL; - - /* retrieve a fd of our prog_array map in BPF */ - map_fd = skel_map_get_fd_by_id(jmp_table.map->id); - - if (map_fd < 0) { - err = -EINVAL; - goto out; - } - - /* find the first available index in the jmp_table */ - for (i = 0; i < HID_BPF_MAX_PROGS; i++) { - if (!jmp_table.progs[i] && index < 0) { - /* mark the index as used */ - jmp_table.progs[i] = prog; - index = i; - __set_bit(i, jmp_table.enabled); - } - } - if (index < 0) { - err = -ENOMEM; - goto out; - } - - /* insert the program in the jump table */ - err = skel_map_update_elem(map_fd, &index, &prog_fd, 0); - if (err) - goto out; - - /* return the index */ - err = index; - - out: - if (err < 0) - __hid_bpf_do_release_prog(map_fd, index); - if (map_fd >= 0) - close_fd(map_fd); - return err; -} - -int hid_bpf_get_prog_attach_type(struct bpf_prog *prog) -{ - int prog_type = HID_BPF_PROG_TYPE_UNDEF; - int i; - - for (i = 0; i < HID_BPF_PROG_TYPE_MAX; i++) { - if (hid_bpf_btf_ids[i] == prog->aux->attach_btf_id) { - prog_type = i; - break; - } - } - - return prog_type; -} - -static void hid_bpf_link_release(struct bpf_link *link) -{ - struct hid_bpf_link *hid_link = - container_of(link, struct hid_bpf_link, link); - - __clear_bit(hid_link->hid_table_index, jmp_table.enabled); - schedule_work(&release_work); -} - -static void hid_bpf_link_dealloc(struct bpf_link *link) -{ - struct hid_bpf_link *hid_link = - container_of(link, struct hid_bpf_link, link); - - kfree(hid_link); -} - -static void hid_bpf_link_show_fdinfo(const struct bpf_link *link, - struct seq_file *seq) -{ - seq_printf(seq, - "attach_type:\tHID-BPF\n"); -} - -static const struct bpf_link_ops hid_bpf_link_lops = { - .release = hid_bpf_link_release, - .dealloc = hid_bpf_link_dealloc, - .show_fdinfo = hid_bpf_link_show_fdinfo, -}; - -/* called from syscall */ -noinline int -__hid_bpf_attach_prog(struct hid_device *hdev, enum hid_bpf_prog_type prog_type, - int prog_fd, struct bpf_prog *prog, __u32 flags) -{ - struct bpf_link_primer link_primer; - struct hid_bpf_link *link; - struct hid_bpf_prog_entry *prog_entry; - int cnt, err = -EINVAL, prog_table_idx = -1; - - mutex_lock(&hid_bpf_attach_lock); - - link = kzalloc(sizeof(*link), GFP_USER); - if (!link) { - err = -ENOMEM; - goto err_unlock; - } - - bpf_link_init(&link->link, BPF_LINK_TYPE_UNSPEC, - &hid_bpf_link_lops, prog); - - /* do not attach too many programs to a given HID device */ - cnt = hid_bpf_program_count(hdev, NULL, prog_type); - if (cnt < 0) { - err = cnt; - goto err_unlock; - } - - if (cnt >= hid_bpf_max_programs(prog_type)) { - err = -E2BIG; - goto err_unlock; - } - - prog_table_idx = hid_bpf_insert_prog(prog_fd, prog); - /* if the jmp table is full, abort */ - if (prog_table_idx < 0) { - err = prog_table_idx; - goto err_unlock; - } - - if (flags & HID_BPF_FLAG_INSERT_HEAD) { - /* take the previous prog_entry slot */ - jmp_table.tail = PREV(jmp_table.tail); - prog_entry = &jmp_table.entries[jmp_table.tail]; - } else { - /* take the next prog_entry slot */ - prog_entry = &jmp_table.entries[jmp_table.head]; - jmp_table.head = NEXT(jmp_table.head); - } - - /* we steal the ref here */ - prog_entry->prog = prog; - prog_entry->idx = prog_table_idx; - prog_entry->hdev = hdev; - prog_entry->type = prog_type; - - /* finally store the index in the device list */ - err = hid_bpf_populate_hdev(hdev, prog_type); - if (err) { - hid_bpf_release_prog_at(prog_table_idx); - goto err_unlock; - } - - link->hid_table_index = prog_table_idx; - - err = bpf_link_prime(&link->link, &link_primer); - if (err) - goto err_unlock; - - mutex_unlock(&hid_bpf_attach_lock); - - return bpf_link_settle(&link_primer); - - err_unlock: - mutex_unlock(&hid_bpf_attach_lock); - - kfree(link); - - return err; -} - -void __hid_bpf_destroy_device(struct hid_device *hdev) -{ - int type, i; - struct hid_bpf_prog_list *prog_list; - - rcu_read_lock(); - - for (type = 0; type < HID_BPF_PROG_TYPE_MAX; type++) { - prog_list = rcu_dereference(hdev->bpf.progs[type]); - - if (!prog_list) - continue; - - for (i = 0; i < prog_list->prog_cnt; i++) - __clear_bit(prog_list->prog_idx[i], jmp_table.enabled); - } - - rcu_read_unlock(); - - for (type = 0; type < HID_BPF_PROG_TYPE_MAX; type++) - __hid_bpf_set_hdev_progs(hdev, NULL, type); - - /* schedule release of all detached progs */ - schedule_work(&release_work); -} - -#define HID_BPF_PROGS_COUNT 1 - -static struct bpf_link *links[HID_BPF_PROGS_COUNT]; -static struct entrypoints_bpf *skel; - -void hid_bpf_free_links_and_skel(void) -{ - int i; - - /* the following is enough to release all programs attached to hid */ - if (jmp_table.map) - bpf_map_put_with_uref(jmp_table.map); - - for (i = 0; i < ARRAY_SIZE(links); i++) { - if (!IS_ERR_OR_NULL(links[i])) - bpf_link_put(links[i]); - } - entrypoints_bpf__destroy(skel); -} - -#define ATTACH_AND_STORE_LINK(__name) do { \ - err = entrypoints_bpf__##__name##__attach(skel); \ - if (err) \ - goto out; \ - \ - links[idx] = bpf_link_get_from_fd(skel->links.__name##_fd); \ - if (IS_ERR(links[idx])) { \ - err = PTR_ERR(links[idx]); \ - goto out; \ - } \ - \ - /* Avoid taking over stdin/stdout/stderr of init process. Zeroing out \ - * makes skel_closenz() a no-op later in iterators_bpf__destroy(). \ - */ \ - close_fd(skel->links.__name##_fd); \ - skel->links.__name##_fd = 0; \ - idx++; \ -} while (0) - -int hid_bpf_preload_skel(void) -{ - int err, idx = 0; - - skel = entrypoints_bpf__open(); - if (!skel) - return -ENOMEM; - - err = entrypoints_bpf__load(skel); - if (err) - goto out; - - jmp_table.map = bpf_map_get_with_uref(skel->maps.hid_jmp_table.map_fd); - if (IS_ERR(jmp_table.map)) { - err = PTR_ERR(jmp_table.map); - goto out; - } - - ATTACH_AND_STORE_LINK(hid_tail_call); - - return 0; -out: - hid_bpf_free_links_and_skel(); - return err; -} diff --git a/drivers/hid/bpf/hid_bpf_struct_ops.c b/drivers/hid/bpf/hid_bpf_struct_ops.c new file mode 100644 index 000000000000..702c22fae136 --- /dev/null +++ b/drivers/hid/bpf/hid_bpf_struct_ops.c @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * HID-BPF support for Linux + * + * Copyright (c) 2024 Benjamin Tissoires + */ + +#include <linux/bitops.h> +#include <linux/bpf_verifier.h> +#include <linux/bpf.h> +#include <linux/btf.h> +#include <linux/btf_ids.h> +#include <linux/filter.h> +#include <linux/hid.h> +#include <linux/hid_bpf.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/stddef.h> +#include <linux/workqueue.h> +#include "hid_bpf_dispatch.h" + +static struct btf *hid_bpf_ops_btf; + +static int hid_bpf_ops_init(struct btf *btf) +{ + hid_bpf_ops_btf = btf; + return 0; +} + +static bool hid_bpf_ops_is_valid_access(int off, int size, + enum bpf_access_type type, + const struct bpf_prog *prog, + struct bpf_insn_access_aux *info) +{ + return bpf_tracing_btf_ctx_access(off, size, type, prog, info); +} + +static int hid_bpf_ops_check_member(const struct btf_type *t, + const struct btf_member *member, + const struct bpf_prog *prog) +{ + u32 moff = __btf_member_bit_offset(t, member) / 8; + + switch (moff) { + case offsetof(struct hid_bpf_ops, hid_rdesc_fixup): + case offsetof(struct hid_bpf_ops, hid_hw_request): + case offsetof(struct hid_bpf_ops, hid_hw_output_report): + break; + default: + if (prog->sleepable) + return -EINVAL; + } + + return 0; +} + +struct hid_bpf_offset_write_range { + const char *struct_name; + u32 struct_length; + u32 start; + u32 end; +}; + +static int hid_bpf_ops_btf_struct_access(struct bpf_verifier_log *log, + const struct bpf_reg_state *reg, + int off, int size) +{ +#define WRITE_RANGE(_name, _field, _is_string) \ + { \ + .struct_name = #_name, \ + .struct_length = sizeof(struct _name), \ + .start = offsetof(struct _name, _field), \ + .end = offsetofend(struct _name, _field) - !!(_is_string), \ + } + + const struct hid_bpf_offset_write_range write_ranges[] = { + WRITE_RANGE(hid_bpf_ctx, retval, false), + WRITE_RANGE(hid_device, name, true), + WRITE_RANGE(hid_device, uniq, true), + WRITE_RANGE(hid_device, phys, true), + }; +#undef WRITE_RANGE + const struct btf_type *state = NULL; + const struct btf_type *t; + const char *cur = NULL; + int i; + + t = btf_type_by_id(reg->btf, reg->btf_id); + + for (i = 0; i < ARRAY_SIZE(write_ranges); i++) { + const struct hid_bpf_offset_write_range *write_range = &write_ranges[i]; + s32 type_id; + + /* we already found a writeable struct, but there is a + * new one, let's break the loop. + */ + if (t == state && write_range->struct_name != cur) + break; + + /* new struct to look for */ + if (write_range->struct_name != cur) { + type_id = btf_find_by_name_kind(reg->btf, write_range->struct_name, + BTF_KIND_STRUCT); + if (type_id < 0) + return -EINVAL; + + state = btf_type_by_id(reg->btf, type_id); + } + + /* this is not the struct we are looking for */ + if (t != state) { + cur = write_range->struct_name; + continue; + } + + /* first time we see this struct, check for out of bounds */ + if (cur != write_range->struct_name && + off + size > write_range->struct_length) { + bpf_log(log, "write access for struct %s at off %d with size %d\n", + write_range->struct_name, off, size); + return -EACCES; + } + + /* now check if we are in our boundaries */ + if (off >= write_range->start && off + size <= write_range->end) + return NOT_INIT; + + cur = write_range->struct_name; + } + + + if (t != state) + bpf_log(log, "write access to this struct is not supported\n"); + else + bpf_log(log, + "write access at off %d with size %d on read-only part of %s\n", + off, size, cur); + + return -EACCES; +} + +static const struct bpf_verifier_ops hid_bpf_verifier_ops = { + .get_func_proto = bpf_base_func_proto, + .is_valid_access = hid_bpf_ops_is_valid_access, + .btf_struct_access = hid_bpf_ops_btf_struct_access, +}; + +static int hid_bpf_ops_init_member(const struct btf_type *t, + const struct btf_member *member, + void *kdata, const void *udata) +{ + const struct hid_bpf_ops *uhid_bpf_ops; + struct hid_bpf_ops *khid_bpf_ops; + u32 moff; + + uhid_bpf_ops = (const struct hid_bpf_ops *)udata; + khid_bpf_ops = (struct hid_bpf_ops *)kdata; + + moff = __btf_member_bit_offset(t, member) / 8; + + switch (moff) { + case offsetof(struct hid_bpf_ops, hid_id): + /* For hid_id and flags fields, this function has to copy it + * and return 1 to indicate that the data has been handled by + * the struct_ops type, or the verifier will reject the map if + * the value of those fields is not zero. + */ + khid_bpf_ops->hid_id = uhid_bpf_ops->hid_id; + return 1; + case offsetof(struct hid_bpf_ops, flags): + if (uhid_bpf_ops->flags & ~BPF_F_BEFORE) + return -EINVAL; + khid_bpf_ops->flags = uhid_bpf_ops->flags; + return 1; + } + return 0; +} + +static int hid_bpf_reg(void *kdata, struct bpf_link *link) +{ + struct hid_bpf_ops *ops = kdata; + struct hid_device *hdev; + int count, err = 0; + + /* prevent multiple attach of the same struct_ops */ + if (ops->hdev) + return -EINVAL; + + hdev = hid_get_device(ops->hid_id); + if (IS_ERR(hdev)) + return PTR_ERR(hdev); + + ops->hdev = hdev; + + mutex_lock(&hdev->bpf.prog_list_lock); + + count = list_count_nodes(&hdev->bpf.prog_list); + if (count >= HID_BPF_MAX_PROGS_PER_DEV) { + err = -E2BIG; + goto out_unlock; + } + + if (ops->hid_rdesc_fixup) { + if (hdev->bpf.rdesc_ops) { + err = -EINVAL; + goto out_unlock; + } + + hdev->bpf.rdesc_ops = ops; + } + + if (ops->hid_device_event) { + err = hid_bpf_allocate_event_data(hdev); + if (err) + goto out_unlock; + } + + if (ops->flags & BPF_F_BEFORE) + list_add_rcu(&ops->list, &hdev->bpf.prog_list); + else + list_add_tail_rcu(&ops->list, &hdev->bpf.prog_list); + synchronize_srcu(&hdev->bpf.srcu); + +out_unlock: + mutex_unlock(&hdev->bpf.prog_list_lock); + + if (err) { + if (hdev->bpf.rdesc_ops == ops) + hdev->bpf.rdesc_ops = NULL; + hid_put_device(hdev); + } else if (ops->hid_rdesc_fixup) { + hid_bpf_reconnect(hdev); + } + + return err; +} + +static void hid_bpf_unreg(void *kdata, struct bpf_link *link) +{ + struct hid_bpf_ops *ops = kdata; + struct hid_device *hdev; + bool reconnect = false; + + hdev = ops->hdev; + + /* check if __hid_bpf_ops_destroy_device() has been called */ + if (!hdev) + return; + + mutex_lock(&hdev->bpf.prog_list_lock); + + list_del_rcu(&ops->list); + synchronize_srcu(&hdev->bpf.srcu); + ops->hdev = NULL; + + reconnect = hdev->bpf.rdesc_ops == ops; + if (reconnect) + hdev->bpf.rdesc_ops = NULL; + + mutex_unlock(&hdev->bpf.prog_list_lock); + + if (reconnect) + hid_bpf_reconnect(hdev); + + hid_put_device(hdev); +} + +static int __hid_bpf_device_event(struct hid_bpf_ctx *ctx, enum hid_report_type type, u64 source) +{ + return 0; +} + +static int __hid_bpf_rdesc_fixup(struct hid_bpf_ctx *ctx) +{ + return 0; +} + +static int __hid_bpf_hw_request(struct hid_bpf_ctx *ctx, unsigned char reportnum, + enum hid_report_type rtype, enum hid_class_request reqtype, + u64 source) +{ + return 0; +} + +static int __hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, u64 source) +{ + return 0; +} + +static struct hid_bpf_ops __bpf_hid_bpf_ops = { + .hid_device_event = __hid_bpf_device_event, + .hid_rdesc_fixup = __hid_bpf_rdesc_fixup, + .hid_hw_request = __hid_bpf_hw_request, + .hid_hw_output_report = __hid_bpf_hw_output_report, +}; + +static struct bpf_struct_ops bpf_hid_bpf_ops = { + .verifier_ops = &hid_bpf_verifier_ops, + .init = hid_bpf_ops_init, + .check_member = hid_bpf_ops_check_member, + .init_member = hid_bpf_ops_init_member, + .reg = hid_bpf_reg, + .unreg = hid_bpf_unreg, + .name = "hid_bpf_ops", + .cfi_stubs = &__bpf_hid_bpf_ops, + .owner = THIS_MODULE, +}; + +void __hid_bpf_ops_destroy_device(struct hid_device *hdev) +{ + struct hid_bpf_ops *e; + + rcu_read_lock(); + list_for_each_entry_rcu(e, &hdev->bpf.prog_list, list) { + hid_put_device(hdev); + e->hdev = NULL; + } + rcu_read_unlock(); +} + +static int __init hid_bpf_struct_ops_init(void) +{ + return register_bpf_struct_ops(&bpf_hid_bpf_ops, hid_bpf_ops); +} +late_initcall(hid_bpf_struct_ops_init); diff --git a/drivers/hid/bpf/progs/FR-TEC__Raptor-Mach-2.bpf.c b/drivers/hid/bpf/progs/FR-TEC__Raptor-Mach-2.bpf.c new file mode 100644 index 000000000000..caec91391d32 --- /dev/null +++ b/drivers/hid/bpf/progs/FR-TEC__Raptor-Mach-2.bpf.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2024 Benjamin Tissoires + */ + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include <bpf/bpf_tracing.h> + +#define VID_BETOP_2185PC 0x11C0 +#define PID_RAPTOR_MACH_2 0x5606 + +HID_BPF_CONFIG( + HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_BETOP_2185PC, PID_RAPTOR_MACH_2), +); + +/* + * For reference, this is the fixed report descriptor + * + * static const __u8 fixed_rdesc[] = { + * 0x05, 0x01, // Usage Page (Generic Desktop) 0 + * 0x09, 0x04, // Usage (Joystick) 2 + * 0xa1, 0x01, // Collection (Application) 4 + * 0x05, 0x01, // Usage Page (Generic Desktop) 6 + * 0x85, 0x01, // Report ID (1) 8 + * 0x05, 0x01, // Usage Page (Generic Desktop) 10 + * 0x09, 0x30, // Usage (X) 12 + * 0x75, 0x10, // Report Size (16) 14 + * 0x95, 0x01, // Report Count (1) 16 + * 0x15, 0x00, // Logical Minimum (0) 18 + * 0x26, 0xff, 0x07, // Logical Maximum (2047) 20 + * 0x46, 0xff, 0x07, // Physical Maximum (2047) 23 + * 0x81, 0x02, // Input (Data,Var,Abs) 26 + * 0x05, 0x01, // Usage Page (Generic Desktop) 28 + * 0x09, 0x31, // Usage (Y) 30 + * 0x75, 0x10, // Report Size (16) 32 + * 0x95, 0x01, // Report Count (1) 34 + * 0x15, 0x00, // Logical Minimum (0) 36 + * 0x26, 0xff, 0x07, // Logical Maximum (2047) 38 + * 0x46, 0xff, 0x07, // Physical Maximum (2047) 41 + * 0x81, 0x02, // Input (Data,Var,Abs) 44 + * 0x05, 0x01, // Usage Page (Generic Desktop) 46 + * 0x09, 0x33, // Usage (Rx) 48 + * 0x75, 0x10, // Report Size (16) 50 + * 0x95, 0x01, // Report Count (1) 52 + * 0x15, 0x00, // Logical Minimum (0) 54 + * 0x26, 0xff, 0x03, // Logical Maximum (1023) 56 + * 0x46, 0xff, 0x03, // Physical Maximum (1023) 59 + * 0x81, 0x02, // Input (Data,Var,Abs) 62 + * 0x05, 0x00, // Usage Page (Undefined) 64 + * 0x09, 0x00, // Usage (Undefined) 66 + * 0x75, 0x10, // Report Size (16) 68 + * 0x95, 0x01, // Report Count (1) 70 + * 0x15, 0x00, // Logical Minimum (0) 72 + * 0x26, 0xff, 0x03, // Logical Maximum (1023) 74 + * 0x46, 0xff, 0x03, // Physical Maximum (1023) 77 + * 0x81, 0x02, // Input (Data,Var,Abs) 80 + * 0x05, 0x01, // Usage Page (Generic Desktop) 82 + * 0x09, 0x32, // Usage (Z) 84 + * 0x75, 0x10, // Report Size (16) 86 + * 0x95, 0x01, // Report Count (1) 88 + * 0x15, 0x00, // Logical Minimum (0) 90 + * 0x26, 0xff, 0x03, // Logical Maximum (1023) 92 + * 0x46, 0xff, 0x03, // Physical Maximum (1023) 95 + * 0x81, 0x02, // Input (Data,Var,Abs) 98 + * 0x05, 0x01, // Usage Page (Generic Desktop) 100 + * 0x09, 0x35, // Usage (Rz) 102 + * 0x75, 0x10, // Report Size (16) 104 + * 0x95, 0x01, // Report Count (1) 106 + * 0x15, 0x00, // Logical Minimum (0) 108 + * 0x26, 0xff, 0x03, // Logical Maximum (1023) 110 + * 0x46, 0xff, 0x03, // Physical Maximum (1023) 113 + * 0x81, 0x02, // Input (Data,Var,Abs) 116 + * 0x05, 0x01, // Usage Page (Generic Desktop) 118 + * 0x09, 0x34, // Usage (Ry) 120 + * 0x75, 0x10, // Report Size (16) 122 + * 0x95, 0x01, // Report Count (1) 124 + * 0x15, 0x00, // Logical Minimum (0) 126 + * 0x26, 0xff, 0x07, // Logical Maximum (2047) 128 + * 0x46, 0xff, 0x07, // Physical Maximum (2047) 131 + * 0x81, 0x02, // Input (Data,Var,Abs) 134 + * 0x05, 0x01, // Usage Page (Generic Desktop) 136 + * 0x09, 0x36, // Usage (Slider) 138 + * 0x75, 0x10, // Report Size (16) 140 + * 0x95, 0x01, // Report Count (1) 142 + * 0x15, 0x00, // Logical Minimum (0) 144 + * 0x26, 0xff, 0x03, // Logical Maximum (1023) 146 + * 0x46, 0xff, 0x03, // Physical Maximum (1023) 149 + * 0x81, 0x02, // Input (Data,Var,Abs) 152 + * 0x05, 0x09, // Usage Page (Button) 154 + * 0x19, 0x01, // Usage Minimum (1) 156 + * 0x2a, 0x1d, 0x00, // Usage Maximum (29) 158 + * 0x15, 0x00, // Logical Minimum (0) 161 + * 0x25, 0x01, // Logical Maximum (1) 163 + * 0x75, 0x01, // Report Size (1) 165 + * 0x96, 0x80, 0x00, // Report Count (128) 167 + * 0x81, 0x02, // Input (Data,Var,Abs) 170 + * 0x05, 0x01, // Usage Page (Generic Desktop) 172 + * 0x09, 0x39, // Usage (Hat switch) 174 + * 0x26, 0x07, 0x00, // Logical Maximum (7) 176 // changed (was 239) + * 0x46, 0x68, 0x01, // Physical Maximum (360) 179 + * 0x65, 0x14, // Unit (EnglishRotation: deg) 182 + * 0x75, 0x10, // Report Size (16) 184 + * 0x95, 0x01, // Report Count (1) 186 + * 0x81, 0x42, // Input (Data,Var,Abs,Null) 188 + * 0x05, 0x01, // Usage Page (Generic Desktop) 190 + * 0x09, 0x00, // Usage (Undefined) 192 + * 0x75, 0x08, // Report Size (8) 194 + * 0x95, 0x1d, // Report Count (29) 196 + * 0x81, 0x01, // Input (Cnst,Arr,Abs) 198 + * 0x15, 0x00, // Logical Minimum (0) 200 + * 0x26, 0xef, 0x00, // Logical Maximum (239) 202 + * 0x85, 0x58, // Report ID (88) 205 + * 0x26, 0xff, 0x00, // Logical Maximum (255) 207 + * 0x46, 0xff, 0x00, // Physical Maximum (255) 210 + * 0x75, 0x08, // Report Size (8) 213 + * 0x95, 0x3f, // Report Count (63) 215 + * 0x09, 0x00, // Usage (Undefined) 217 + * 0x91, 0x02, // Output (Data,Var,Abs) 219 + * 0x85, 0x59, // Report ID (89) 221 + * 0x75, 0x08, // Report Size (8) 223 + * 0x95, 0x80, // Report Count (128) 225 + * 0x09, 0x00, // Usage (Undefined) 227 + * 0xb1, 0x02, // Feature (Data,Var,Abs) 229 + * 0xc0, // End Collection 231 + * }; + */ + +/* + * We need to amend the report descriptor for the following: + * - the joystick sends its hat_switch data between 0 and 239 but + * the kernel expects the logical max to stick into a signed 8 bits + * integer. We thus divide it by 30 to match what other joysticks are + * doing + */ +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(hid_fix_rdesc_raptor_mach_2, struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */); + + if (!data) + return 0; /* EPERM check */ + + data[177] = 0x07; + + return 0; +} + +/* + * The hat_switch value at offsets 33 and 34 (16 bits) needs + * to be reduced to a single 8 bit signed integer. So we + * divide it by 30. + * Byte 34 is always null, so it is ignored. + */ +SEC(HID_BPF_DEVICE_EVENT) +int BPF_PROG(raptor_mach_2_fix_hat_switch, struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 64 /* size */); + + if (!data) + return 0; /* EPERM check */ + + if (data[0] != 0x01) /* not the joystick report ID */ + return 0; + + data[33] /= 30; + + return 0; +} + +HID_BPF_OPS(raptor_mach_2) = { + .hid_rdesc_fixup = (void *)hid_fix_rdesc_raptor_mach_2, + .hid_device_event = (void *)raptor_mach_2_fix_hat_switch, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + ctx->retval = ctx->rdesc_size != 232; + if (ctx->retval) + ctx->retval = -EINVAL; + + /* ensure the kernel isn't fixed already */ + if (ctx->rdesc[177] != 0xef) /* Logical Max of 239 */ + ctx->retval = -EINVAL; + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/drivers/hid/bpf/progs/HP__Elite-Presenter.bpf.c b/drivers/hid/bpf/progs/HP__Elite-Presenter.bpf.c new file mode 100644 index 000000000000..c2413fa80543 --- /dev/null +++ b/drivers/hid/bpf/progs/HP__Elite-Presenter.bpf.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2023 Benjamin Tissoires + */ + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include <bpf/bpf_tracing.h> + +#define VID_HP 0x03F0 +#define PID_ELITE_PRESENTER 0x464A + +HID_BPF_CONFIG( + HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_GENERIC, VID_HP, PID_ELITE_PRESENTER) +); + +/* + * Already fixed as of commit 0db117359e47 ("HID: add quirk for 03f0:464a + * HP Elite Presenter Mouse") in the kernel, but this is a slightly better + * fix. + * + * The HP Elite Presenter Mouse HID Record Descriptor shows + * two mice (Report ID 0x1 and 0x2), one keypad (Report ID 0x5), + * two Consumer Controls (Report IDs 0x6 and 0x3). + * Prior to these fixes it registers one mouse, one keypad + * and one Consumer Control, and it was usable only as a + * digital laser pointer (one of the two mouses). + * We replace the second mouse collection with a pointer collection, + * allowing to use the device both as a mouse and a digital laser + * pointer. + */ + +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */); + + if (!data) + return 0; /* EPERM check */ + + /* replace application mouse by application pointer on the second collection */ + if (data[79] == 0x02) + data[79] = 0x01; + + return 0; +} + +HID_BPF_OPS(hp_elite_presenter) = { + .hid_rdesc_fixup = (void *)hid_fix_rdesc, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + ctx->retval = ctx->rdesc_size != 264; + if (ctx->retval) + ctx->retval = -EINVAL; + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/drivers/hid/bpf/progs/Huion__Dial-2.bpf.c b/drivers/hid/bpf/progs/Huion__Dial-2.bpf.c new file mode 100644 index 000000000000..9670e5ef8d54 --- /dev/null +++ b/drivers/hid/bpf/progs/Huion__Dial-2.bpf.c @@ -0,0 +1,636 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2024 Red Hat, Inc + */ + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include "hid_report_helpers.h" +#include <bpf/bpf_tracing.h> + +#define VID_HUION 0x256C +#define PID_DIAL_2 0x0060 + + +HID_BPF_CONFIG( + HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_HUION, PID_DIAL_2), +); + +/* Filled in by udev-hid-bpf */ +char UDEV_PROP_HUION_FIRMWARE_ID[64]; + +/* The prefix of the firmware ID we expect for this device. The full firmware + * string has a date suffix, e.g. HUION_T21j_221221 + */ +char EXPECTED_FIRMWARE_ID[] = "HUION_T216_"; + +/* How this BPF program works: the tablet has two modes, firmware mode and + * tablet mode. In firmware mode (out of the box) the tablet sends button events + * and the dial as keyboard combinations. In tablet mode it uses a vendor specific + * hid report to report everything instead. + * Depending on the mode some hid reports are never sent and the corresponding + * devices are mute. + * + * To switch the tablet use e.g. https://github.com/whot/huion-switcher + * or one of the tools from the digimend project + * + * This BPF works for both modes. The huion-switcher tool sets the + * HUION_FIRMWARE_ID udev property - if that is set then we disable the firmware + * pad and pen reports (by making them vendor collections that are ignored). + * If that property is not set we fix all hidraw nodes so the tablet works in + * either mode though the drawback is that the device will show up twice if + * you bind it to all event nodes + * + * Default report descriptor for the first exposed hidraw node: + * + * # HUION Huion Tablet_Q630M + * # 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 0 + * # 0x09, 0x01, // Usage (Vendor Usage 1) 3 + * # 0xa1, 0x01, // Collection (Application) 5 + * # 0x85, 0x08, // Report ID (8) 7 + * # 0x75, 0x58, // Report Size (88) 9 + * # 0x95, 0x01, // Report Count (1) 11 + * # 0x09, 0x01, // Usage (Vendor Usage 1) 13 + * # 0x81, 0x02, // Input (Data,Var,Abs) 15 + * # 0xc0, // End Collection 17 + * R: 18 06 00 ff 09 01 a1 01 85 08 75 58 95 01 09 01 81 02 c0 + * + * This rdesc does nothing until the tablet is switched to raw mode, see + * https://github.com/whot/huion-switcher + * + * + * Second hidraw node is the Pen. This one sends events until the tablet is + * switched to raw mode, then it's mute. + * + * # Report descriptor length: 93 bytes + * # HUION Huion Tablet_Q630M + * # 0x05, 0x0d, // Usage Page (Digitizers) 0 + * # 0x09, 0x02, // Usage (Pen) 2 + * # 0xa1, 0x01, // Collection (Application) 4 + * # 0x85, 0x0a, // Report ID (10) 6 + * # 0x09, 0x20, // Usage (Stylus) 8 + * # 0xa1, 0x01, // Collection (Application) 10 + * # 0x09, 0x42, // Usage (Tip Switch) 12 + * # 0x09, 0x44, // Usage (Barrel Switch) 14 + * # 0x09, 0x45, // Usage (Eraser) 16 + * # 0x09, 0x3c, // Usage (Invert) 18 + * # 0x15, 0x00, // Logical Minimum (0) 20 + * # 0x25, 0x01, // Logical Maximum (1) 22 + * # 0x75, 0x01, // Report Size (1) 24 + * # 0x95, 0x06, // Report Count (6) 26 + * # 0x81, 0x02, // Input (Data,Var,Abs) 28 + * # 0x09, 0x32, // Usage (In Range) 30 + * # 0x75, 0x01, // Report Size (1) 32 + * # 0x95, 0x01, // Report Count (1) 34 + * # 0x81, 0x02, // Input (Data,Var,Abs) 36 + * # 0x81, 0x03, // Input (Cnst,Var,Abs) 38 + * # 0x05, 0x01, // Usage Page (Generic Desktop) 40 + * # 0x09, 0x30, // Usage (X) 42 + * # 0x09, 0x31, // Usage (Y) 44 + * # 0x55, 0x0d, // Unit Exponent (-3) 46 + * # 0x65, 0x33, // Unit (EnglishLinear: in³) 48 + * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 50 + * # 0x35, 0x00, // Physical Minimum (0) 53 + * # 0x46, 0x00, 0x08, // Physical Maximum (2048) 55 + * # 0x75, 0x10, // Report Size (16) 58 + * # 0x95, 0x02, // Report Count (2) 60 + * # 0x81, 0x02, // Input (Data,Var,Abs) 62 + * # 0x05, 0x0d, // Usage Page (Digitizers) 64 + * # 0x09, 0x30, // Usage (Tip Pressure) 66 + * # 0x26, 0xff, 0x1f, // Logical Maximum (8191) 68 + * # 0x75, 0x10, // Report Size (16) 71 + * # 0x95, 0x01, // Report Count (1) 73 + * # 0x81, 0x02, // Input (Data,Var,Abs) 75 + * # 0x09, 0x3d, // Usage (X Tilt) 77 + * # 0x09, 0x3e, // Usage (Y Tilt) 79 + * # 0x15, 0x81, // Logical Minimum (-127) 81 + * # 0x25, 0x7f, // Logical Maximum (127) 83 + * # 0x75, 0x08, // Report Size (8) 85 + * # 0x95, 0x02, // Report Count (2) 87 + * # 0x81, 0x02, // Input (Data,Var,Abs) 89 + * # 0xc0, // End Collection 91 + * # 0xc0, // End Collection 92 + * R: 93 05 0d 09 02 a1 01 85 0a 09 20 a1 01 09 42 09 44 09 45 09 3c 15 00 25 01 75 01 95 06 81 02 09 32 75 01 95 01 81 02 81 03 05 01 09 30 09 31 55 0d 65 33 26 ff 7f 35 00 46 00 08 75 10 95 02 81 02 05 0d 09 30 26 ff 1f 75 10 95 01 81 02 09 3d 09 3e 15 81 25 7f 75 08 95 02 81 02 c0 c0 + * + * Third hidraw node is the pad which sends a combination of keyboard shortcuts until + * the tablet is switched to raw mode, then it's mute: + * + * # Report descriptor length: 148 bytes + * # HUION Huion Tablet_Q630M + * # 0x05, 0x01, // Usage Page (Generic Desktop) 0 + * # 0x09, 0x0e, // Usage (System Multi-Axis Controller) 2 + * # 0xa1, 0x01, // Collection (Application) 4 + * # 0x85, 0x11, // Report ID (17) 6 + * # 0x05, 0x0d, // Usage Page (Digitizers) 8 + * # 0x09, 0x21, // Usage (Puck) 10 + * # 0xa1, 0x02, // Collection (Logical) 12 + * # 0x15, 0x00, // Logical Minimum (0) 14 + * # 0x25, 0x01, // Logical Maximum (1) 16 + * # 0x75, 0x01, // Report Size (1) 18 + * # 0x95, 0x01, // Report Count (1) 20 + * # 0xa1, 0x00, // Collection (Physical) 22 + * # 0x05, 0x09, // Usage Page (Button) 24 + * # 0x09, 0x01, // Usage (Vendor Usage 0x01) 26 + * # 0x81, 0x02, // Input (Data,Var,Abs) 28 + * # 0x05, 0x0d, // Usage Page (Digitizers) 30 + * # 0x09, 0x33, // Usage (Touch) 32 + * # 0x81, 0x02, // Input (Data,Var,Abs) 34 + * # 0x95, 0x06, // Report Count (6) 36 + * # 0x81, 0x03, // Input (Cnst,Var,Abs) 38 + * # 0xa1, 0x02, // Collection (Logical) 40 + * # 0x05, 0x01, // Usage Page (Generic Desktop) 42 + * # 0x09, 0x37, // Usage (Dial) 44 + * # 0x16, 0x00, 0x80, // Logical Minimum (-32768) 46 + * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 49 + * # 0x75, 0x10, // Report Size (16) 52 + * # 0x95, 0x01, // Report Count (1) 54 + * # 0x81, 0x06, // Input (Data,Var,Rel) 56 + * # 0x35, 0x00, // Physical Minimum (0) 58 + * # 0x46, 0x10, 0x0e, // Physical Maximum (3600) 60 + * # 0x15, 0x00, // Logical Minimum (0) 63 + * # 0x26, 0x10, 0x0e, // Logical Maximum (3600) 65 + * # 0x09, 0x48, // Usage (Resolution Multiplier) 68 + * # 0xb1, 0x02, // Feature (Data,Var,Abs) 70 + * # 0x45, 0x00, // Physical Maximum (0) 72 + * # 0xc0, // End Collection 74 + * # 0x75, 0x08, // Report Size (8) 75 + * # 0x95, 0x01, // Report Count (1) 77 + * # 0x81, 0x01, // Input (Cnst,Arr,Abs) 79 + * # 0x75, 0x08, // Report Size (8) 81 + * # 0x95, 0x01, // Report Count (1) 83 + * # 0x81, 0x01, // Input (Cnst,Arr,Abs) 85 + * # 0x75, 0x08, // Report Size (8) 87 + * # 0x95, 0x01, // Report Count (1) 89 + * # 0x81, 0x01, // Input (Cnst,Arr,Abs) 91 + * # 0x75, 0x08, // Report Size (8) 93 + * # 0x95, 0x01, // Report Count (1) 95 + * # 0x81, 0x01, // Input (Cnst,Arr,Abs) 97 + * # 0x75, 0x08, // Report Size (8) 99 + * # 0x95, 0x01, // Report Count (1) 101 + * # 0x81, 0x01, // Input (Cnst,Arr,Abs) 103 + * # 0xc0, // End Collection 105 + * # 0xc0, // End Collection 106 + * # 0xc0, // End Collection 107 + * # 0x05, 0x01, // Usage Page (Generic Desktop) 108 + * # 0x09, 0x06, // Usage (Keyboard) 110 + * # 0xa1, 0x01, // Collection (Application) 112 + * # 0x85, 0x03, // Report ID (3) 114 + * # 0x05, 0x07, // Usage Page (Keyboard) 116 + * # 0x19, 0xe0, // Usage Minimum (224) 118 + * # 0x29, 0xe7, // Usage Maximum (231) 120 + * # 0x15, 0x00, // Logical Minimum (0) 122 + * # 0x25, 0x01, // Logical Maximum (1) 124 + * # 0x75, 0x01, // Report Size (1) 126 + * # 0x95, 0x08, // Report Count (8) 128 + * # 0x81, 0x02, // Input (Data,Var,Abs) 130 + * # 0x05, 0x07, // Usage Page (Keyboard) 132 + * # 0x19, 0x00, // Usage Minimum (0) 134 + * # 0x29, 0xff, // Usage Maximum (255) 136 + * # 0x26, 0xff, 0x00, // Logical Maximum (255) 138 + * # 0x75, 0x08, // Report Size (8) 141 + * # 0x95, 0x06, // Report Count (6) 143 + * # 0x81, 0x00, // Input (Data,Arr,Abs) 145 + * # 0xc0, // End Collection 147 + * R: 148 05 01 09 0e a1 01 85 11 05 0d 09 21 a1 02 15 00 25 01 75 01 95 01 a1 00 05 09 09 01 81 02 05 0d 09 33 81 02 95 06 81 03 a1 02 05 01 09 37 16 00 80 26 ff 7f 75 10 95 01 81 06 35 00 46 10 0e 15 00 26 10 0e 09 48 b1 02 45 00 c0 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 01 81 01 c0 c0 c0 05 01 09 06 a1 01 85 03 05 07 19 e0 29 e7 15 00 25 01 75 01 95 08 81 02 05 07 19 00 29 ff 26 ff 00 75 08 95 06 81 00 c0 + */ + +#define PAD_REPORT_DESCRIPTOR_LENGTH 148 +#define PEN_REPORT_DESCRIPTOR_LENGTH 93 +#define VENDOR_REPORT_DESCRIPTOR_LENGTH 18 +#define PAD_REPORT_ID 3 +#define DIAL_REPORT_ID 17 +#define PEN_REPORT_ID 10 +#define VENDOR_REPORT_ID 8 +#define PAD_REPORT_LENGTH 9 +#define PEN_REPORT_LENGTH 10 +#define VENDOR_REPORT_LENGTH 12 + + +__u8 last_button_state; + +static const __u8 fixed_rdesc_pad[] = { + UsagePage_GenericDesktop + Usage_GD_Keypad + CollectionApplication( + // -- Byte 0 in report + ReportId(PAD_REPORT_ID) + LogicalMaximum_i8(0) + LogicalMaximum_i8(1) + UsagePage_Digitizers + Usage_Dig_TabletFunctionKeys + CollectionPhysical( + // Byte 1 in report - just exists so we get to be a tablet pad + Usage_Dig_BarrelSwitch // BTN_STYLUS + ReportCount(1) + ReportSize(1) + Input(Var|Abs) + ReportCount(7) // padding + Input(Const) + // Bytes 2/3 in report - just exists so we get to be a tablet pad + UsagePage_GenericDesktop + Usage_GD_X + Usage_GD_Y + ReportCount(2) + ReportSize(8) + Input(Var|Abs) + // Byte 4 in report is the dial + Usage_GD_Wheel + LogicalMinimum_i8(-1) + LogicalMaximum_i8(1) + ReportCount(1) + ReportSize(8) + Input(Var|Rel) + // Byte 5 is the button state + UsagePage_Button + UsageMinimum_i8(0x01) + UsageMaximum_i8(0x08) + LogicalMinimum_i8(0x0) + LogicalMaximum_i8(0x1) + ReportCount(7) + ReportSize(1) + Input(Var|Abs) + ReportCount(1) // padding + Input(Const) + ) + // Make sure we match our original report length + FixedSizeVendorReport(PAD_REPORT_LENGTH) + ) +}; + +static const __u8 fixed_rdesc_pen[] = { + UsagePage_Digitizers + Usage_Dig_Pen + CollectionApplication( + // -- Byte 0 in report + ReportId(PEN_REPORT_ID) + Usage_Dig_Pen + CollectionPhysical( + // -- Byte 1 in report + Usage_Dig_TipSwitch + Usage_Dig_BarrelSwitch + Usage_Dig_SecondaryBarrelSwitch // maps eraser to BTN_STYLUS2 + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) + ReportSize(1) + ReportCount(3) + Input(Var|Abs) + ReportCount(4) // Padding + Input(Const) + Usage_Dig_InRange + ReportCount(1) + Input(Var|Abs) + ReportSize(16) + ReportCount(1) + PushPop( + UsagePage_GenericDesktop + Unit(cm) + UnitExponent(-1) + PhysicalMinimum_i16(0) + PhysicalMaximum_i16(266) + LogicalMinimum_i16(0) + LogicalMaximum_i16(32767) + Usage_GD_X + Input(Var|Abs) // Bytes 2+3 + PhysicalMinimum_i16(0) + PhysicalMaximum_i16(166) + LogicalMinimum_i16(0) + LogicalMaximum_i16(32767) + Usage_GD_Y + Input(Var|Abs) // Bytes 4+5 + ) + UsagePage_Digitizers + Usage_Dig_TipPressure + LogicalMinimum_i16(0) + LogicalMaximum_i16(8191) + Input(Var|Abs) // Byte 6+7 + ReportSize(8) + ReportCount(2) + LogicalMinimum_i8(-60) + LogicalMaximum_i8(60) + Usage_Dig_XTilt + Usage_Dig_YTilt + Input(Var|Abs) // Byte 8+9 + ) + ) +}; + +static const __u8 fixed_rdesc_vendor[] = { + UsagePage_Digitizers + Usage_Dig_Pen + CollectionApplication( + // Byte 0 + // We leave the pen on the vendor report ID + ReportId(VENDOR_REPORT_ID) + Usage_Dig_Pen + CollectionPhysical( + // Byte 1 are the buttons + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) + ReportSize(1) + Usage_Dig_TipSwitch + Usage_Dig_BarrelSwitch + Usage_Dig_SecondaryBarrelSwitch + ReportCount(3) + Input(Var|Abs) + ReportCount(4) // Padding + Input(Const) + Usage_Dig_InRange + ReportCount(1) + Input(Var|Abs) + ReportSize(16) + ReportCount(1) + PushPop( + UsagePage_GenericDesktop + Unit(cm) + UnitExponent(-1) + // Note: reported logical range differs + // from the pen report ID for x and y + LogicalMinimum_i16(0) + LogicalMaximum_i16(53340) + PhysicalMinimum_i16(0) + PhysicalMaximum_i16(266) + // Bytes 2/3 in report + Usage_GD_X + Input(Var|Abs) + LogicalMinimum_i16(0) + LogicalMaximum_i16(33340) + PhysicalMinimum_i16(0) + PhysicalMaximum_i16(166) + // Bytes 4/5 in report + Usage_GD_Y + Input(Var|Abs) + ) + // Bytes 6/7 in report + LogicalMinimum_i16(0) + LogicalMaximum_i16(8191) + Usage_Dig_TipPressure + Input(Var|Abs) + // Bytes 8/9 in report + ReportCount(1) // Padding + Input(Const) + LogicalMinimum_i8(-60) + LogicalMaximum_i8(60) + // Byte 10 in report + Usage_Dig_XTilt + // Byte 11 in report + Usage_Dig_YTilt + ReportSize(8) + ReportCount(2) + Input(Var|Abs) + ) + ) + UsagePage_GenericDesktop + Usage_GD_Keypad + CollectionApplication( + // Byte 0 + ReportId(PAD_REPORT_ID) + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) + UsagePage_Digitizers + Usage_Dig_TabletFunctionKeys + CollectionPhysical( + // Byte 1 are the buttons + Usage_Dig_BarrelSwitch // BTN_STYLUS, needed so we get to be a tablet pad + ReportCount(1) + ReportSize(1) + Input(Var|Abs) + ReportCount(7) // Padding + Input(Const) + // Bytes 2/3 - x/y just exist so we get to be a tablet pad + UsagePage_GenericDesktop + Usage_GD_X + Usage_GD_Y + ReportCount(2) + ReportSize(8) + Input(Var|Abs) + // Byte 4 is the button state + UsagePage_Button + UsageMinimum_i8(0x1) + UsageMaximum_i8(0x8) + LogicalMinimum_i8(0x0) + LogicalMaximum_i8(0x1) + ReportCount(8) + ReportSize(1) + Input(Var|Abs) + // Byte 5 is the top dial + UsagePage_GenericDesktop + Usage_GD_Wheel + LogicalMinimum_i8(-1) + LogicalMaximum_i8(1) + ReportCount(1) + ReportSize(8) + Input(Var|Rel) + // Byte 6 is the bottom dial + UsagePage_Consumer + Usage_Con_ACPan + Input(Var|Rel) + ) + // Make sure we match our original report length + FixedSizeVendorReport(VENDOR_REPORT_LENGTH) + ) +}; + +static const __u8 disabled_rdesc_pen[] = { + FixedSizeVendorReport(PEN_REPORT_LENGTH) +}; + +static const __u8 disabled_rdesc_pad[] = { + FixedSizeVendorReport(PAD_REPORT_LENGTH) +}; + +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(dial_2_fix_rdesc, struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */); + __s32 rdesc_size = hctx->size; + __u8 have_fw_id; + + if (!data) + return 0; /* EPERM check */ + + /* If we have a firmware ID and it matches our expected prefix, we + * disable the default pad/pen nodes. They won't send events + * but cause duplicate devices. + */ + have_fw_id = __builtin_memcmp(UDEV_PROP_HUION_FIRMWARE_ID, + EXPECTED_FIRMWARE_ID, + sizeof(EXPECTED_FIRMWARE_ID) - 1) == 0; + if (rdesc_size == PAD_REPORT_DESCRIPTOR_LENGTH) { + if (have_fw_id) { + __builtin_memcpy(data, disabled_rdesc_pad, sizeof(disabled_rdesc_pad)); + return sizeof(disabled_rdesc_pad); + } + + __builtin_memcpy(data, fixed_rdesc_pad, sizeof(fixed_rdesc_pad)); + return sizeof(fixed_rdesc_pad); + } + if (rdesc_size == PEN_REPORT_DESCRIPTOR_LENGTH) { + if (have_fw_id) { + __builtin_memcpy(data, disabled_rdesc_pen, sizeof(disabled_rdesc_pen)); + return sizeof(disabled_rdesc_pen); + } + + __builtin_memcpy(data, fixed_rdesc_pen, sizeof(fixed_rdesc_pen)); + return sizeof(fixed_rdesc_pen); + } + /* Always fix the vendor mode so the tablet will work even if nothing sets + * the udev property (e.g. huion-switcher run manually) + */ + if (rdesc_size == VENDOR_REPORT_DESCRIPTOR_LENGTH) { + __builtin_memcpy(data, fixed_rdesc_vendor, sizeof(fixed_rdesc_vendor)); + return sizeof(fixed_rdesc_vendor); + + } + return 0; +} + +SEC(HID_BPF_DEVICE_EVENT) +int BPF_PROG(dial_2_fix_events, struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 16 /* size */); + static __u8 button; + + if (!data) + return 0; /* EPERM check */ + + /* Only sent if tablet is in default mode */ + if (data[0] == PAD_REPORT_ID) { + /* Nicely enough, this device only supports one button down at a time so + * the reports are easy to match. Buttons numbered from the top + * Button released: 03 00 00 00 00 00 00 00 + * Button 1: 03 00 05 00 00 00 00 00 -> b + * Button 2: 03 00 08 00 00 00 00 00 -> e + * Button 3: 03 00 0c 00 00 00 00 00 -> i + * Button 4: 03 00 e0 16 00 00 00 00 -> Ctrl S + * Button 5: 03 00 2c 00 00 00 00 00 -> space + * Button 6: 03 00 e0 e2 1d 00 00 00 -> Ctrl Alt Z + */ + button &= 0xc0; + + switch ((data[2] << 16) | (data[3] << 8) | data[4]) { + case 0x000000: + break; + case 0x050000: + button |= BIT(0); + break; + case 0x080000: + button |= BIT(1); + break; + case 0x0c0000: + button |= BIT(2); + break; + case 0xe01600: + button |= BIT(3); + break; + case 0x2c0000: + button |= BIT(4); + break; + case 0xe0e21d: + button |= BIT(5); + break; + } + + __u8 report[8] = {PAD_REPORT_ID, 0x0, 0x0, 0x0, 0x00, button}; + + __builtin_memcpy(data, report, sizeof(report)); + return sizeof(report); + } + + /* Only sent if tablet is in default mode */ + if (data[0] == DIAL_REPORT_ID) { + /* + * In default mode, both dials are merged together: + * + * Dial down: 11 00 ff ff 00 00 00 00 00 -> Dial -1 + * Dial up: 11 00 01 00 00 00 00 00 00 -> Dial 1 + */ + __u16 dial = data[3] << 8 | data[2]; + + button &= 0x3f; + button |= !!data[1] << 6; + + __u8 report[] = {PAD_REPORT_ID, 0x0, 0x0, 0x0, dial, button}; + + __builtin_memcpy(data, report, sizeof(report)); + return sizeof(report); + } + + /* Nothing to do for the PEN_REPORT_ID, it's already mapped */ + + /* Only sent if tablet is in raw mode */ + if (data[0] == VENDOR_REPORT_ID) { + /* Pad reports */ + if (data[1] & 0x20) { + /* See fixed_rdesc_pad */ + struct pad_report { + __u8 report_id; + __u8 btn_stylus; + __u8 x; + __u8 y; + __u8 buttons; + __u8 dial_1; + __u8 dial_2; + } __attribute__((packed)) *pad_report; + __u8 dial_1 = 0, dial_2 = 0; + + /* Dial report */ + if (data[1] == 0xf1) { + __u8 d = 0; + + if (data[5] == 2) + d = 0xff; + else + d = data[5]; + + if (data[3] == 1) + dial_1 = d; + else + dial_2 = d; + } else { + /* data[4] are the buttons, mapped correctly */ + last_button_state = data[4]; + dial_1 = 0; // dial + dial_2 = 0; + } + + pad_report = (struct pad_report *)data; + + pad_report->report_id = PAD_REPORT_ID; + pad_report->btn_stylus = 0; + pad_report->x = 0; + pad_report->y = 0; + pad_report->buttons = last_button_state; + pad_report->dial_1 = dial_1; + pad_report->dial_2 = dial_2; + + return sizeof(struct pad_report); + } + + /* Pen reports need nothing done */ + } + + return 0; +} + +HID_BPF_OPS(inspiroy_dial2) = { + .hid_device_event = (void *)dial_2_fix_events, + .hid_rdesc_fixup = (void *)dial_2_fix_rdesc, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + switch (ctx->rdesc_size) { + case PAD_REPORT_DESCRIPTOR_LENGTH: + case PEN_REPORT_DESCRIPTOR_LENGTH: + case VENDOR_REPORT_DESCRIPTOR_LENGTH: + ctx->retval = 0; + break; + default: + ctx->retval = -EINVAL; + } + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/drivers/hid/bpf/progs/Huion__Inspiroy-2-S.bpf.c b/drivers/hid/bpf/progs/Huion__Inspiroy-2-S.bpf.c new file mode 100644 index 000000000000..13f64fb49800 --- /dev/null +++ b/drivers/hid/bpf/progs/Huion__Inspiroy-2-S.bpf.c @@ -0,0 +1,554 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2024 Red Hat, Inc + */ + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include "hid_report_helpers.h" +#include <bpf/bpf_tracing.h> + +#define VID_HUION 0x256C +#define PID_INSPIROY_2_S 0x0066 + +HID_BPF_CONFIG( + HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_HUION, PID_INSPIROY_2_S), +); + +/* Filled in by udev-hid-bpf */ +char UDEV_PROP_HUION_FIRMWARE_ID[64]; + +/* The prefix of the firmware ID we expect for this device. The full firmware + * string has a date suffix, e.g. HUION_T21j_221221 + */ +char EXPECTED_FIRMWARE_ID[] = "HUION_T21j_"; + +/* How this BPF program works: the tablet has two modes, firmware mode and + * tablet mode. In firmware mode (out of the box) the tablet sends button events + * and the dial as keyboard combinations. In tablet mode it uses a vendor specific + * hid report to report everything instead. + * Depending on the mode some hid reports are never sent and the corresponding + * devices are mute. + * + * To switch the tablet use e.g. https://github.com/whot/huion-switcher + * or one of the tools from the digimend project + * + * This BPF works for both modes. The huion-switcher tool sets the + * HUION_FIRMWARE_ID udev property - if that is set then we disable the firmware + * pad and pen reports (by making them vendor collections that are ignored). + * If that property is not set we fix all hidraw nodes so the tablet works in + * either mode though the drawback is that the device will show up twice if + * you bind it to all event nodes + * + * Default report descriptor for the first exposed hidraw node: + * + * # HUION Huion Tablet_H641P + * # Report descriptor length: 18 bytes + * # 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 0xFF00) 0 + * # 0x09, 0x01, // Usage (Vendor Usage 0x01) 3 + * # 0xa1, 0x01, // Collection (Application) 5 + * # 0x85, 0x08, // Report ID (8) 7 + * # 0x75, 0x58, // Report Size (88) 9 + * # 0x95, 0x01, // Report Count (1) 11 + * # 0x09, 0x01, // Usage (Vendor Usage 0x01) 13 + * # 0x81, 0x02, // Input (Data,Var,Abs) 15 + * # 0xc0, // End Collection 17 + * R: 18 06 00 ff 09 01 a1 01 85 08 75 58 95 01 09 01 81 02 c0 + * + * This rdesc does nothing until the tablet is switched to raw mode, see + * https://github.com/whot/huion-switcher + * + * + * Second hidraw node is the Pen. This one sends events until the tablet is + * switched to raw mode, then it's mute. + * + * # Report descriptor length: 93 bytes + * # 0x05, 0x0d, // Usage Page (Digitizers) 0 + * # 0x09, 0x02, // Usage (Pen) 2 + * # 0xa1, 0x01, // Collection (Application) 4 + * # 0x85, 0x0a, // Report ID (10) 6 + * # 0x09, 0x20, // Usage (Stylus) 8 + * # 0xa1, 0x01, // Collection (Application) 10 + * # 0x09, 0x42, // Usage (Tip Switch) 12 + * # 0x09, 0x44, // Usage (Barrel Switch) 14 + * # 0x09, 0x45, // Usage (Eraser) 16 + * # 0x09, 0x3c, // Usage (Invert) 18 <-- has no Invert eraser + * # 0x15, 0x00, // Logical Minimum (0) 20 + * # 0x25, 0x01, // Logical Maximum (1) 22 + * # 0x75, 0x01, // Report Size (1) 24 + * # 0x95, 0x06, // Report Count (6) 26 + * # 0x81, 0x02, // Input (Data,Var,Abs) 28 + * # 0x09, 0x32, // Usage (In Range) 30 + * # 0x75, 0x01, // Report Size (1) 32 + * # 0x95, 0x01, // Report Count (1) 34 + * # 0x81, 0x02, // Input (Data,Var,Abs) 36 + * # 0x81, 0x03, // Input (Cnst,Var,Abs) 38 + * # 0x05, 0x01, // Usage Page (Generic Desktop) 40 + * # 0x09, 0x30, // Usage (X) 42 + * # 0x09, 0x31, // Usage (Y) 44 + * # 0x55, 0x0d, // Unit Exponent (-3) 46 <-- change to -2 + * # 0x65, 0x33, // Unit (EnglishLinear: in³) 48 <-- change in³ to in + * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 50 + * # 0x35, 0x00, // Physical Minimum (0) 53 + * # 0x46, 0x00, 0x08, // Physical Maximum (2048) 55 <-- invalid size + * # 0x75, 0x10, // Report Size (16) 58 + * # 0x95, 0x02, // Report Count (2) 60 + * # 0x81, 0x02, // Input (Data,Var,Abs) 62 + * # 0x05, 0x0d, // Usage Page (Digitizers) 64 + * # 0x09, 0x30, // Usage (Tip Pressure) 66 + * # 0x26, 0xff, 0x1f, // Logical Maximum (8191) 68 + * # 0x75, 0x10, // Report Size (16) 71 + * # 0x95, 0x01, // Report Count (1) 73 + * # 0x81, 0x02, // Input (Data,Var,Abs) 75 + * # 0x09, 0x3d, // Usage (X Tilt) 77 <-- No tilt reported + * # 0x09, 0x3e, // Usage (Y Tilt) 79 + * # 0x15, 0x81, // Logical Minimum (-127) 81 + * # 0x25, 0x7f, // Logical Maximum (127) 83 + * # 0x75, 0x08, // Report Size (8) 85 + * # 0x95, 0x02, // Report Count (2) 87 + * # 0x81, 0x02, // Input (Data,Var,Abs) 89 + * # 0xc0, // End Collection 91 + * # 0xc0, // End Collection 92 + * R: 93 05 0d 09 02 a1 01 85 0a 09 20 a1 01 09 42 09 44 09 45 09 3c 15 00 25 01 7501 95 06 81 02 09 32 75 01 95 01 81 02 81 03 05 01 09 30 09 31 55 0d 65 33 26 ff7f 35 00 46 00 08 75 10 95 02 81 02 05 0d 09 30 26 ff 1f 75 10 95 01 81 02 09 3d09 3e 15 81 25 7f 75 08 95 02 81 02 c0 c0 + * + * Third hidraw node is the pad which sends a combination of keyboard shortcuts until + * the tablet is switched to raw mode, then it's mute: + * + * # Report descriptor length: 65 bytes + * # 0x05, 0x01, // Usage Page (Generic Desktop) 0 + * # 0x09, 0x06, // Usage (Keyboard) 2 + * # 0xa1, 0x01, // Collection (Application) 4 + * # 0x85, 0x03, // Report ID (3) 6 + * # 0x05, 0x07, // Usage Page (Keyboard/Keypad) 8 + * # 0x19, 0xe0, // UsageMinimum (224) 10 + * # 0x29, 0xe7, // UsageMaximum (231) 12 + * # 0x15, 0x00, // Logical Minimum (0) 14 + * # 0x25, 0x01, // Logical Maximum (1) 16 + * # 0x75, 0x01, // Report Size (1) 18 + * # 0x95, 0x08, // Report Count (8) 20 + * # 0x81, 0x02, // Input (Data,Var,Abs) 22 + * # 0x05, 0x07, // Usage Page (Keyboard/Keypad) 24 + * # 0x19, 0x00, // UsageMinimum (0) 26 + * # 0x29, 0xff, // UsageMaximum (255) 28 + * # 0x26, 0xff, 0x00, // Logical Maximum (255) 30 + * # 0x75, 0x08, // Report Size (8) 33 + * # 0x95, 0x06, // Report Count (6) 35 + * # 0x81, 0x00, // Input (Data,Arr,Abs) 37 + * # 0xc0, // End Collection 39 + * # 0x05, 0x0c, // Usage Page (Consumer) 40 + * # 0x09, 0x01, // Usage (Consumer Control) 42 + * # 0xa1, 0x01, // Collection (Application) 44 + * # 0x85, 0x04, // Report ID (4) 46 + * # 0x19, 0x00, // UsageMinimum (0) 48 + * # 0x2a, 0x3c, 0x02, // UsageMaximum (572) 50 + * # 0x15, 0x00, // Logical Minimum (0) 53 + * # 0x26, 0x3c, 0x02, // Logical Maximum (572) 55 + * # 0x95, 0x01, // Report Count (1) 58 + * # 0x75, 0x10, // Report Size (16) 60 + * # 0x81, 0x00, // Input (Data,Arr,Abs) 62 + * # 0xc0, // End Collection 64 + * R: 65 05 01 09 06 a1 01 85 03 05 07 19 e0 29 e7 15 00 25 01 75 01 95 08 81 02 0507 19 00 29 ff 26 ff 00 75 08 95 06 81 00 c0 05 0c 09 01 a1 01 85 04 19 00 2a 3c02 15 00 26 3c 02 95 01 75 10 81 00 c0 + * N: HUION Huion Tablet_H641P + */ + +#define PAD_REPORT_DESCRIPTOR_LENGTH 65 +#define PEN_REPORT_DESCRIPTOR_LENGTH 93 +#define VENDOR_REPORT_DESCRIPTOR_LENGTH 18 +#define PAD_REPORT_ID 3 +#define PEN_REPORT_ID 10 +#define VENDOR_REPORT_ID 8 +#define PAD_REPORT_LENGTH 8 +#define PEN_REPORT_LENGTH 10 +#define VENDOR_REPORT_LENGTH 12 + + +__u8 last_button_state; + +static const __u8 fixed_rdesc_pad[] = { + UsagePage_GenericDesktop + Usage_GD_Keypad + CollectionApplication( + // -- Byte 0 in report + ReportId(PAD_REPORT_ID) + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) + UsagePage_Digitizers + Usage_Dig_TabletFunctionKeys + CollectionPhysical( + // Byte 1 in report - just exists so we get to be a tablet pad + Usage_Dig_BarrelSwitch // BTN_STYLUS + ReportCount(1) + ReportSize(1) + Input(Var|Abs) + ReportCount(7) // padding + Input(Const) + // Bytes 2/3 in report - just exists so we get to be a tablet pad + UsagePage_GenericDesktop + Usage_GD_X + Usage_GD_Y + ReportCount(2) + ReportSize(8) + Input(Var|Abs) + // Byte 4 in report is the wheel + Usage_GD_Wheel + LogicalMinimum_i8(-1) + LogicalMaximum_i8(1) + ReportCount(1) + ReportSize(8) + Input(Var|Rel) + // Byte 5 is the button state + UsagePage_Button + UsageMinimum_i8(0x1) + UsageMaximum_i8(0x6) + LogicalMinimum_i8(0x1) + LogicalMaximum_i8(0x6) + ReportCount(1) + ReportSize(8) + Input(Arr|Abs) + ) + // Make sure we match our original report length + FixedSizeVendorReport(PAD_REPORT_LENGTH) + ) +}; + +static const __u8 fixed_rdesc_pen[] = { + UsagePage_Digitizers + Usage_Dig_Pen + CollectionApplication( + // -- Byte 0 in report + ReportId(PEN_REPORT_ID) + Usage_Dig_Pen + CollectionPhysical( + // -- Byte 1 in report + Usage_Dig_TipSwitch + Usage_Dig_BarrelSwitch + Usage_Dig_SecondaryBarrelSwitch // maps eraser to BTN_STYLUS2 + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) + ReportSize(1) + ReportCount(3) + Input(Var|Abs) + ReportCount(4) // Padding + Input(Const) + Usage_Dig_InRange + ReportCount(1) + Input(Var|Abs) + ReportSize(16) + ReportCount(1) + PushPop( + UsagePage_GenericDesktop + Unit(cm) + UnitExponent(-1) + PhysicalMinimum_i16(0) + PhysicalMaximum_i16(160) + LogicalMinimum_i16(0) + LogicalMaximum_i16(32767) + Usage_GD_X + Input(Var|Abs) // Bytes 2+3 + PhysicalMinimum_i16(0) + PhysicalMaximum_i16(100) + LogicalMinimum_i16(0) + LogicalMaximum_i16(32767) + Usage_GD_Y + Input(Var|Abs) // Bytes 4+5 + ) + UsagePage_Digitizers + Usage_Dig_TipPressure + LogicalMinimum_i16(0) + LogicalMaximum_i16(8191) + Input(Var|Abs) // Byte 6+7 + // Two bytes padding so we don't need to change the report at all + ReportSize(8) + ReportCount(2) + Input(Const) // Byte 6+7 + ) + ) +}; + +static const __u8 fixed_rdesc_vendor[] = { + UsagePage_Digitizers + Usage_Dig_Pen + CollectionApplication( + // Byte 0 + // We leave the pen on the vendor report ID + ReportId(VENDOR_REPORT_ID) + Usage_Dig_Pen + CollectionPhysical( + // Byte 1 are the buttons + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) + ReportSize(1) + Usage_Dig_TipSwitch + Usage_Dig_BarrelSwitch + Usage_Dig_SecondaryBarrelSwitch + ReportCount(3) + Input(Var|Abs) + ReportCount(4) // Padding + Input(Const) + Usage_Dig_InRange + ReportCount(1) + Input(Var|Abs) + ReportSize(16) + ReportCount(1) + PushPop( + UsagePage_GenericDesktop + Unit(cm) + UnitExponent(-1) + // Note: reported logical range differs + // from the pen report ID for x and y + LogicalMinimum_i16(0) + LogicalMaximum_i16(32000) + PhysicalMinimum_i16(0) + PhysicalMaximum_i16(160) + // Bytes 2/3 in report + Usage_GD_X + Input(Var|Abs) + LogicalMinimum_i16(0) + LogicalMaximum_i16(20000) + PhysicalMinimum_i16(0) + PhysicalMaximum_i16(100) + // Bytes 4/5 in report + Usage_GD_Y + Input(Var|Abs) + ) + // Bytes 6/7 in report + LogicalMinimum_i16(0) + LogicalMaximum_i16(8192) + Usage_Dig_TipPressure + Input(Var|Abs) + ) + ) + UsagePage_GenericDesktop + Usage_GD_Keypad + CollectionApplication( + // Byte 0 + ReportId(PAD_REPORT_ID) + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) + UsagePage_Digitizers + Usage_Dig_TabletFunctionKeys + CollectionPhysical( + // Byte 1 are the buttons + Usage_Dig_BarrelSwitch // BTN_STYLUS, needed so we get to be a tablet pad + ReportCount(1) + ReportSize(1) + Input(Var|Abs) + ReportCount(7) // Padding + Input(Const) + // Bytes 2/3 - x/y just exist so we get to be a tablet pad + UsagePage_GenericDesktop + Usage_GD_X + Usage_GD_Y + ReportCount(2) + ReportSize(8) + Input(Var|Abs) + // Byte 4 is the button state + UsagePage_Button + UsageMinimum_i8(0x1) + UsageMaximum_i8(0x6) + LogicalMinimum_i8(0x0) + LogicalMaximum_i8(0x1) + ReportCount(6) + ReportSize(1) + Input(Var|Abs) + ReportCount(2) + Input(Const) + // Byte 5 is the wheel + UsagePage_GenericDesktop + Usage_GD_Wheel + LogicalMinimum_i8(-1) + LogicalMaximum_i8(1) + ReportCount(1) + ReportSize(8) + Input(Var|Rel) + ) + // Make sure we match our original report length + FixedSizeVendorReport(VENDOR_REPORT_LENGTH) + ) +}; + +static const __u8 disabled_rdesc_pen[] = { + FixedSizeVendorReport(PEN_REPORT_LENGTH) +}; + +static const __u8 disabled_rdesc_pad[] = { + FixedSizeVendorReport(PAD_REPORT_LENGTH) +}; + +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */); + __s32 rdesc_size = hctx->size; + __u8 have_fw_id; + + if (!data) + return 0; /* EPERM check */ + + /* If we have a firmware ID and it matches our expected prefix, we + * disable the default pad/pen nodes. They won't send events + * but cause duplicate devices. + */ + have_fw_id = __builtin_memcmp(UDEV_PROP_HUION_FIRMWARE_ID, + EXPECTED_FIRMWARE_ID, + sizeof(EXPECTED_FIRMWARE_ID) - 1) == 0; + if (rdesc_size == PAD_REPORT_DESCRIPTOR_LENGTH) { + if (have_fw_id) { + __builtin_memcpy(data, disabled_rdesc_pad, sizeof(disabled_rdesc_pad)); + return sizeof(disabled_rdesc_pad); + } + + __builtin_memcpy(data, fixed_rdesc_pad, sizeof(fixed_rdesc_pad)); + return sizeof(fixed_rdesc_pad); + } + if (rdesc_size == PEN_REPORT_DESCRIPTOR_LENGTH) { + if (have_fw_id) { + __builtin_memcpy(data, disabled_rdesc_pen, sizeof(disabled_rdesc_pen)); + return sizeof(disabled_rdesc_pen); + } + + __builtin_memcpy(data, fixed_rdesc_pen, sizeof(fixed_rdesc_pen)); + return sizeof(fixed_rdesc_pen); + } + /* Always fix the vendor mode so the tablet will work even if nothing sets + * the udev property (e.g. huion-switcher run manually) + */ + if (rdesc_size == VENDOR_REPORT_DESCRIPTOR_LENGTH) { + __builtin_memcpy(data, fixed_rdesc_vendor, sizeof(fixed_rdesc_vendor)); + return sizeof(fixed_rdesc_vendor); + + } + return 0; +} + +SEC(HID_BPF_DEVICE_EVENT) +int BPF_PROG(inspiroy_2_fix_events, struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */); + + if (!data) + return 0; /* EPERM check */ + + /* Only sent if tablet is in default mode */ + if (data[0] == PAD_REPORT_ID) { + /* Nicely enough, this device only supports one button down at a time so + * the reports are easy to match. Buttons numbered from the top + * Button released: 03 00 00 00 00 00 00 00 + * Button 1: 03 00 05 00 00 00 00 00 -> b + * Button 2: 03 00 0c 00 00 00 00 00 -> i + * Button 3: 03 00 08 00 00 00 00 00 -> e + * Button 4: 03 01 16 00 00 00 00 00 -> Ctrl S + * Button 5: 03 00 2c 00 00 00 00 00 -> space + * Button 6: 03 05 1d 00 00 00 00 00 -> Ctrl Alt Z + * + * Wheel down: 03 01 2d 00 00 00 00 00 -> Ctrl - + * Wheel up: 03 01 2e 00 00 00 00 00 -> Ctrl = + */ + __u8 button = 0; + __u8 wheel = 0; + + switch (data[1] << 8 | data[2]) { + case 0x0000: + break; + case 0x0005: + button = 1; + break; + case 0x000c: + button = 2; + break; + case 0x0008: + button = 3; + break; + case 0x0116: + button = 4; + break; + case 0x002c: + button = 5; + break; + case 0x051d: + button = 6; + break; + case 0x012d: + wheel = -1; + break; + case 0x012e: + wheel = 1; + break; + + } + + __u8 report[6] = {PAD_REPORT_ID, 0x0, 0x0, 0x0, wheel, button}; + + __builtin_memcpy(data, report, sizeof(report)); + return sizeof(report); + } + + /* Nothing to do for the PEN_REPORT_ID, it's already mapped */ + + /* Only sent if tablet is in raw mode */ + if (data[0] == VENDOR_REPORT_ID) { + /* Pad reports */ + if (data[1] & 0x20) { + /* See fixed_rdesc_pad */ + struct pad_report { + __u8 report_id; + __u8 btn_stylus; + __u8 x; + __u8 y; + __u8 buttons; + __u8 wheel; + } __attribute__((packed)) *pad_report; + __u8 wheel = 0; + + /* Wheel report */ + if (data[1] == 0xf1) { + if (data[5] == 2) + wheel = 0xff; + else + wheel = data[5]; + } else { + /* data[4] are the buttons, mapped correctly */ + last_button_state = data[4]; + wheel = 0; // wheel + } + + pad_report = (struct pad_report *)data; + + pad_report->report_id = PAD_REPORT_ID; + pad_report->btn_stylus = 0; + pad_report->x = 0; + pad_report->y = 0; + pad_report->buttons = last_button_state; + pad_report->wheel = wheel; + + return sizeof(struct pad_report); + } + + /* Pen reports need nothing done */ + } + + return 0; +} + +HID_BPF_OPS(inspiroy_2) = { + .hid_device_event = (void *)inspiroy_2_fix_events, + .hid_rdesc_fixup = (void *)hid_fix_rdesc, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + switch (ctx->rdesc_size) { + case PAD_REPORT_DESCRIPTOR_LENGTH: + case PEN_REPORT_DESCRIPTOR_LENGTH: + case VENDOR_REPORT_DESCRIPTOR_LENGTH: + ctx->retval = 0; + break; + default: + ctx->retval = -EINVAL; + } + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/drivers/hid/bpf/progs/Huion__Kamvas-Pro-19.bpf.c b/drivers/hid/bpf/progs/Huion__Kamvas-Pro-19.bpf.c new file mode 100644 index 000000000000..a4a4f324aedd --- /dev/null +++ b/drivers/hid/bpf/progs/Huion__Kamvas-Pro-19.bpf.c @@ -0,0 +1,295 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2024 Benjamin Tissoires + */ + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include <bpf/bpf_tracing.h> + +#define VID_HUION 0x256C +#define PID_KAMVAS_PRO_19 0x006B +#define NAME_KAMVAS_PRO_19 "HUION Huion Tablet_GT1902" + +#define TEST_PREFIX "uhid test " + +HID_BPF_CONFIG( + HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8, VID_HUION, PID_KAMVAS_PRO_19), +); + +bool prev_was_out_of_range; +bool in_eraser_mode; + +/* + * We need to amend the report descriptor for the following: + * - the second button is reported through Secondary Tip Switch instead of Secondary Barrel Switch + * - the third button is reported through Invert, and we need some room to report it. + * + */ +static const __u8 fixed_rdesc[] = { + 0x05, 0x0d, // Usage Page (Digitizers) 0 + 0x09, 0x02, // Usage (Pen) 2 + 0xa1, 0x01, // Collection (Application) 4 + 0x85, 0x0a, // Report ID (10) 6 + 0x09, 0x20, // Usage (Stylus) 8 + 0xa1, 0x01, // Collection (Application) 10 + 0x09, 0x42, // Usage (Tip Switch) 12 + 0x09, 0x44, // Usage (Barrel Switch) 14 + 0x09, 0x5a, // Usage (Secondary Barrel Switch) 16 /* changed from Secondary Tip Switch */ + 0x09, 0x3c, // Usage (Invert) 18 + 0x09, 0x45, // Usage (Eraser) 20 + 0x15, 0x00, // Logical Minimum (0) 22 + 0x25, 0x01, // Logical Maximum (1) 24 + 0x75, 0x01, // Report Size (1) 26 + 0x95, 0x05, // Report Count (5) 28 /* changed (was 5) */ + 0x81, 0x02, // Input (Data,Var,Abs) 30 + 0x05, 0x09, // Usage Page (Button) /* inserted */ + 0x09, 0x4a, // Usage (0x4a) /* inserted to be translated as input usage 0x149: BTN_STYLUS3 */ + 0x95, 0x01, // Report Count (1) /* inserted */ + 0x81, 0x02, // Input (Data,Var,Abs) /* inserted */ + 0x05, 0x0d, // Usage Page (Digitizers) /* inserted */ + 0x09, 0x32, // Usage (In Range) 32 + 0x75, 0x01, // Report Size (1) 34 + 0x95, 0x01, // Report Count (1) 36 + 0x81, 0x02, // Input (Data,Var,Abs) 38 + 0x81, 0x03, // Input (Cnst,Var,Abs) 40 + 0x05, 0x01, // Usage Page (Generic Desktop) 42 + 0x09, 0x30, // Usage (X) 44 + 0x09, 0x31, // Usage (Y) 46 + 0x55, 0x0d, // Unit Exponent (-3) 48 + 0x65, 0x33, // Unit (EnglishLinear: in³) 50 + 0x26, 0xff, 0x7f, // Logical Maximum (32767) 52 + 0x35, 0x00, // Physical Minimum (0) 55 + 0x46, 0x00, 0x08, // Physical Maximum (2048) 57 + 0x75, 0x10, // Report Size (16) 60 + 0x95, 0x02, // Report Count (2) 62 + 0x81, 0x02, // Input (Data,Var,Abs) 64 + 0x05, 0x0d, // Usage Page (Digitizers) 66 + 0x09, 0x30, // Usage (Tip Pressure) 68 + 0x26, 0xff, 0x3f, // Logical Maximum (16383) 70 + 0x75, 0x10, // Report Size (16) 73 + 0x95, 0x01, // Report Count (1) 75 + 0x81, 0x02, // Input (Data,Var,Abs) 77 + 0x09, 0x3d, // Usage (X Tilt) 79 + 0x09, 0x3e, // Usage (Y Tilt) 81 + 0x15, 0xa6, // Logical Minimum (-90) 83 + 0x25, 0x5a, // Logical Maximum (90) 85 + 0x75, 0x08, // Report Size (8) 87 + 0x95, 0x02, // Report Count (2) 89 + 0x81, 0x02, // Input (Data,Var,Abs) 91 + 0xc0, // End Collection 93 + 0xc0, // End Collection 94 + 0x05, 0x0d, // Usage Page (Digitizers) 95 + 0x09, 0x04, // Usage (Touch Screen) 97 + 0xa1, 0x01, // Collection (Application) 99 + 0x85, 0x04, // Report ID (4) 101 + 0x09, 0x22, // Usage (Finger) 103 + 0xa1, 0x02, // Collection (Logical) 105 + 0x05, 0x0d, // Usage Page (Digitizers) 107 + 0x95, 0x01, // Report Count (1) 109 + 0x75, 0x06, // Report Size (6) 111 + 0x09, 0x51, // Usage (Contact Id) 113 + 0x15, 0x00, // Logical Minimum (0) 115 + 0x25, 0x3f, // Logical Maximum (63) 117 + 0x81, 0x02, // Input (Data,Var,Abs) 119 + 0x09, 0x42, // Usage (Tip Switch) 121 + 0x25, 0x01, // Logical Maximum (1) 123 + 0x75, 0x01, // Report Size (1) 125 + 0x95, 0x01, // Report Count (1) 127 + 0x81, 0x02, // Input (Data,Var,Abs) 129 + 0x75, 0x01, // Report Size (1) 131 + 0x95, 0x01, // Report Count (1) 133 + 0x81, 0x03, // Input (Cnst,Var,Abs) 135 + 0x05, 0x01, // Usage Page (Generic Desktop) 137 + 0x75, 0x10, // Report Size (16) 139 + 0x55, 0x0e, // Unit Exponent (-2) 141 + 0x65, 0x11, // Unit (SILinear: cm) 143 + 0x09, 0x30, // Usage (X) 145 + 0x26, 0xff, 0x7f, // Logical Maximum (32767) 147 + 0x35, 0x00, // Physical Minimum (0) 150 + 0x46, 0x15, 0x0c, // Physical Maximum (3093) 152 + 0x81, 0x42, // Input (Data,Var,Abs,Null) 155 + 0x09, 0x31, // Usage (Y) 157 + 0x26, 0xff, 0x7f, // Logical Maximum (32767) 159 + 0x46, 0xcb, 0x06, // Physical Maximum (1739) 162 + 0x81, 0x42, // Input (Data,Var,Abs,Null) 165 + 0x05, 0x0d, // Usage Page (Digitizers) 167 + 0x09, 0x30, // Usage (Tip Pressure) 169 + 0x26, 0xff, 0x1f, // Logical Maximum (8191) 171 + 0x75, 0x10, // Report Size (16) 174 + 0x95, 0x01, // Report Count (1) 176 + 0x81, 0x02, // Input (Data,Var,Abs) 178 + 0xc0, // End Collection 180 + 0x05, 0x0d, // Usage Page (Digitizers) 181 + 0x09, 0x22, // Usage (Finger) 183 + 0xa1, 0x02, // Collection (Logical) 185 + 0x05, 0x0d, // Usage Page (Digitizers) 187 + 0x95, 0x01, // Report Count (1) 189 + 0x75, 0x06, // Report Size (6) 191 + 0x09, 0x51, // Usage (Contact Id) 193 + 0x15, 0x00, // Logical Minimum (0) 195 + 0x25, 0x3f, // Logical Maximum (63) 197 + 0x81, 0x02, // Input (Data,Var,Abs) 199 + 0x09, 0x42, // Usage (Tip Switch) 201 + 0x25, 0x01, // Logical Maximum (1) 203 + 0x75, 0x01, // Report Size (1) 205 + 0x95, 0x01, // Report Count (1) 207 + 0x81, 0x02, // Input (Data,Var,Abs) 209 + 0x75, 0x01, // Report Size (1) 211 + 0x95, 0x01, // Report Count (1) 213 + 0x81, 0x03, // Input (Cnst,Var,Abs) 215 + 0x05, 0x01, // Usage Page (Generic Desktop) 217 + 0x75, 0x10, // Report Size (16) 219 + 0x55, 0x0e, // Unit Exponent (-2) 221 + 0x65, 0x11, // Unit (SILinear: cm) 223 + 0x09, 0x30, // Usage (X) 225 + 0x26, 0xff, 0x7f, // Logical Maximum (32767) 227 + 0x35, 0x00, // Physical Minimum (0) 230 + 0x46, 0x15, 0x0c, // Physical Maximum (3093) 232 + 0x81, 0x42, // Input (Data,Var,Abs,Null) 235 + 0x09, 0x31, // Usage (Y) 237 + 0x26, 0xff, 0x7f, // Logical Maximum (32767) 239 + 0x46, 0xcb, 0x06, // Physical Maximum (1739) 242 + 0x81, 0x42, // Input (Data,Var,Abs,Null) 245 + 0x05, 0x0d, // Usage Page (Digitizers) 247 + 0x09, 0x30, // Usage (Tip Pressure) 249 + 0x26, 0xff, 0x1f, // Logical Maximum (8191) 251 + 0x75, 0x10, // Report Size (16) 254 + 0x95, 0x01, // Report Count (1) 256 + 0x81, 0x02, // Input (Data,Var,Abs) 258 + 0xc0, // End Collection 260 + 0x05, 0x0d, // Usage Page (Digitizers) 261 + 0x09, 0x56, // Usage (Scan Time) 263 + 0x55, 0x00, // Unit Exponent (0) 265 + 0x65, 0x00, // Unit (None) 267 + 0x27, 0xff, 0xff, 0xff, 0x7f, // Logical Maximum (2147483647) 269 + 0x95, 0x01, // Report Count (1) 274 + 0x75, 0x20, // Report Size (32) 276 + 0x81, 0x02, // Input (Data,Var,Abs) 278 + 0x09, 0x54, // Usage (Contact Count) 280 + 0x25, 0x7f, // Logical Maximum (127) 282 + 0x95, 0x01, // Report Count (1) 284 + 0x75, 0x08, // Report Size (8) 286 + 0x81, 0x02, // Input (Data,Var,Abs) 288 + 0x75, 0x08, // Report Size (8) 290 + 0x95, 0x08, // Report Count (8) 292 + 0x81, 0x03, // Input (Cnst,Var,Abs) 294 + 0x85, 0x05, // Report ID (5) 296 + 0x09, 0x55, // Usage (Contact Max) 298 + 0x25, 0x0a, // Logical Maximum (10) 300 + 0x75, 0x08, // Report Size (8) 302 + 0x95, 0x01, // Report Count (1) 304 + 0xb1, 0x02, // Feature (Data,Var,Abs) 306 + 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 308 + 0x09, 0xc5, // Usage (Vendor Usage 0xc5) 311 + 0x85, 0x06, // Report ID (6) 313 + 0x15, 0x00, // Logical Minimum (0) 315 + 0x26, 0xff, 0x00, // Logical Maximum (255) 317 + 0x75, 0x08, // Report Size (8) 320 + 0x96, 0x00, 0x01, // Report Count (256) 322 + 0xb1, 0x02, // Feature (Data,Var,Abs) 325 + 0xc0, // End Collection 327 +}; + +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(hid_fix_rdesc_huion_kamvas_pro_19, struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */); + + if (!data) + return 0; /* EPERM check */ + + __builtin_memcpy(data, fixed_rdesc, sizeof(fixed_rdesc)); + + return sizeof(fixed_rdesc); +} + +/* + * This tablet reports the 3rd button through invert, but this conflict + * with the normal eraser mode. + * Fortunately, before entering eraser mode, (so Invert = 1), + * the tablet always sends an out-of-proximity event. + * So we can detect that single event and: + * - if there was none but the invert bit was toggled: this is the + * third button + * - if there was this out-of-proximity event, we are entering + * eraser mode, and we will until the next out-of-proximity. + */ +SEC(HID_BPF_DEVICE_EVENT) +int BPF_PROG(kamvas_pro_19_fix_3rd_button, struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */); + + if (!data) + return 0; /* EPERM check */ + + if (data[0] != 0x0a) /* not the pen report ID */ + return 0; + + /* stylus is out of range */ + if (!(data[1] & 0x40)) { + prev_was_out_of_range = true; + in_eraser_mode = false; + return 0; + } + + /* going into eraser mode (Invert = 1) only happens after an + * out of range event + */ + if (prev_was_out_of_range && (data[1] & 0x18)) + in_eraser_mode = true; + + /* eraser mode works fine */ + if (in_eraser_mode) + return 0; + + /* copy the Invert bit reported for the 3rd button in bit 7 */ + if (data[1] & 0x08) + data[1] |= 0x20; + + /* clear Invert bit now that it was copied */ + data[1] &= 0xf7; + + prev_was_out_of_range = false; + + return 0; +} + +HID_BPF_OPS(huion_Kamvas_pro_19) = { + .hid_rdesc_fixup = (void *)hid_fix_rdesc_huion_kamvas_pro_19, + .hid_device_event = (void *)kamvas_pro_19_fix_3rd_button, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + ctx->retval = ctx->rdesc_size != 328; + if (ctx->retval) + ctx->retval = -EINVAL; + + /* ensure the kernel isn't fixed already */ + if (ctx->rdesc[17] != 0x43) /* Secondary Tip Switch */ + ctx->retval = -EINVAL; + + struct hid_bpf_ctx *hctx = hid_bpf_allocate_context(ctx->hid); + + if (!hctx) { + return ctx->retval = -EINVAL; + return 0; + } + + const char *name = hctx->hid->name; + + /* strip out TEST_PREFIX */ + if (!__builtin_memcmp(name, TEST_PREFIX, sizeof(TEST_PREFIX) - 1)) + name += sizeof(TEST_PREFIX) - 1; + + if (__builtin_memcmp(name, NAME_KAMVAS_PRO_19, sizeof(NAME_KAMVAS_PRO_19))) + ctx->retval = -EINVAL; + + hid_bpf_release_context(hctx); + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/drivers/hid/bpf/progs/IOGEAR__Kaliber-MMOmentum.bpf.c b/drivers/hid/bpf/progs/IOGEAR__Kaliber-MMOmentum.bpf.c new file mode 100644 index 000000000000..82f1950445dd --- /dev/null +++ b/drivers/hid/bpf/progs/IOGEAR__Kaliber-MMOmentum.bpf.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2023 Benjamin Tissoires + */ + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include <bpf/bpf_tracing.h> + +#define VID_IOGEAR 0x258A /* VID is shared with SinoWealth and Glorious and prob others */ +#define PID_MOMENTUM 0x0027 + +HID_BPF_CONFIG( + HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_IOGEAR, PID_MOMENTUM) +); + +/* + * The IOGear Kaliber Gaming MMOmentum Pro mouse has multiple buttons (12) + * but only 5 are accessible out of the box because the report descriptor + * marks the other buttons as constants. + * We just fix the report descriptor to enable those missing 7 buttons. + */ + +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx) +{ + const u8 offsets[] = {84, 112, 140}; + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */); + + if (!data) + return 0; /* EPERM check */ + + /* if not Keyboard */ + if (data[3] != 0x06) + return 0; + + for (int idx = 0; idx < ARRAY_SIZE(offsets); idx++) { + u8 offset = offsets[idx]; + + /* if Input (Cnst,Var,Abs) , make it Input (Data,Var,Abs) */ + if (data[offset] == 0x81 && data[offset + 1] == 0x03) + data[offset + 1] = 0x02; + } + + return 0; +} + +HID_BPF_OPS(iogear_kaliber_momentum) = { + .hid_rdesc_fixup = (void *)hid_fix_rdesc, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + /* only bind to the keyboard interface */ + ctx->retval = ctx->rdesc_size != 213; + if (ctx->retval) + ctx->retval = -EINVAL; + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/drivers/hid/bpf/entrypoints/Makefile b/drivers/hid/bpf/progs/Makefile index 43b99b5575cf..ec1fc642fd63 100644 --- a/drivers/hid/bpf/entrypoints/Makefile +++ b/drivers/hid/bpf/progs/Makefile @@ -45,24 +45,22 @@ endif .PHONY: all clean -all: entrypoints.lskel.h +SOURCES = $(wildcard *.bpf.c) +TARGETS = $(SOURCES:.bpf.c=.bpf.o) + +all: $(TARGETS) clean: $(call msg,CLEAN) - $(Q)rm -rf $(OUTPUT) entrypoints - -entrypoints.lskel.h: $(OUTPUT)/entrypoints.bpf.o | $(BPFTOOL) - $(call msg,GEN-SKEL,$@) - $(Q)$(BPFTOOL) gen skeleton -L $< > $@ - + $(Q)rm -rf $(OUTPUT) $(TARGETS) -$(OUTPUT)/entrypoints.bpf.o: entrypoints.bpf.c $(OUTPUT)/vmlinux.h $(BPFOBJ) | $(OUTPUT) +%.bpf.o: %.bpf.c vmlinux.h $(BPFOBJ) | $(OUTPUT) $(call msg,BPF,$@) - $(Q)$(CLANG) -g -O2 --target=bpf $(INCLUDES) \ + $(Q)$(CLANG) -g -O2 --target=bpf -Wall -Werror $(INCLUDES) \ -c $(filter %.c,$^) -o $@ && \ $(LLVM_STRIP) -g $@ -$(OUTPUT)/vmlinux.h: $(VMLINUX_BTF) $(BPFTOOL) | $(INCLUDE_DIR) +vmlinux.h: $(VMLINUX_BTF) $(BPFTOOL) | $(INCLUDE_DIR) ifeq ($(VMLINUX_H),) $(call msg,GEN,,$@) $(Q)$(BPFTOOL) btf dump file $(VMLINUX_BTF) format c > $@ diff --git a/drivers/hid/bpf/progs/Microsoft__Xbox-Elite-2.bpf.c b/drivers/hid/bpf/progs/Microsoft__Xbox-Elite-2.bpf.c new file mode 100644 index 000000000000..382bba735a2c --- /dev/null +++ b/drivers/hid/bpf/progs/Microsoft__Xbox-Elite-2.bpf.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2024 Benjamin Tissoires + */ + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include <bpf/bpf_tracing.h> + +#define VID_MICROSOFT 0x045e +#define PID_XBOX_ELITE_2 0x0b22 + +HID_BPF_CONFIG( + HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_GENERIC, VID_MICROSOFT, PID_XBOX_ELITE_2) +); + +/* + * When using the Xbox Wireless Controller Elite 2 over Bluetooth, + * the device exports the paddles on the back of the device as a single + * bitfield value of usage "Assign Selection". + * + * The kernel doesn't process the paddles usage properly and reports KEY_UNKNOWN. + * + * SDL doesn't know how to interpret KEY_UNKNOWN and thus ignores the paddles. + * + * Given that over USB the kernel uses BTN_TRIGGER_HAPPY[5-8], we + * can tweak the report descriptor to make the kernel interpret it properly: + * - We need an application collection of gamepad (so we have to close the current + * Consumer Control one) + * - We need to change the usage to be buttons from 0x15 to 0x18 + */ + +#define OFFSET_ASSIGN_SELECTION 211 +#define ORIGINAL_RDESC_SIZE 464 + +const __u8 rdesc_assign_selection[] = { + 0x0a, 0x99, 0x00, // Usage (Media Select Security) 211 + 0x15, 0x00, // Logical Minimum (0) 214 + 0x26, 0xff, 0x00, // Logical Maximum (255) 216 + 0x95, 0x01, // Report Count (1) 219 + 0x75, 0x04, // Report Size (4) 221 + 0x81, 0x02, // Input (Data,Var,Abs) 223 + 0x15, 0x00, // Logical Minimum (0) 225 + 0x25, 0x00, // Logical Maximum (0) 227 + 0x95, 0x01, // Report Count (1) 229 + 0x75, 0x04, // Report Size (4) 231 + 0x81, 0x03, // Input (Cnst,Var,Abs) 233 + 0x0a, 0x81, 0x00, // Usage (Assign Selection) 235 + 0x15, 0x00, // Logical Minimum (0) 238 + 0x26, 0xff, 0x00, // Logical Maximum (255) 240 + 0x95, 0x01, // Report Count (1) 243 + 0x75, 0x04, // Report Size (4) 245 + 0x81, 0x02, // Input (Data,Var,Abs) 247 +}; + +/* + * we replace the above report descriptor extract + * with the one below. + * To make things equal in size, we take out a larger + * portion than just the "Assign Selection" range, because + * we need to insert a new application collection to force + * the kernel to use BTN_TRIGGER_HAPPY[4-7]. + */ +const __u8 fixed_rdesc_assign_selection[] = { + 0x0a, 0x99, 0x00, // Usage (Media Select Security) 211 + 0x15, 0x00, // Logical Minimum (0) 214 + 0x26, 0xff, 0x00, // Logical Maximum (255) 216 + 0x95, 0x01, // Report Count (1) 219 + 0x75, 0x04, // Report Size (4) 221 + 0x81, 0x02, // Input (Data,Var,Abs) 223 + /* 0x15, 0x00, */ // Logical Minimum (0) ignored + 0x25, 0x01, // Logical Maximum (1) 225 + 0x95, 0x04, // Report Count (4) 227 + 0x75, 0x01, // Report Size (1) 229 + 0x81, 0x03, // Input (Cnst,Var,Abs) 231 + 0xc0, // End Collection 233 + 0x05, 0x01, // Usage Page (Generic Desktop) 234 + 0x0a, 0x05, 0x00, // Usage (Game Pad) 236 + 0xa1, 0x01, // Collection (Application) 239 + 0x05, 0x09, // Usage Page (Button) 241 + 0x19, 0x15, // Usage Minimum (21) 243 + 0x29, 0x18, // Usage Maximum (24) 245 + /* 0x15, 0x00, */ // Logical Minimum (0) ignored + /* 0x25, 0x01, */ // Logical Maximum (1) ignored + /* 0x95, 0x01, */ // Report Size (1) ignored + /* 0x75, 0x04, */ // Report Count (4) ignored + 0x81, 0x02, // Input (Data,Var,Abs) 247 +}; + +_Static_assert(sizeof(rdesc_assign_selection) == sizeof(fixed_rdesc_assign_selection), + "Rdesc and fixed rdesc of different size"); +_Static_assert(sizeof(rdesc_assign_selection) + OFFSET_ASSIGN_SELECTION < ORIGINAL_RDESC_SIZE, + "Rdesc at given offset is too big"); + +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */); + + if (!data) + return 0; /* EPERM check */ + + /* Check that the device is compatible */ + if (__builtin_memcmp(data + OFFSET_ASSIGN_SELECTION, + rdesc_assign_selection, + sizeof(rdesc_assign_selection))) + return 0; + + __builtin_memcpy(data + OFFSET_ASSIGN_SELECTION, + fixed_rdesc_assign_selection, + sizeof(fixed_rdesc_assign_selection)); + + return 0; +} + +HID_BPF_OPS(xbox_elite_2) = { + .hid_rdesc_fixup = (void *)hid_fix_rdesc, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + /* only bind to the keyboard interface */ + ctx->retval = ctx->rdesc_size != ORIGINAL_RDESC_SIZE; + if (ctx->retval) + ctx->retval = -EINVAL; + + if (__builtin_memcmp(ctx->rdesc + OFFSET_ASSIGN_SELECTION, + rdesc_assign_selection, + sizeof(rdesc_assign_selection))) + ctx->retval = -EINVAL; + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/drivers/hid/bpf/progs/Mistel__MD770.bpf.c b/drivers/hid/bpf/progs/Mistel__MD770.bpf.c new file mode 100644 index 000000000000..fb8b5a6968b1 --- /dev/null +++ b/drivers/hid/bpf/progs/Mistel__MD770.bpf.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2024 Tatsuyuki Ishi + */ + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include <bpf/bpf_tracing.h> + +#define VID_HOLTEK 0x04D9 +#define PID_MD770 0x0339 +#define RDESC_SIZE 203 + +HID_BPF_CONFIG( + HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_HOLTEK, PID_MD770) +); + +/* + * The Mistel MD770 keyboard reports the first 6 simultaneous key presses + * through the first interface, and anything beyond that through a second + * interface. Unfortunately, the second interface's report descriptor has an + * error, causing events to be malformed and ignored. This HID-BPF driver + * fixes the descriptor to allow NKRO to work again. + * + * For reference, this is the original report descriptor: + * + * 0x05, 0x01, // Usage Page (Generic Desktop) 0 + * 0x09, 0x80, // Usage (System Control) 2 + * 0xa1, 0x01, // Collection (Application) 4 + * 0x85, 0x01, // Report ID (1) 6 + * 0x19, 0x81, // Usage Minimum (129) 8 + * 0x29, 0x83, // Usage Maximum (131) 10 + * 0x15, 0x00, // Logical Minimum (0) 12 + * 0x25, 0x01, // Logical Maximum (1) 14 + * 0x95, 0x03, // Report Count (3) 16 + * 0x75, 0x01, // Report Size (1) 18 + * 0x81, 0x02, // Input (Data,Var,Abs) 20 + * 0x95, 0x01, // Report Count (1) 22 + * 0x75, 0x05, // Report Size (5) 24 + * 0x81, 0x01, // Input (Cnst,Arr,Abs) 26 + * 0xc0, // End Collection 28 + * 0x05, 0x0c, // Usage Page (Consumer Devices) 29 + * 0x09, 0x01, // Usage (Consumer Control) 31 + * 0xa1, 0x01, // Collection (Application) 33 + * 0x85, 0x02, // Report ID (2) 35 + * 0x15, 0x00, // Logical Minimum (0) 37 + * 0x25, 0x01, // Logical Maximum (1) 39 + * 0x95, 0x12, // Report Count (18) 41 + * 0x75, 0x01, // Report Size (1) 43 + * 0x0a, 0x83, 0x01, // Usage (AL Consumer Control Config) 45 + * 0x0a, 0x8a, 0x01, // Usage (AL Email Reader) 48 + * 0x0a, 0x92, 0x01, // Usage (AL Calculator) 51 + * 0x0a, 0x94, 0x01, // Usage (AL Local Machine Browser) 54 + * 0x09, 0xcd, // Usage (Play/Pause) 57 + * 0x09, 0xb7, // Usage (Stop) 59 + * 0x09, 0xb6, // Usage (Scan Previous Track) 61 + * 0x09, 0xb5, // Usage (Scan Next Track) 63 + * 0x09, 0xe2, // Usage (Mute) 65 + * 0x09, 0xea, // Usage (Volume Down) 67 + * 0x09, 0xe9, // Usage (Volume Up) 69 + * 0x0a, 0x21, 0x02, // Usage (AC Search) 71 + * 0x0a, 0x23, 0x02, // Usage (AC Home) 74 + * 0x0a, 0x24, 0x02, // Usage (AC Back) 77 + * 0x0a, 0x25, 0x02, // Usage (AC Forward) 80 + * 0x0a, 0x26, 0x02, // Usage (AC Stop) 83 + * 0x0a, 0x27, 0x02, // Usage (AC Refresh) 86 + * 0x0a, 0x2a, 0x02, // Usage (AC Bookmarks) 89 + * 0x81, 0x02, // Input (Data,Var,Abs) 92 + * 0x95, 0x01, // Report Count (1) 94 + * 0x75, 0x0e, // Report Size (14) 96 + * 0x81, 0x01, // Input (Cnst,Arr,Abs) 98 + * 0xc0, // End Collection 100 + * 0x05, 0x01, // Usage Page (Generic Desktop) 101 + * 0x09, 0x02, // Usage (Mouse) 103 + * 0xa1, 0x01, // Collection (Application) 105 + * 0x09, 0x01, // Usage (Pointer) 107 + * 0xa1, 0x00, // Collection (Physical) 109 + * 0x85, 0x03, // Report ID (3) 111 + * 0x05, 0x09, // Usage Page (Button) 113 + * 0x19, 0x01, // Usage Minimum (1) 115 + * 0x29, 0x08, // Usage Maximum (8) 117 + * 0x15, 0x00, // Logical Minimum (0) 119 + * 0x25, 0x01, // Logical Maximum (1) 121 + * 0x75, 0x01, // Report Size (1) 123 + * 0x95, 0x08, // Report Count (8) 125 + * 0x81, 0x02, // Input (Data,Var,Abs) 127 + * 0x05, 0x01, // Usage Page (Generic Desktop) 129 + * 0x09, 0x30, // Usage (X) 131 + * 0x09, 0x31, // Usage (Y) 133 + * 0x16, 0x01, 0x80, // Logical Minimum (-32767) 135 + * 0x26, 0xff, 0x7f, // Logical Maximum (32767) 138 + * 0x75, 0x10, // Report Size (16) 141 + * 0x95, 0x02, // Report Count (2) 143 + * 0x81, 0x06, // Input (Data,Var,Rel) 145 + * 0x09, 0x38, // Usage (Wheel) 147 + * 0x15, 0x81, // Logical Minimum (-127) 149 + * 0x25, 0x7f, // Logical Maximum (127) 151 + * 0x75, 0x08, // Report Size (8) 153 + * 0x95, 0x01, // Report Count (1) 155 + * 0x81, 0x06, // Input (Data,Var,Rel) 157 + * 0x05, 0x0c, // Usage Page (Consumer Devices) 159 + * 0x0a, 0x38, 0x02, // Usage (AC Pan) 161 + * 0x95, 0x01, // Report Count (1) 164 + * 0x81, 0x06, // Input (Data,Var,Rel) 166 + * 0xc0, // End Collection 168 + * 0xc0, // End Collection 169 + * 0x05, 0x01, // Usage Page (Generic Desktop) 170 + * 0x09, 0x06, // Usage (Keyboard) 172 + * 0xa1, 0x01, // Collection (Application) 174 + * 0x85, 0x04, // Report ID (4) 176 + * 0x05, 0x07, // Usage Page (Keyboard) 178 + * 0x95, 0x01, // Report Count (1) 180 + * 0x75, 0x08, // Report Size (8) 182 + * 0x81, 0x03, // Input (Cnst,Var,Abs) 184 + * 0x95, 0xe8, // Report Count (232) 186 + * 0x75, 0x01, // Report Size (1) 188 + * 0x15, 0x00, // Logical Minimum (0) 190 + * 0x25, 0x01, // Logical Maximum (1) 192 + * 0x05, 0x07, // Usage Page (Keyboard) 194 + * 0x19, 0x00, // Usage Minimum (0) 196 + * 0x29, 0xe7, // Usage Maximum (231) 198 + * 0x81, 0x00, // Input (Data,Arr,Abs) 200 <- change to 0x81, 0x02 (Data,Var,Abs) + * 0xc0, // End Collection 202 + */ + +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(hid_rdesc_fixup_mistel_md770, struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0, HID_MAX_DESCRIPTOR_SIZE); + + if (!data) + return 0; /* EPERM check */ + + if (data[201] == 0x00) + data[201] = 0x02; + + return 0; +} + +HID_BPF_OPS(mistel_md770) = { + .hid_rdesc_fixup = (void *)hid_rdesc_fixup_mistel_md770, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + ctx->retval = ctx->rdesc_size != RDESC_SIZE; + if (ctx->retval) + ctx->retval = -EINVAL; + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/drivers/hid/bpf/progs/README b/drivers/hid/bpf/progs/README new file mode 100644 index 000000000000..20b0928f385b --- /dev/null +++ b/drivers/hid/bpf/progs/README @@ -0,0 +1,102 @@ +# HID-BPF programs + +This directory contains various fixes for devices. They add new features or +fix some behaviors without being entirely mandatory. It is better to load them +when you have such a device, but they should not be a requirement for a device +to be working during the boot stage. + +The .bpf.c files provided here are not automatically compiled in the kernel. +They should be loaded in the kernel by `udev-hid-bpf`: + +https://gitlab.freedesktop.org/libevdev/udev-hid-bpf + +The main reasons for these fixes to be here is to have a central place to +"upstream" them, but also this way we can test them thanks to the HID +selftests. + +Once a .bpf.c file is accepted here, it is duplicated in `udev-hid-bpf` +in the `src/bpf/stable` directory, and distributions are encouraged to +only ship those bpf objects. So adding a file here should eventually +land in distributions when they update `udev-hid-bpf` + +## Compilation + +Just run `make` + +## Installation + +### Automated way + +Just run `sudo udev-hid-bpf install ./my-awesome-fix.bpf.o` + +### Manual way + +- copy the `.bpf.o` you want in `/etc/udev-hid-bpf/` +- create a new udev rule to automatically load it + +The following should do the trick (assuming udev-hid-bpf is available in +/usr/bin): + +``` +$> cp xppen-ArtistPro16Gen2.bpf.o /etc/udev-hid-bpf/ +$> udev-hid-bpf inspect xppen-ArtistPro16Gen2.bpf.o +[ + { + "name": "xppen-ArtistPro16Gen2.bpf.o", + "devices": [ + { + "bus": "0x0003", + "group": "0x0001", + "vid": "0x28BD", + "pid": "0x095A" + }, + { + "bus": "0x0003", + "group": "0x0001", + "vid": "0x28BD", + "pid": "0x095B" + } + ], +... +$> cat <EOF > /etc/udev/rules.d/99-load-hid-bpf-xppen-ArtistPro16Gen2.rules +ACTION!="add|remove", GOTO="hid_bpf_end" +SUBSYSTEM!="hid", GOTO="hid_bpf_end" + +# xppen-ArtistPro16Gen2.bpf.o +ACTION=="add",ENV{MODALIAS}=="hid:b0003g0001v000028BDp0000095A", RUN{program}+="/usr/local/bin/udev-hid-bpf add $sys$devpath /etc/udev-hid-bpf/xppen-ArtistPro16Gen2.bpf.o" +ACTION=="remove",ENV{MODALIAS}=="hid:b0003g0001v000028BDp0000095A", RUN{program}+="/usr/local/bin/udev-hid-bpf remove $sys$devpath " +# xppen-ArtistPro16Gen2.bpf.o +ACTION=="add",ENV{MODALIAS}=="hid:b0003g0001v000028BDp0000095B", RUN{program}+="/usr/local/bin/udev-hid-bpf add $sys$devpath /etc/udev-hid-bpf/xppen-ArtistPro16Gen2.bpf.o" +ACTION=="remove",ENV{MODALIAS}=="hid:b0003g0001v000028BDp0000095B", RUN{program}+="/usr/local/bin/udev-hid-bpf remove $sys$devpath " + +LABEL="hid_bpf_end" +EOF +$> udevadm control --reload +``` + +Then unplug and replug the device. + +## Checks + +### udev rule + +You can check that the udev rule is correctly working by issuing + +``` +$> udevadm test /sys/bus/hid/devices/0003:28BD:095B* +... +run: '/usr/local/bin/udev-hid-bpf add /sys/devices/virtual/misc/uhid/0003:28BD:095B.0E57 /etc/udev-hid-bpf/xppen-ArtistPro16Gen2.bpf.o' +``` + +### program loaded + +You can check that the program has been properly loaded with `bpftool` + +``` +$> bpftool prog +... +247: tracing name xppen_16_fix_eraser tag 18d389353ed2ef07 gpl + loaded_at 2024-03-28T16:02:28+0100 uid 0 + xlated 120B jited 77B memlock 4096B + btf_id 487 +``` diff --git a/drivers/hid/bpf/progs/Rapoo__M50-Plus-Silent.bpf.c b/drivers/hid/bpf/progs/Rapoo__M50-Plus-Silent.bpf.c new file mode 100644 index 000000000000..6b379e45f531 --- /dev/null +++ b/drivers/hid/bpf/progs/Rapoo__M50-Plus-Silent.bpf.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2024 José Expósito + */ + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include <bpf/bpf_tracing.h> + +#define VID_RAPOO 0x24AE +#define PID_M50 0x2015 +#define RDESC_SIZE 186 + +HID_BPF_CONFIG( + HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_RAPOO, PID_M50) +); + +/* + * The Rapoo M50 Plus Silent mouse has 2 side buttons in addition to the left, + * right and middle buttons. However, its original HID descriptor has a Usage + * Maximum of 3, preventing the side buttons to work. This HID-BPF driver + * changes that usage to 5. + * + * For reference, this is the original report descriptor: + * + * 0x05, 0x01, // Usage Page (Generic Desktop) 0 + * 0x09, 0x02, // Usage (Mouse) 2 + * 0xa1, 0x01, // Collection (Application) 4 + * 0x85, 0x01, // Report ID (1) 6 + * 0x09, 0x01, // Usage (Pointer) 8 + * 0xa1, 0x00, // Collection (Physical) 10 + * 0x05, 0x09, // Usage Page (Button) 12 + * 0x19, 0x01, // Usage Minimum (1) 14 + * 0x29, 0x03, // Usage Maximum (3) 16 <- change to 0x05 + * 0x15, 0x00, // Logical Minimum (0) 18 + * 0x25, 0x01, // Logical Maximum (1) 20 + * 0x75, 0x01, // Report Size (1) 22 + * 0x95, 0x05, // Report Count (5) 24 + * 0x81, 0x02, // Input (Data,Var,Abs) 26 + * 0x75, 0x03, // Report Size (3) 28 + * 0x95, 0x01, // Report Count (1) 30 + * 0x81, 0x01, // Input (Cnst,Arr,Abs) 32 + * 0x05, 0x01, // Usage Page (Generic Desktop) 34 + * 0x09, 0x30, // Usage (X) 36 + * 0x09, 0x31, // Usage (Y) 38 + * 0x16, 0x01, 0x80, // Logical Minimum (-32767) 40 + * 0x26, 0xff, 0x7f, // Logical Maximum (32767) 43 + * 0x75, 0x10, // Report Size (16) 46 + * 0x95, 0x02, // Report Count (2) 48 + * 0x81, 0x06, // Input (Data,Var,Rel) 50 + * 0x09, 0x38, // Usage (Wheel) 52 + * 0x15, 0x81, // Logical Minimum (-127) 54 + * 0x25, 0x7f, // Logical Maximum (127) 56 + * 0x75, 0x08, // Report Size (8) 58 + * 0x95, 0x01, // Report Count (1) 60 + * 0x81, 0x06, // Input (Data,Var,Rel) 62 + * 0xc0, // End Collection 64 + * 0xc0, // End Collection 65 + * 0x05, 0x0c, // Usage Page (Consumer Devices) 66 + * 0x09, 0x01, // Usage (Consumer Control) 68 + * 0xa1, 0x01, // Collection (Application) 70 + * 0x85, 0x02, // Report ID (2) 72 + * 0x75, 0x10, // Report Size (16) 74 + * 0x95, 0x01, // Report Count (1) 76 + * 0x15, 0x01, // Logical Minimum (1) 78 + * 0x26, 0x8c, 0x02, // Logical Maximum (652) 80 + * 0x19, 0x01, // Usage Minimum (1) 83 + * 0x2a, 0x8c, 0x02, // Usage Maximum (652) 85 + * 0x81, 0x00, // Input (Data,Arr,Abs) 88 + * 0xc0, // End Collection 90 + * 0x05, 0x01, // Usage Page (Generic Desktop) 91 + * 0x09, 0x80, // Usage (System Control) 93 + * 0xa1, 0x01, // Collection (Application) 95 + * 0x85, 0x03, // Report ID (3) 97 + * 0x09, 0x82, // Usage (System Sleep) 99 + * 0x09, 0x81, // Usage (System Power Down) 101 + * 0x09, 0x83, // Usage (System Wake Up) 103 + * 0x15, 0x00, // Logical Minimum (0) 105 + * 0x25, 0x01, // Logical Maximum (1) 107 + * 0x19, 0x01, // Usage Minimum (1) 109 + * 0x29, 0x03, // Usage Maximum (3) 111 + * 0x75, 0x01, // Report Size (1) 113 + * 0x95, 0x03, // Report Count (3) 115 + * 0x81, 0x02, // Input (Data,Var,Abs) 117 + * 0x95, 0x05, // Report Count (5) 119 + * 0x81, 0x01, // Input (Cnst,Arr,Abs) 121 + * 0xc0, // End Collection 123 + * 0x05, 0x01, // Usage Page (Generic Desktop) 124 + * 0x09, 0x00, // Usage (Undefined) 126 + * 0xa1, 0x01, // Collection (Application) 128 + * 0x85, 0x05, // Report ID (5) 130 + * 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 132 + * 0x09, 0x01, // Usage (Vendor Usage 1) 135 + * 0x15, 0x81, // Logical Minimum (-127) 137 + * 0x25, 0x7f, // Logical Maximum (127) 139 + * 0x75, 0x08, // Report Size (8) 141 + * 0x95, 0x07, // Report Count (7) 143 + * 0xb1, 0x02, // Feature (Data,Var,Abs) 145 + * 0xc0, // End Collection 147 + * 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 148 + * 0x09, 0x0e, // Usage (Vendor Usage 0x0e) 151 + * 0xa1, 0x01, // Collection (Application) 153 + * 0x85, 0xba, // Report ID (186) 155 + * 0x95, 0x1f, // Report Count (31) 157 + * 0x75, 0x08, // Report Size (8) 159 + * 0x26, 0xff, 0x00, // Logical Maximum (255) 161 + * 0x15, 0x00, // Logical Minimum (0) 164 + * 0x09, 0x01, // Usage (Vendor Usage 1) 166 + * 0x91, 0x02, // Output (Data,Var,Abs) 168 + * 0x85, 0xba, // Report ID (186) 170 + * 0x95, 0x1f, // Report Count (31) 172 + * 0x75, 0x08, // Report Size (8) 174 + * 0x26, 0xff, 0x00, // Logical Maximum (255) 176 + * 0x15, 0x00, // Logical Minimum (0) 179 + * 0x09, 0x01, // Usage (Vendor Usage 1) 181 + * 0x81, 0x02, // Input (Data,Var,Abs) 183 + * 0xc0, // End Collection 185 + */ + +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(hid_rdesc_fixup_rapoo_m50, struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0, HID_MAX_DESCRIPTOR_SIZE); + + if (!data) + return 0; /* EPERM check */ + + if (data[17] == 0x03) + data[17] = 0x05; + + return 0; +} + +HID_BPF_OPS(rapoo_m50) = { + .hid_rdesc_fixup = (void *)hid_rdesc_fixup_rapoo_m50, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + ctx->retval = ctx->rdesc_size != RDESC_SIZE; + if (ctx->retval) + ctx->retval = -EINVAL; + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/drivers/hid/bpf/progs/Thrustmaster__TCA-Yoke-Boeing.bpf.c b/drivers/hid/bpf/progs/Thrustmaster__TCA-Yoke-Boeing.bpf.c new file mode 100644 index 000000000000..ecf775a99346 --- /dev/null +++ b/drivers/hid/bpf/progs/Thrustmaster__TCA-Yoke-Boeing.bpf.c @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2024 Kumar Swarnam Iyer (kumar.s.iyer65@gmail.com) + */ + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include <bpf/bpf_tracing.h> + +#define VID_THRUSTMASTER 0x044F +#define PID_TCA_YOKE_BOEING 0x0409 + +HID_BPF_CONFIG( + HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_THRUSTMASTER, PID_TCA_YOKE_BOEING) +); + +/* The original HID descriptor of the Thrustmaster TCA Yoke Boeing joystick contains + * an Input field that shows up as an axis, ABS_MISC in Linux. But it is not possible + * to assign an actual physical control to this axis as they're all taken up. There + * are 2 vendor-defined inputs where the Input type appears to be defined wrongly. + * This bpf attempts to fix this by changing the Inputs so that it doesn't show up in + * Linux at all. + * This version is the short version fix that only changes 2 fields in the descriptor + * instead of the whole report descriptor. + * For reference, this is the original report descriptor: + * + * 0x05, 0x01, // Usage Page (Generic Desktop) 0 + * 0x09, 0x04, // Usage (Joystick) 2 + * 0xa1, 0x01, // Collection (Application) 4 + * 0x85, 0x01, // Report ID (1) 6 + * 0x09, 0x39, // Usage (Hat switch) 8 + * 0x15, 0x00, // Logical Minimum (0) 10 + * 0x25, 0x07, // Logical Maximum (7) 12 + * 0x35, 0x00, // Physical Minimum (0) 14 + * 0x46, 0x3b, 0x01, // Physical Maximum (315) 16 + * 0x65, 0x14, // Unit (EnglishRotation: deg) 19 + * 0x75, 0x04, // Report Size (4) 21 + * 0x95, 0x01, // Report Count (1) 23 + * 0x81, 0x42, // Input (Data,Var,Abs,Null) 25 + * 0x65, 0x00, // Unit (None) 27 + * 0x05, 0x09, // Usage Page (Button) 29 + * 0x19, 0x01, // Usage Minimum (1) 31 + * 0x29, 0x12, // Usage Maximum (18) 33 + * 0x15, 0x00, // Logical Minimum (0) 35 + * 0x25, 0x01, // Logical Maximum (1) 37 + * 0x75, 0x01, // Report Size (1) 39 + * 0x95, 0x12, // Report Count (18) 41 + * 0x81, 0x02, // Input (Data,Var,Abs) 43 + * 0x95, 0x02, // Report Count (2) 45 + * 0x81, 0x03, // Input (Cnst,Var,Abs) 47 + * 0x05, 0x01, // Usage Page (Generic Desktop) 49 + * 0x09, 0x31, // Usage (Y) 51 + * 0x09, 0x30, // Usage (X) 53 + * 0x09, 0x32, // Usage (Z) 55 + * 0x09, 0x34, // Usage (Ry) 57 + * 0x09, 0x33, // Usage (Rx) 59 + * 0x09, 0x35, // Usage (Rz) 61 + * 0x15, 0x00, // Logical Minimum (0) 63 + * 0x27, 0xff, 0xff, 0x00, 0x00, // Logical Maximum (65535) 65 + * 0x75, 0x10, // Report Size (16) 70 + * 0x95, 0x06, // Report Count (6) 72 + * 0x81, 0x02, // Input (Data,Var,Abs) 74 + * 0x06, 0xf0, 0xff, // Usage Page (Vendor Usage Page 0xfff0) 76 + * 0x09, 0x59, // Usage (Vendor Usage 0x59) 79 + * 0x15, 0x00, // Logical Minimum (0) 81 + * 0x26, 0xff, 0x00, // Logical Maximum (255) 83 + * 0x75, 0x08, // Report Size (8) 86 + * 0x95, 0x01, // Report Count (1) 88 + * 0x81, 0x02, // Input (Data,Var,Abs) 90 --> Needs to be changed + * 0x09, 0x51, // Usage (Vendor Usage 0x51) 92 + * 0x15, 0x00, // Logical Minimum (0) 94 + * 0x26, 0xff, 0x00, // Logical Maximum (255) 96 + * 0x75, 0x08, // Report Size (8) 99 + * 0x95, 0x20, // Report Count (32) 101 --> Needs to be changed + * 0x81, 0x02, // Input (Data,Var,Abs) 103 + * 0x09, 0x50, // Usage (Vendor Usage 0x50) 105 + * 0x15, 0x00, // Logical Minimum (0) 107 + * 0x26, 0xff, 0x00, // Logical Maximum (255) 109 + * 0x75, 0x08, // Report Size (8) 112 + * 0x95, 0x0f, // Report Count (15) 114 + * 0x81, 0x03, // Input (Cnst,Var,Abs) 116 + * 0x09, 0x47, // Usage (Vendor Usage 0x47) 118 + * 0x85, 0xf2, // Report ID (242) 120 + * 0x15, 0x00, // Logical Minimum (0) 122 + * 0x26, 0xff, 0x00, // Logical Maximum (255) 124 + * 0x75, 0x08, // Report Size (8) 127 + * 0x95, 0x3f, // Report Count (63) 129 + * 0xb1, 0x02, // Feature (Data,Var,Abs) 131 + * 0x09, 0x48, // Usage (Vendor Usage 0x48) 133 + * 0x85, 0xf3, // Report ID (243) 135 + * 0x15, 0x00, // Logical Minimum (0) 137 + * 0x26, 0xff, 0x00, // Logical Maximum (255) 139 + * 0x75, 0x08, // Report Size (8) 142 + * 0x95, 0x3f, // Report Count (63) 144 + * 0xb1, 0x02, // Feature (Data,Var,Abs) 146 + * 0xc0, // End Collection 148 + */ + +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(hid_fix_rdesc_tca_yoke, struct hid_bpf_ctx *hctx) +{ + const int expected_length = 148; + + if (hctx->size != expected_length) + return 0; + + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */); + + if (!data) + return 0; /* EPERM */ + + /* Safety check, our probe() should take care of this though */ + if (data[1] != 0x01 /* Generic Desktop */ || data[3] != 0x04 /* Joystick */) + return 0; + + /* The report descriptor sets incorrect Input items in 2 places, resulting in a + * non-existing axis showing up. + * This change sets the correct Input which prevents the axis from showing up in Linux. + */ + + if (data[90] == 0x81 && /* Input */ + data[103] == 0x81) { /* Input */ + data[91] = 0x03; /* Input set to 0x03 Constant, Variable Absolute */ + data[104] = 0x03; /* Input set to 0X03 Constant, Variable Absolute */ + } + + return 0; +} + +HID_BPF_OPS(tca_yoke) = { + .hid_rdesc_fixup = (void *)hid_fix_rdesc_tca_yoke, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + /* ensure the kernel isn't fixed already */ + if (ctx->rdesc[91] != 0x02) /* Input for 0x59 Usage type has changed */ + ctx->retval = -EINVAL; + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/drivers/hid/bpf/progs/Wacom__ArtPen.bpf.c b/drivers/hid/bpf/progs/Wacom__ArtPen.bpf.c new file mode 100644 index 000000000000..2da680bc4e11 --- /dev/null +++ b/drivers/hid/bpf/progs/Wacom__ArtPen.bpf.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2024 Benjamin Tissoires + */ + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include <bpf/bpf_tracing.h> + +#define VID_WACOM 0x056a +#define ART_PEN_ID 0x0804 +#define PID_INTUOS_PRO_2_M 0x0357 + +HID_BPF_CONFIG( + HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_WACOM, PID_INTUOS_PRO_2_M) +); + +/* + * This filter is here for the Art Pen stylus only: + * - when used on some Wacom devices (see the list of attached PIDs), this pen + * reports pressure every other events. + * - to solve that, given that we know that the next event will be the same as + * the current one, we can emulate a smoother pressure reporting by reporting + * the mean of the previous value and the current one. + * + * We are effectively delaying the pressure by one event every other event, but + * that's less of an annoyance compared to the chunkiness of the reported data. + * + * For example, let's assume the following set of events: + * <Tip switch 0> <X 0> <Y 0> <Pressure 0 > <Tooltype 0x0804> + * <Tip switch 1> <X 1> <Y 1> <Pressure 100 > <Tooltype 0x0804> + * <Tip switch 1> <X 2> <Y 2> <Pressure 100 > <Tooltype 0x0804> + * <Tip switch 1> <X 3> <Y 3> <Pressure 200 > <Tooltype 0x0804> + * <Tip switch 1> <X 4> <Y 4> <Pressure 200 > <Tooltype 0x0804> + * <Tip switch 0> <X 5> <Y 5> <Pressure 0 > <Tooltype 0x0804> + * + * The filter will report: + * <Tip switch 0> <X 0> <Y 0> <Pressure 0 > <Tooltype 0x0804> + * <Tip switch 1> <X 1> <Y 1> <Pressure * 50*> <Tooltype 0x0804> + * <Tip switch 1> <X 2> <Y 2> <Pressure 100 > <Tooltype 0x0804> + * <Tip switch 1> <X 3> <Y 3> <Pressure *150*> <Tooltype 0x0804> + * <Tip switch 1> <X 4> <Y 4> <Pressure 200 > <Tooltype 0x0804> + * <Tip switch 0> <X 5> <Y 5> <Pressure 0 > <Tooltype 0x0804> + * + */ + +struct wacom_params { + __u16 pid; + __u16 rdesc_len; + __u8 report_id; + __u8 report_len; + struct { + __u8 tip_switch; + __u8 pressure; + __u8 tool_type; + } offsets; +}; + +/* + * Multiple device can support the same stylus, so + * we need to know which device has which offsets + */ +static const struct wacom_params devices[] = { + { + .pid = PID_INTUOS_PRO_2_M, + .rdesc_len = 949, + .report_id = 16, + .report_len = 27, + .offsets = { + .tip_switch = 1, + .pressure = 8, + .tool_type = 25, + }, + }, +}; + +static struct wacom_params params = { 0 }; + +/* HID-BPF reports a 64 bytes chunk anyway, so this ensures + * the verifier to know we are addressing the memory correctly + */ +#define PEN_REPORT_LEN 64 + +/* only odd frames are modified */ +static bool odd; + +static __u16 prev_pressure; + +static inline void *get_bits(__u8 *data, unsigned int byte_offset) +{ + return data + byte_offset; +} + +static inline __u16 *get_u16(__u8 *data, unsigned int offset) +{ + return (__u16 *)get_bits(data, offset); +} + +static inline __u8 *get_u8(__u8 *data, unsigned int offset) +{ + return (__u8 *)get_bits(data, offset); +} + +SEC(HID_BPF_DEVICE_EVENT) +int BPF_PROG(artpen_pressure_interpolate, struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, PEN_REPORT_LEN /* size */); + __u16 *pressure, *tool_type; + __u8 *tip_switch; + + if (!data) + return 0; /* EPERM check */ + + if (data[0] != params.report_id || + params.offsets.tip_switch >= PEN_REPORT_LEN || + params.offsets.pressure >= PEN_REPORT_LEN - 1 || + params.offsets.tool_type >= PEN_REPORT_LEN - 1) + return 0; /* invalid report or parameters */ + + tool_type = get_u16(data, params.offsets.tool_type); + if (*tool_type != ART_PEN_ID) + return 0; + + tip_switch = get_u8(data, params.offsets.tip_switch); + if ((*tip_switch & 0x01) == 0) { + prev_pressure = 0; + odd = true; + return 0; + } + + pressure = get_u16(data, params.offsets.pressure); + + if (odd) + *pressure = (*pressure + prev_pressure) / 2; + + prev_pressure = *pressure; + odd = !odd; + + return 0; +} + +HID_BPF_OPS(wacom_artpen) = { + .hid_device_event = (void *)artpen_pressure_interpolate, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + struct hid_bpf_ctx *hid_ctx; + __u16 pid; + int i; + + /* get a struct hid_device to access the actual pid of the device */ + hid_ctx = hid_bpf_allocate_context(ctx->hid); + if (!hid_ctx) { + ctx->retval = -ENODEV; + return -1; /* EPERM check */ + } + pid = hid_ctx->hid->product; + + ctx->retval = -EINVAL; + + /* Match the given device with the list of known devices */ + for (i = 0; i < ARRAY_SIZE(devices); i++) { + const struct wacom_params *device = &devices[i]; + + if (device->pid == pid && device->rdesc_len == ctx->rdesc_size) { + params = *device; + ctx->retval = 0; + } + } + + hid_bpf_release_context(hid_ctx); + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/drivers/hid/bpf/progs/XPPen__Artist24.bpf.c b/drivers/hid/bpf/progs/XPPen__Artist24.bpf.c new file mode 100644 index 000000000000..c24fe905c50d --- /dev/null +++ b/drivers/hid/bpf/progs/XPPen__Artist24.bpf.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2023 Benjamin Tissoires + */ + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include <bpf/bpf_tracing.h> + +#define VID_UGEE 0x28BD /* VID is shared with SinoWealth and Glorious and prob others */ +#define PID_ARTIST_24 0x093A +#define PID_ARTIST_24_PRO 0x092D + +HID_BPF_CONFIG( + HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_ARTIST_24), + HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_ARTIST_24_PRO) +); + +/* + * We need to amend the report descriptor for the following: + * - the device reports Eraser instead of using Secondary Barrel Switch + * - the pen doesn't have a rubber tail, so basically we are removing any + * eraser/invert bits + */ +static const __u8 fixed_rdesc[] = { + 0x05, 0x0d, // Usage Page (Digitizers) 0 + 0x09, 0x02, // Usage (Pen) 2 + 0xa1, 0x01, // Collection (Application) 4 + 0x85, 0x07, // Report ID (7) 6 + 0x09, 0x20, // Usage (Stylus) 8 + 0xa1, 0x00, // Collection (Physical) 10 + 0x09, 0x42, // Usage (Tip Switch) 12 + 0x09, 0x44, // Usage (Barrel Switch) 14 + 0x09, 0x5a, // Usage (Secondary Barrel Switch) 16 /* changed from 0x45 (Eraser) to 0x5a (Secondary Barrel Switch) */ + 0x15, 0x00, // Logical Minimum (0) 18 + 0x25, 0x01, // Logical Maximum (1) 20 + 0x75, 0x01, // Report Size (1) 22 + 0x95, 0x03, // Report Count (3) 24 + 0x81, 0x02, // Input (Data,Var,Abs) 26 + 0x95, 0x02, // Report Count (2) 28 + 0x81, 0x03, // Input (Cnst,Var,Abs) 30 + 0x09, 0x32, // Usage (In Range) 32 + 0x95, 0x01, // Report Count (1) 34 + 0x81, 0x02, // Input (Data,Var,Abs) 36 + 0x95, 0x02, // Report Count (2) 38 + 0x81, 0x03, // Input (Cnst,Var,Abs) 40 + 0x75, 0x10, // Report Size (16) 42 + 0x95, 0x01, // Report Count (1) 44 + 0x35, 0x00, // Physical Minimum (0) 46 + 0xa4, // Push 48 + 0x05, 0x01, // Usage Page (Generic Desktop) 49 + 0x09, 0x30, // Usage (X) 51 + 0x65, 0x13, // Unit (EnglishLinear: in) 53 + 0x55, 0x0d, // Unit Exponent (-3) 55 + 0x46, 0xf0, 0x50, // Physical Maximum (20720) 57 + 0x26, 0xff, 0x7f, // Logical Maximum (32767) 60 + 0x81, 0x02, // Input (Data,Var,Abs) 63 + 0x09, 0x31, // Usage (Y) 65 + 0x46, 0x91, 0x2d, // Physical Maximum (11665) 67 + 0x26, 0xff, 0x7f, // Logical Maximum (32767) 70 + 0x81, 0x02, // Input (Data,Var,Abs) 73 + 0xb4, // Pop 75 + 0x09, 0x30, // Usage (Tip Pressure) 76 + 0x45, 0x00, // Physical Maximum (0) 78 + 0x26, 0xff, 0x1f, // Logical Maximum (8191) 80 + 0x81, 0x42, // Input (Data,Var,Abs,Null) 83 + 0x09, 0x3d, // Usage (X Tilt) 85 + 0x15, 0x81, // Logical Minimum (-127) 87 + 0x25, 0x7f, // Logical Maximum (127) 89 + 0x75, 0x08, // Report Size (8) 91 + 0x95, 0x01, // Report Count (1) 93 + 0x81, 0x02, // Input (Data,Var,Abs) 95 + 0x09, 0x3e, // Usage (Y Tilt) 97 + 0x15, 0x81, // Logical Minimum (-127) 99 + 0x25, 0x7f, // Logical Maximum (127) 101 + 0x81, 0x02, // Input (Data,Var,Abs) 103 + 0xc0, // End Collection 105 + 0xc0, // End Collection 106 +}; + +#define TIP_SWITCH BIT(0) +#define BARREL_SWITCH BIT(1) +#define ERASER BIT(2) +/* padding BIT(3) */ +/* padding BIT(4) */ +#define IN_RANGE BIT(5) +/* padding BIT(6) */ +/* padding BIT(7) */ + +#define U16(index) (data[index] | (data[index + 1] << 8)) + +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(hid_fix_rdesc_xppen_artist24, struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */); + + if (!data) + return 0; /* EPERM check */ + + __builtin_memcpy(data, fixed_rdesc, sizeof(fixed_rdesc)); + + return sizeof(fixed_rdesc); +} + +static __u8 prev_state = 0; + +/* + * There are a few cases where the device is sending wrong event + * sequences, all related to the second button (the pen doesn't + * have an eraser switch on the tail end): + * + * whenever the second button gets pressed or released, an + * out-of-proximity event is generated and then the firmware + * compensate for the missing state (and the firmware uses + * eraser for that button): + * + * - if the pen is in range, an extra out-of-range is sent + * when the second button is pressed/released: + * // Pen is in range + * E: InRange + * + * // Second button is pressed + * E: + * E: Eraser InRange + * + * // Second button is released + * E: + * E: InRange + * + * This case is ignored by this filter, it's "valid" + * and userspace knows how to deal with it, there are just + * a few out-of-prox events generated, but the user doesn´t + * see them. + * + * - if the pen is in contact, 2 extra events are added when + * the second button is pressed/released: an out of range + * and an in range: + * + * // Pen is in contact + * E: TipSwitch InRange + * + * // Second button is pressed + * E: <- false release, needs to be filtered out + * E: Eraser InRange <- false release, needs to be filtered out + * E: TipSwitch Eraser InRange + * + * // Second button is released + * E: <- false release, needs to be filtered out + * E: InRange <- false release, needs to be filtered out + * E: TipSwitch InRange + * + */ +SEC(HID_BPF_DEVICE_EVENT) +int BPF_PROG(xppen_24_fix_eraser, struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */); + __u8 current_state, changed_state; + bool prev_tip; + + if (!data) + return 0; /* EPERM check */ + + current_state = data[1]; + + /* if the state is identical to previously, early return */ + if (current_state == prev_state) + return 0; + + prev_tip = !!(prev_state & TIP_SWITCH); + + /* + * Illegal transition: pen is in range with the tip pressed, and + * it goes into out of proximity. + * + * Ideally we should hold the event, start a timer and deliver it + * only if the timer ends, but we are not capable of that now. + * + * And it doesn't matter because when we are in such cases, this + * means we are detecting a false release. + */ + if ((current_state & IN_RANGE) == 0) { + if (prev_tip) + return HID_IGNORE_EVENT; + return 0; + } + + /* + * XOR to only set the bits that have changed between + * previous and current state + */ + changed_state = prev_state ^ current_state; + + /* Store the new state for future processing */ + prev_state = current_state; + + /* + * We get both a tipswitch and eraser change in the same HID report: + * this is not an authorized transition and is unlikely to happen + * in real life. + * This is likely to be added by the firmware to emulate the + * eraser mode so we can skip the event. + */ + if ((changed_state & (TIP_SWITCH | ERASER)) == (TIP_SWITCH | ERASER)) /* we get both a tipswitch and eraser change at the same time */ + return HID_IGNORE_EVENT; + + return 0; +} + +HID_BPF_OPS(xppen_artist_24) = { + .hid_rdesc_fixup = (void *)hid_fix_rdesc_xppen_artist24, + .hid_device_event = (void *)xppen_24_fix_eraser, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + /* + * The device exports 3 interfaces. + */ + ctx->retval = ctx->rdesc_size != 107; + if (ctx->retval) + ctx->retval = -EINVAL; + + /* ensure the kernel isn't fixed already */ + if (ctx->rdesc[17] != 0x45) /* Eraser */ + ctx->retval = -EINVAL; + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/drivers/hid/bpf/progs/XPPen__ArtistPro16Gen2.bpf.c b/drivers/hid/bpf/progs/XPPen__ArtistPro16Gen2.bpf.c new file mode 100644 index 000000000000..a669525691aa --- /dev/null +++ b/drivers/hid/bpf/progs/XPPen__ArtistPro16Gen2.bpf.c @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2023 Benjamin Tissoires + */ + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include <bpf/bpf_tracing.h> + +#define VID_UGEE 0x28BD /* VID is shared with SinoWealth and Glorious and prob others */ +#define PID_ARTIST_PRO14_GEN2 0x095A +#define PID_ARTIST_PRO16_GEN2 0x095B + +HID_BPF_CONFIG( + HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_ARTIST_PRO14_GEN2), + HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_ARTIST_PRO16_GEN2) +); + +/* + * We need to amend the report descriptor for the following: + * - the device reports Eraser instead of using Secondary Barrel Switch + * - when the eraser button is pressed and the stylus is touching the tablet, + * the device sends Tip Switch instead of sending Eraser + * + * This descriptor uses physical dimensions of the 16" device. + */ +static const __u8 fixed_rdesc[] = { + 0x05, 0x0d, // Usage Page (Digitizers) 0 + 0x09, 0x02, // Usage (Pen) 2 + 0xa1, 0x01, // Collection (Application) 4 + 0x85, 0x07, // Report ID (7) 6 + 0x09, 0x20, // Usage (Stylus) 8 + 0xa1, 0x00, // Collection (Physical) 10 + 0x09, 0x42, // Usage (Tip Switch) 12 + 0x09, 0x44, // Usage (Barrel Switch) 14 + 0x09, 0x5a, // Usage (Secondary Barrel Switch) 16 /* changed from 0x45 (Eraser) to 0x5a (Secondary Barrel Switch) */ + 0x09, 0x3c, // Usage (Invert) 18 + 0x09, 0x45, // Usage (Eraser) 16 /* created over a padding bit at offset 29-33 */ + 0x15, 0x00, // Logical Minimum (0) 20 + 0x25, 0x01, // Logical Maximum (1) 22 + 0x75, 0x01, // Report Size (1) 24 + 0x95, 0x05, // Report Count (5) 26 /* changed from 4 to 5 */ + 0x81, 0x02, // Input (Data,Var,Abs) 28 + 0x09, 0x32, // Usage (In Range) 34 + 0x15, 0x00, // Logical Minimum (0) 36 + 0x25, 0x01, // Logical Maximum (1) 38 + 0x95, 0x01, // Report Count (1) 40 + 0x81, 0x02, // Input (Data,Var,Abs) 42 + 0x95, 0x02, // Report Count (2) 44 + 0x81, 0x03, // Input (Cnst,Var,Abs) 46 + 0x75, 0x10, // Report Size (16) 48 + 0x95, 0x01, // Report Count (1) 50 + 0x35, 0x00, // Physical Minimum (0) 52 + 0xa4, // Push 54 + 0x05, 0x01, // Usage Page (Generic Desktop) 55 + 0x09, 0x30, // Usage (X) 57 + 0x65, 0x13, // Unit (EnglishLinear: in) 59 + 0x55, 0x0d, // Unit Exponent (-3) 61 + 0x46, 0xff, 0x34, // Physical Maximum (13567) 63 + 0x26, 0xff, 0x7f, // Logical Maximum (32767) 66 + 0x81, 0x02, // Input (Data,Var,Abs) 69 + 0x09, 0x31, // Usage (Y) 71 + 0x46, 0x20, 0x21, // Physical Maximum (8480) 73 + 0x26, 0xff, 0x7f, // Logical Maximum (32767) 76 + 0x81, 0x02, // Input (Data,Var,Abs) 79 + 0xb4, // Pop 81 + 0x09, 0x30, // Usage (Tip Pressure) 82 + 0x45, 0x00, // Physical Maximum (0) 84 + 0x26, 0xff, 0x3f, // Logical Maximum (16383) 86 + 0x81, 0x42, // Input (Data,Var,Abs,Null) 89 + 0x09, 0x3d, // Usage (X Tilt) 91 + 0x15, 0x81, // Logical Minimum (-127) 93 + 0x25, 0x7f, // Logical Maximum (127) 95 + 0x75, 0x08, // Report Size (8) 97 + 0x95, 0x01, // Report Count (1) 99 + 0x81, 0x02, // Input (Data,Var,Abs) 101 + 0x09, 0x3e, // Usage (Y Tilt) 103 + 0x15, 0x81, // Logical Minimum (-127) 105 + 0x25, 0x7f, // Logical Maximum (127) 107 + 0x81, 0x02, // Input (Data,Var,Abs) 109 + 0xc0, // End Collection 111 + 0xc0, // End Collection 112 +}; + +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(hid_fix_rdesc_xppen_artistpro16gen2, struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */); + + if (!data) + return 0; /* EPERM check */ + + __builtin_memcpy(data, fixed_rdesc, sizeof(fixed_rdesc)); + + /* Fix the Physical maximum values for different sizes of the device + * The 14" screen device descriptor size is 11.874" x 7.421" + */ + if (hctx->hid->product == PID_ARTIST_PRO14_GEN2) { + data[63] = 0x2e; + data[62] = 0x62; + data[73] = 0x1c; + data[72] = 0xfd; + } + + return sizeof(fixed_rdesc); +} + +static int xppen_16_fix_eraser(struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */); + + if (!data) + return 0; /* EPERM check */ + + if ((data[1] & 0x29) != 0x29) /* tip switch=1 invert=1 inrange=1 */ + return 0; + + /* xor bits 0,3 and 4: convert Tip Switch + Invert into Eraser only */ + data[1] ^= 0x19; + + return 0; +} + +/* + * Static coordinate offset table based on positive only angles + * Two tables are needed, because the logical coordinates are scaled + * + * The table can be generated by Python like this: + * >>> full_scale = 11.874 # the display width/height in inches + * >>> tip_height = 0.055677699 # the center of the pen coil distance from screen in inch (empirical) + * >>> h = tip_height * (32767 / full_scale) # height of the coil in logical coordinates + * >>> [round(h*math.sin(math.radians(d))) for d in range(0, 128)] + * [0, 13, 26, ....] + */ + +/* 14" inch screen 11.874" x 7.421" */ +static const __u16 angle_offsets_horizontal_14[128] = { + 0, 3, 5, 8, 11, 13, 16, 19, 21, 24, 27, 29, 32, 35, 37, 40, 42, 45, 47, 50, 53, + 55, 58, 60, 62, 65, 67, 70, 72, 74, 77, 79, 81, 84, 86, 88, 90, 92, 95, 97, 99, + 101, 103, 105, 107, 109, 111, 112, 114, 116, 118, 119, 121, 123, 124, 126, 127, + 129, 130, 132, 133, 134, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 148, 149, 150, 150, 151, 151, 152, 152, 153, 153, 153, 153, 153, 154, + 154, 154, 154, 154, 153, 153, 153, 153, 153, 152, 152, 151, 151, 150, 150, 149, + 148, 148, 147, 146, 145, 144, 143, 142, 141, 140, 139, 138, 137, 136, 134, 133, + 132, 130, 129, 127, 126, 124, 123 +}; +static const __u16 angle_offsets_vertical_14[128] = { + 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 59, 64, 68, 72, 76, 80, 84, + 88, 92, 96, 100, 104, 108, 112, 115, 119, 123, 127, 130, 134, 137, 141, 145, 148, + 151, 155, 158, 161, 165, 168, 171, 174, 177, 180, 183, 186, 188, 191, 194, 196, + 199, 201, 204, 206, 208, 211, 213, 215, 217, 219, 221, 223, 225, 226, 228, 230, + 231, 232, 234, 235, 236, 237, 239, 240, 240, 241, 242, 243, 243, 244, 244, 245, + 245, 246, 246, 246, 246, 246, 246, 246, 245, 245, 244, 244, 243, 243, 242, 241, + 240, 240, 239, 237, 236, 235, 234, 232, 231, 230, 228, 226, 225, 223, 221, 219, + 217, 215, 213, 211, 208, 206, 204, 201, 199, 196 +}; + +/* 16" inch screen 13.567" x 8.480" */ +static const __u16 angle_offsets_horizontal_16[128] = { + 0, 2, 5, 7, 9, 12, 14, 16, 19, 21, 23, 26, 28, 30, 33, 35, 37, 39, 42, 44, 46, 48, + 50, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 86, 88, 90, + 92, 93, 95, 97, 98, 100, 101, 103, 105, 106, 107, 109, 110, 111, 113, 114, 115, + 116, 118, 119, 120, 121, 122, 123, 124, 125, 126, 126, 127, 128, 129, 129, 130, + 130, 131, 132, 132, 132, 133, 133, 133, 134, 134, 134, 134, 134, 134, 134, 134, + 134, 134, 134, 134, 134, 133, 133, 133, 132, 132, 132, 131, 130, 130, 129, 129, + 128, 127, 126, 126, 125, 124, 123, 122, 121, 120, 119, 118, 116, 115, 114, 113, + 111, 110, 109, 107 +}; +static const __u16 angle_offsets_vertical_16[128] = { + 0, 4, 8, 11, 15, 19, 22, 26, 30, 34, 37, 41, 45, 48, 52, 56, 59, 63, 66, 70, 74, + 77, 81, 84, 88, 91, 94, 98, 101, 104, 108, 111, 114, 117, 120, 123, 126, 129, 132, + 135, 138, 141, 144, 147, 149, 152, 155, 157, 160, 162, 165, 167, 170, 172, 174, + 176, 178, 180, 182, 184, 186, 188, 190, 192, 193, 195, 197, 198, 199, 201, 202, + 203, 205, 206, 207, 208, 209, 210, 210, 211, 212, 212, 213, 214, 214, 214, 215, + 215, 215, 215, 215, 215, 215, 215, 215, 214, 214, 214, 213, 212, 212, 211, 210, + 210, 209, 208, 207, 206, 205, 203, 202, 201, 199, 198, 197, 195, 193, 192, 190, + 188, 186, 184, 182, 180, 178, 176, 174, 172 +}; + +static void compensate_coordinates_by_tilt(__u8 *data, const __u8 idx, + const __s8 tilt, const __u16 (*compensation_table)[128]) +{ + __u16 coords = data[idx+1]; + + coords <<= 8; + coords += data[idx]; + + __u8 direction = tilt > 0 ? 0 : 1; /* Positive tilt means we need to subtract the compensation (vs. negative angle where we need to add) */ + __u8 angle = tilt > 0 ? tilt : -tilt; + + if (angle > 127) + return; + + __u16 compensation = (*compensation_table)[angle]; + + if (direction == 0) { + coords = (coords > compensation) ? coords - compensation : 0; + } else { + const __u16 logical_maximum = 32767; + __u16 max = logical_maximum - compensation; + + coords = (coords < max) ? coords + compensation : logical_maximum; + } + + data[idx] = coords & 0xff; + data[idx+1] = coords >> 8; +} + +static int xppen_16_fix_angle_offset(struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */); + + if (!data) + return 0; /* EPERM check */ + + /* + * Compensate X and Y offset caused by tilt. + * + * The magnetic center moves when the pen is tilted, because the coil + * is not touching the screen. + * + * a (tilt angle) + * | /... h (coil distance from tip) + * | / + * |/______ + * |x (position offset) + * + * x = sin a * h + * + * Subtract the offset from the coordinates. Use the precomputed table! + * + * bytes 0 - report id + * 1 - buttons + * 2-3 - X coords (logical) + * 4-5 - Y coords + * 6-7 - pressure (ignore) + * 8 - tilt X + * 9 - tilt Y + */ + + __s8 tilt_x = (__s8) data[8]; + __s8 tilt_y = (__s8) data[9]; + + if (hctx->hid->product == PID_ARTIST_PRO14_GEN2) { + compensate_coordinates_by_tilt(data, 2, tilt_x, &angle_offsets_horizontal_14); + compensate_coordinates_by_tilt(data, 4, tilt_y, &angle_offsets_vertical_14); + } else if (hctx->hid->product == PID_ARTIST_PRO16_GEN2) { + compensate_coordinates_by_tilt(data, 2, tilt_x, &angle_offsets_horizontal_16); + compensate_coordinates_by_tilt(data, 4, tilt_y, &angle_offsets_vertical_16); + } + + return 0; +} + +SEC(HID_BPF_DEVICE_EVENT) +int BPF_PROG(xppen_artist_pro_16_device_event, struct hid_bpf_ctx *hctx) +{ + int ret = xppen_16_fix_angle_offset(hctx); + + if (ret) + return ret; + + return xppen_16_fix_eraser(hctx); +} + +HID_BPF_OPS(xppen_artist_pro_16) = { + .hid_rdesc_fixup = (void *)hid_fix_rdesc_xppen_artistpro16gen2, + .hid_device_event = (void *)xppen_artist_pro_16_device_event, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + /* + * The device exports 3 interfaces. + */ + ctx->retval = ctx->rdesc_size != 113; + if (ctx->retval) + ctx->retval = -EINVAL; + + /* ensure the kernel isn't fixed already */ + if (ctx->rdesc[17] != 0x45) /* Eraser */ + ctx->retval = -EINVAL; + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/drivers/hid/bpf/progs/XPPen__DecoMini4.bpf.c b/drivers/hid/bpf/progs/XPPen__DecoMini4.bpf.c new file mode 100644 index 000000000000..46d5c459d0c9 --- /dev/null +++ b/drivers/hid/bpf/progs/XPPen__DecoMini4.bpf.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2024 José Expósito + */ + +#include "vmlinux.h" +#include "hid_bpf.h" +#include "hid_bpf_helpers.h" +#include <bpf/bpf_tracing.h> + +#define VID_UGEE 0x28BD +#define PID_DECO_MINI_4 0x0929 +#define RDESC_SIZE_PAD 177 +#define RDESC_SIZE_PEN 109 +#define PAD_REPORT_ID 0x06 + +/* + * XP-Pen devices return a descriptor with the values the driver should use when + * one of its interfaces is queried. For this device the descriptor is: + * + * 0E 03 60 4F 88 3B 06 00 FF 1F D8 13 + * ----- ----- ----- ----- + * | | | | + * | | | `- Resolution: 5080 (13d8) + * | | `- Maximum pressure: 8191 (1FFF) + * | `- Logical maximum Y: 15240 (3B88) + * `- Logical maximum X: 20320 (4F60) + * + * The physical maximum is calculated as (logical_max * 1000) / resolution. + */ +#define LOGICAL_MAX_X 0x60, 0x4F +#define LOGICAL_MAX_Y 0x88, 0x3B +#define PHYSICAL_MAX_X 0xA0, 0x0F +#define PHYSICAL_MAX_Y 0xB8, 0x0B +#define PRESSURE_MAX 0xFF, 0x1F + +HID_BPF_CONFIG( + HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_DECO_MINI_4) +); + +/* + * The tablet send these values when the pad buttons are pressed individually: + * + * Buttons released: 06 00 00 00 00 00 00 00 + * Button 1: 06 00 05 00 00 00 00 00 -> b + * Button 2: 06 00 08 00 00 00 00 00 -> e + * Button 3: 06 04 00 00 00 00 00 00 -> LAlt + * Button 4: 06 00 2c 00 00 00 00 00 -> Space + * Button 5: 06 01 16 00 00 00 00 00 -> LControl + s + * Button 6: 06 01 1d 00 00 00 00 00 -> LControl + z + * + * When multiple buttons are pressed at the same time, the values used to + * identify the buttons are identical, but they appear in different bytes of the + * record. For example, when button 2 (0x08) and button 1 (0x05) are pressed, + * this is the report: + * + * Buttons 2 and 1: 06 00 08 05 00 00 00 00 -> e + b + * + * Buttons 1, 2, 4, 5 and 6 can be matched by finding their values in the + * report. + * + * Button 3 is pressed when the 3rd bit is 1. For example, pressing buttons 3 + * and 5 generates this report: + * + * Buttons 3 and 5: 06 05 16 00 00 00 00 00 -> LControl + LAlt + s + * -- -- + * | | + * | `- Button 5 (0x16) + * `- 0x05 = 0101. Button 3 is pressed + * ^ + * + * pad_buttons contains a list of buttons that can be matched in + * HID_BPF_DEVICE_EVENT. Button 3 as it has a dedicated bit. + */ +static const __u8 pad_buttons[] = { 0x05, 0x08, 0x00, 0x2C, 0x16, 0x1D }; + +static const __u8 fixed_pad_rdesc[] = { + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x07, /* Usage (Keypad), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x06, /* Report ID (6), */ + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x39, /* Usage (Tablet Function Keys), */ + 0xA0, /* Collection (Physical), */ + 0x05, 0x09, /* Usage Page (Button), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x06, /* Report Count (6), */ + 0x19, 0x01, /* Usage Minimum (01h), */ + 0x29, 0x06, /* Usage Maximum (06h), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x32, /* Report Count (50), */ + 0x81, 0x01, /* Input (Constant), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; + +static const __u8 fixed_pen_rdesc[] = { + 0x05, 0x0d, /* Usage Page (Digitizers), */ + 0x09, 0x01, /* Usage (Digitizer), */ + 0xa1, 0x01, /* Collection (Application), */ + 0x85, 0x07, /* Report ID (7), */ + 0x09, 0x20, /* Usage (Stylus), */ + 0xa1, 0x00, /* Collection (Physical), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x09, 0x44, /* Usage (Barrel Switch), */ + 0x09, 0x46, /* Usage (Tablet Pick), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x02, /* Report Count (2), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x09, 0x32, /* Usage (In Range), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x02, /* Report Count (2), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0x35, 0x00, /* Physical Minimum (0), */ + 0xa4, /* Push, */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x30, /* Usage (X), */ + 0x65, 0x13, /* Unit (Inch), */ + 0x55, 0x0d, /* Unit Exponent (-3), */ + 0x26, LOGICAL_MAX_X, /* Logical Maximum, */ + 0x46, PHYSICAL_MAX_X, /* Physical Maximum, */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (Y), */ + 0x26, LOGICAL_MAX_Y, /* Logical Maximum, */ + 0x46, PHYSICAL_MAX_Y, /* Physical Maximum, */ + 0x81, 0x02, /* Input (Variable), */ + 0xb4, /* Pop, */ + 0x09, 0x30, /* Usage (Tip Pressure), */ + 0x45, 0x00, /* Physical Maximum (0), */ + 0x26, PRESSURE_MAX, /* Logical Maximum, */ + 0x75, 0x0D, /* Report Size (13), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x13, /* Report Count (19), */ + 0x81, 0x01, /* Input (Constant), */ + 0xc0, /* End Collection, */ + 0xc0, /* End Collection */ +}; + +static const size_t fixed_pad_rdesc_size = sizeof(fixed_pad_rdesc); +static const size_t fixed_pen_rdesc_size = sizeof(fixed_pen_rdesc); + +SEC(HID_BPF_RDESC_FIXUP) +int BPF_PROG(hid_rdesc_fixup_xppen_deco_mini_4, struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0, HID_MAX_DESCRIPTOR_SIZE); + + if (!data) + return 0; /* EPERM check */ + + if (hctx->size == RDESC_SIZE_PAD) { + __builtin_memcpy(data, fixed_pad_rdesc, fixed_pad_rdesc_size); + return fixed_pad_rdesc_size; + } else if (hctx->size == RDESC_SIZE_PEN) { + __builtin_memcpy(data, fixed_pen_rdesc, fixed_pen_rdesc_size); + return fixed_pen_rdesc_size; + } + + return 0; +} + +SEC(HID_BPF_DEVICE_EVENT) +int BPF_PROG(hid_device_event_xppen_deco_mini_4, struct hid_bpf_ctx *hctx) +{ + __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 8 /* size */); + __u8 button_mask = 0; + int d, b; + + if (!data) + return 0; /* EPERM check */ + + if (data[0] != PAD_REPORT_ID) + return 0; + + /* data[1] stores the status of BTN_2 in the 3rd bit*/ + if (data[1] & BIT(2)) + button_mask |= BIT(2); + + /* The rest of the descriptor stores the buttons as in pad_buttons */ + for (d = 2; d < 8; d++) { + for (b = 0; b < sizeof(pad_buttons); b++) { + if (data[d] != 0 && data[d] == pad_buttons[b]) + button_mask |= BIT(b); + } + } + + __u8 report[8] = {PAD_REPORT_ID, button_mask, 0x00}; + + __builtin_memcpy(data, report, sizeof(report)); + + return 0; +} + +HID_BPF_OPS(deco_mini_4) = { + .hid_device_event = (void *)hid_device_event_xppen_deco_mini_4, + .hid_rdesc_fixup = (void *)hid_rdesc_fixup_xppen_deco_mini_4, +}; + +SEC("syscall") +int probe(struct hid_bpf_probe_args *ctx) +{ + /* + * The device has 2 modes: The compatibility mode, enabled by default, + * and the raw mode, that can be activated by sending a buffer of magic + * data to a certain USB endpoint. + * + * Depending on the mode, different interfaces of the device are used: + * - First interface: Pad in compatibility mode + * - Second interface: Pen in compatibility mode + * - Third interface: Only used in raw mode + * + * We'll use the device in compatibility mode. + */ + ctx->retval = ctx->rdesc_size != RDESC_SIZE_PAD && + ctx->rdesc_size != RDESC_SIZE_PEN; + if (ctx->retval) + ctx->retval = -EINVAL; + + return 0; +} + +char _license[] SEC("license") = "GPL"; diff --git a/drivers/hid/bpf/progs/hid_bpf.h b/drivers/hid/bpf/progs/hid_bpf.h new file mode 100644 index 000000000000..7cabd1b2e837 --- /dev/null +++ b/drivers/hid/bpf/progs/hid_bpf.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2022 Benjamin Tissoires + */ + +#ifndef ____HID_BPF__H +#define ____HID_BPF__H + +#define HID_BPF_DEVICE_EVENT "struct_ops/hid_device_event" +#define HID_BPF_RDESC_FIXUP "struct_ops/hid_rdesc_fixup" +#define HID_BPF_OPS(name) SEC(".struct_ops.link") \ + struct hid_bpf_ops name +#define hid_set_name(_hdev, _name) __builtin_memcpy(_hdev->name, _name, sizeof(_name)) + +struct hid_bpf_probe_args { + unsigned int hid; + unsigned int rdesc_size; + unsigned char rdesc[4096]; + int retval; +}; + +#endif /* ____HID_BPF__H */ diff --git a/drivers/hid/bpf/progs/hid_bpf_helpers.h b/drivers/hid/bpf/progs/hid_bpf_helpers.h new file mode 100644 index 000000000000..3ba24d125a08 --- /dev/null +++ b/drivers/hid/bpf/progs/hid_bpf_helpers.h @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2022 Benjamin Tissoires + */ + +#ifndef __HID_BPF_HELPERS_H +#define __HID_BPF_HELPERS_H + +#include "vmlinux.h" +#include <bpf/bpf_helpers.h> +#include <linux/errno.h> + +extern __u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx, + unsigned int offset, + const size_t __sz) __ksym; +extern struct hid_bpf_ctx *hid_bpf_allocate_context(unsigned int hid_id) __ksym; +extern void hid_bpf_release_context(struct hid_bpf_ctx *ctx) __ksym; +extern int hid_bpf_hw_request(struct hid_bpf_ctx *ctx, + __u8 *data, + size_t buf__sz, + enum hid_report_type type, + enum hid_class_request reqtype) __ksym; + +#define HID_MAX_DESCRIPTOR_SIZE 4096 +#define HID_IGNORE_EVENT -1 + +/* extracted from <linux/input.h> */ +#define BUS_ANY 0x00 +#define BUS_PCI 0x01 +#define BUS_ISAPNP 0x02 +#define BUS_USB 0x03 +#define BUS_HIL 0x04 +#define BUS_BLUETOOTH 0x05 +#define BUS_VIRTUAL 0x06 +#define BUS_ISA 0x10 +#define BUS_I8042 0x11 +#define BUS_XTKBD 0x12 +#define BUS_RS232 0x13 +#define BUS_GAMEPORT 0x14 +#define BUS_PARPORT 0x15 +#define BUS_AMIGA 0x16 +#define BUS_ADB 0x17 +#define BUS_I2C 0x18 +#define BUS_HOST 0x19 +#define BUS_GSC 0x1A +#define BUS_ATARI 0x1B +#define BUS_SPI 0x1C +#define BUS_RMI 0x1D +#define BUS_CEC 0x1E +#define BUS_INTEL_ISHTP 0x1F +#define BUS_AMD_SFH 0x20 + +/* extracted from <linux/hid.h> */ +#define HID_GROUP_ANY 0x0000 +#define HID_GROUP_GENERIC 0x0001 +#define HID_GROUP_MULTITOUCH 0x0002 +#define HID_GROUP_SENSOR_HUB 0x0003 +#define HID_GROUP_MULTITOUCH_WIN_8 0x0004 +#define HID_GROUP_RMI 0x0100 +#define HID_GROUP_WACOM 0x0101 +#define HID_GROUP_LOGITECH_DJ_DEVICE 0x0102 +#define HID_GROUP_STEAM 0x0103 +#define HID_GROUP_LOGITECH_27MHZ_DEVICE 0x0104 +#define HID_GROUP_VIVALDI 0x0105 + +/* include/linux/mod_devicetable.h defines as (~0), but that gives us negative size arrays */ +#define HID_VID_ANY 0x0000 +#define HID_PID_ANY 0x0000 + +#define BIT(n) (1UL << (n)) +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +/* Helper macro to convert (foo, __LINE__) into foo134 so we can use __LINE__ for + * field/variable names + */ +#define COMBINE1(X, Y) X ## Y +#define COMBINE(X, Y) COMBINE1(X, Y) + +/* Macro magic: + * __uint(foo, 123) creates a int (*foo)[1234] + * + * We use that macro to declare an anonymous struct with several + * fields, each is the declaration of an pointer to an array of size + * bus/group/vid/pid. (Because it's a pointer to such an array, actual storage + * would be sizeof(pointer) rather than sizeof(array). Not that we ever + * instantiate it anyway). + * + * This is only used for BTF introspection, we can later check "what size + * is the bus array" in the introspection data and thus extract the bus ID + * again. + * + * And we use the __LINE__ to give each of our structs a unique name so the + * BPF program writer doesn't have to. + * + * $ bpftool btf dump file target/bpf/HP_Elite_Presenter.bpf.o + * shows the inspection data, start by searching for .hid_bpf_config + * and working backwards from that (each entry references the type_id of the + * content). + */ + +#define HID_DEVICE(b, g, ven, prod) \ + struct { \ + __uint(name, 0); \ + __uint(bus, (b)); \ + __uint(group, (g)); \ + __uint(vid, (ven)); \ + __uint(pid, (prod)); \ + } COMBINE(_entry, __LINE__) + +/* Macro magic below is to make HID_BPF_CONFIG() look like a function call that + * we can pass multiple HID_DEVICE() invocations in. + * + * For up to 16 arguments, HID_BPF_CONFIG(one, two) resolves to + * + * union { + * HID_DEVICE(...); + * HID_DEVICE(...); + * } _device_ids SEC(".hid_bpf_config") + * + */ + +/* Returns the number of macro arguments, this expands + * NARGS(a, b, c) to NTH_ARG(a, b, c, 15, 14, 13, .... 4, 3, 2, 1). + * NTH_ARG always returns the 16th argument which in our case is 3. + * + * If we want more than 16 values _COUNTDOWN and _NTH_ARG both need to be + * updated. + */ +#define _NARGS(...) _NARGS1(__VA_ARGS__, _COUNTDOWN) +#define _NARGS1(...) _NTH_ARG(__VA_ARGS__) + +/* Add to this if we need more than 16 args */ +#define _COUNTDOWN \ + 15, 14, 13, 12, 11, 10, 9, 8, \ + 7, 6, 5, 4, 3, 2, 1, 0 + +/* Return the 16 argument passed in. See _NARGS above for usage. Note this is + * 1-indexed. + */ +#define _NTH_ARG( \ + _1, _2, _3, _4, _5, _6, _7, _8, \ + _9, _10, _11, _12, _13, _14, _15,\ + N, ...) N + +/* Turns EXPAND(_ARG, a, b, c) into _ARG3(a, b, c) */ +#define _EXPAND(func, ...) COMBINE(func, _NARGS(__VA_ARGS__)) (__VA_ARGS__) + +/* And now define all the ARG macros for each number of args we want to accept */ +#define _ARG1(_1) _1; +#define _ARG2(_1, _2) _1; _2; +#define _ARG3(_1, _2, _3) _1; _2; _3; +#define _ARG4(_1, _2, _3, _4) _1; _2; _3; _4; +#define _ARG5(_1, _2, _3, _4, _5) _1; _2; _3; _4; _5; +#define _ARG6(_1, _2, _3, _4, _5, _6) _1; _2; _3; _4; _5; _6; +#define _ARG7(_1, _2, _3, _4, _5, _6, _7) _1; _2; _3; _4; _5; _6; _7; +#define _ARG8(_1, _2, _3, _4, _5, _6, _7, _8) _1; _2; _3; _4; _5; _6; _7; _8; +#define _ARG9(_1, _2, _3, _4, _5, _6, _7, _8, _9) _1; _2; _3; _4; _5; _6; _7; _8; _9; +#define _ARG10(_1, _2, _3, _4, _5, _6, _7, _8, _9, _a) _1; _2; _3; _4; _5; _6; _7; _8; _9; _a; +#define _ARG11(_1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b) _1; _2; _3; _4; _5; _6; _7; _8; _9; _a; _b; +#define _ARG12(_1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c) _1; _2; _3; _4; _5; _6; _7; _8; _9; _a; _b; _c; +#define _ARG13(_1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, _d) _1; _2; _3; _4; _5; _6; _7; _8; _9; _a; _b; _c; _d; +#define _ARG14(_1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, _d, _e) _1; _2; _3; _4; _5; _6; _7; _8; _9; _a; _b; _c; _d; _e; +#define _ARG15(_1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, _d, _e, _f) _1; _2; _3; _4; _5; _6; _7; _8; _9; _a; _b; _c; _d; _e; _f; + + +#define HID_BPF_CONFIG(...) union { \ + _EXPAND(_ARG, __VA_ARGS__) \ +} _device_ids SEC(".hid_bpf_config") + +#endif /* __HID_BPF_HELPERS_H */ diff --git a/drivers/hid/bpf/progs/hid_report_helpers.h b/drivers/hid/bpf/progs/hid_report_helpers.h new file mode 100644 index 000000000000..9b2a48e4a311 --- /dev/null +++ b/drivers/hid/bpf/progs/hid_report_helpers.h @@ -0,0 +1,2978 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2024 Red Hat, Inc + */ + +// THIS FILE IS GENERATED, DO NOT EDIT + +#pragma once + + +/* Macros for composing HID reports. + * + * HID Fields are added manually to the template, please add to it as needed + * for any individual device. The Usage Pages and Usages are generated. + * + * Some macros have a _i8, _i16, or _i32 suffix. Pick the + * right suffix given the passed-in value. + */ + +/* + * This macro behaves like static_assert(), failing to + * compile if its argument is not true. However, it always + * returns 0, which allows using it everywhere an expression + * can be used. + */ +#define must_be(e, msg_) \ +( \ + 0 * (int) sizeof( \ + struct { \ + _Static_assert(e, msg_); \ + int ISO_C_forbids_a_struct_with_no_members; \ + } \ + ) \ +) + +/* Ensure the given value fits within 8/16/32 bits */ +#define i4(v_) (((__u8)(v_) & 0xf) + must_be((v_) >= -0x8 && (v_) <= 0x7, "not a i4")) +#define i8(v_) ((__u8)(v_) + must_be((v_) >= -0x80 && (v_) <= 0xff, "not a i8/u8")) +#define i16(v_) ((__u16)(v_) + must_be((v_) >= -0x8000 && (v_) <= 0xffff, "not a i16/u16")) +#define i32(v_) ((__u32)(v_) + must_be((v_) >= -0x80000000L && (v_) <= 0xffffffffL, \ + "not a i32/u16")) + +/* Split a value across multiple bytes in LE order */ +#define LE16(v_) i16(v_) & 0xff, ((v_) >> 8) & 0xff +#define LE32(v_) i32(v_) & 0xff, ((v_) >> 8) & 0xff, ((v_) >> 16) & 0xff, ((v_) >> 24) & 0xff + +/* Collections require two items in the report descriptor, the start + * of the collection (0xa?) and the EndCollection item (0xc?). + * This macro provides both, use like this: + * + * static const __u8 fixed_rdesc[] = { + * UsagePage_Generic_Desktop + * Usage_GD_Keyboard + * CollectionApplication( ← Open the collection + * ReportId(3) + * LogicalMinimum_i8(0) + * LogicalMaximum_i8(1) + * // other fields + * ) ← End EndCollection + * + * Collections may be nested. + */ +#define Collection(col_, ...) 0xa1, i8(col_), __VA_ARGS__ 0xc0, +#define CollectionPhysical(...) Collection(0x00, __VA_ARGS__) +#define CollectionApplication(...) Collection(0x01, __VA_ARGS__) +#define CollectionLogical(...) Collection(0x02, __VA_ARGS__) + +/* See Collections, this macro provides Push and Pop with + * elements in between + */ +#define PushPop(...) 0xa4, __VA_ARGS__ 0xb4, + +/* Arguments to use in bitwise-or for Input, Output, Feature */ +#define Const 0x1 +#define Var 0x2 +#define Arr 0x0 +#define Abs 0x0 +#define Rel 0x4 +#define Null 0x40 +#define Buff 0x0100 + +/* Use like this: Input(Var|Abs) */ +#define Input(i_) 0x081, i8(i_), +#define Output(i_) 0x091, i8(i_), +#define Feature(i_) 0x0b1, i8(i_), + +#define Input_i16(i_) 0x082, LE16(i_), +#define Output_i16(i_) 0x092, LE16(i_), +#define Feature_i16(i_) 0x0b2, LE16(i_), + +#define ReportId(id_) 0x85, i8(id_), +#define ReportSize(sz_) 0x75, i8(sz_), +#define ReportCount(cnt_) 0x95, i8(cnt_), + +#define LogicalMinimum_i8(min_) 0x15, i8(min_), +#define LogicalMinimum_i16(min_) 0x16, LE16(min_), +#define LogicalMinimum_i32(min_) 0x17, LE32(min_), + +#define LogicalMaximum_i8(max_) 0x25, i8(max_), +#define LogicalMaximum_i16(max_) 0x26, LE16(max_), +#define LogicalMaximum_i32(max_) 0x27, LE32(max_), + +#define PhysicalMinimum_i8(min_) 0x35, i8(min_), +#define PhysicalMinimum_i16(min_) 0x36, LE16(min_), +#define PhysicalMinimum_i32(min_) 0x37, LE32(min_), + +#define PhysicalMaximum_i8(max_) 0x45, i8(max_), +#define PhysicalMaximum_i16(max_) 0x46, LE16(max_), +#define PhysicalMaximum_i32(max_) 0x47, LE32(max_), + +#define UsageMinimum_i8(min_) 0x19, i8(min_), +#define UsageMinimum_i16(min_) 0x1a, LE16(min_), + +#define UsageMaximum_i8(max_) 0x29, i8(max_), +#define UsageMaximum_i16(max_) 0x2a, LE16(max_), + +#define UsagePage_i8(p_) 0x05, i8(p_), +#define UsagePage_i16(p_) 0x06, LE16(p_), + +#define Usage_i8(u_) 0x09, i8(u_), +#define Usage_i16(u_) 0x0a, LE16(u_), +#define Usage_i32(u_) 0x0b, LE32(u_), + +#define SILinear 0x1 +#define SIRotation 0x2 +#define EnglishLinear 0x3 +#define EnglishRotation 0x4 +#define cm (SILinear | (0x1 << 4)) +#define rad (SIRotation | (0x1 << 4)) +#define deg (EnglishRotation | (0x1 << 4)) +#define in (EnglishLinear | (0x1 << 4)) +/* Use as Unit(cm) or Unit(rad) or similar. + * This macro currently defaults to exponent 1 only, so no + * cm^2 or others + */ +#define Unit(u_) Unit_i8(u_) +#define Unit_i8(u_) 0x65, i8(u_), +#define Unit_i16(u_) 0x66, i16(u_), +#define Unit_i32(u_) 0x67, i32(u_), + +#define UnitExponent(u_) 0x55, i4(u_), + +/* A macro to generate a vendor-specific padding-only + * report with Report ID 0xac of the given size in bytes. + * The size is inclusive of the 1 byte Report ID prefix. + * + * HID-BPF requires that at least one report has + * the same size as the original report from the device. + * The easy way to ensure that is to add this + * macro as the last element of your CollectionApplication + * other reports can be of any size less than this. + * + * e.g. + * static __u8 fixed_rdesc = [ + * UsagePage_Generic_Desktop + * Usage_GD_Keyboard + * CollectionApplication( + * ... intended rdesc items go here ... + * FixedSizeVendorReport(12) + * ) + * ]; + * + * If the FixedSizeVendorReport is placed outside + * a CollectionApplication it will result in + * an extra useless evdev node being created. + */ +#define FixedSizeVendorReport(bytes_) \ + UsagePage_Vendor(0xffff) \ + Usage_i8(0x01) \ + CollectionPhysical( \ + ReportId(0xac) \ + ReportSize(8) \ + ReportCount((bytes_) - 1) \ + Input(Const) \ + ) + +/* ----- Generated Usage Pages and Usages ------ */ +#define UsagePage_GenericDesktop UsagePage_i8(0x1) +#define UsagePage_SimulationControls UsagePage_i8(0x2) +#define UsagePage_VRControls UsagePage_i8(0x3) +#define UsagePage_SportControls UsagePage_i8(0x4) +#define UsagePage_GameControls UsagePage_i8(0x5) +#define UsagePage_GenericDeviceControls UsagePage_i8(0x6) +#define UsagePage_KeyboardKeypad UsagePage_i8(0x7) +#define UsagePage_LED UsagePage_i8(0x8) +#define UsagePage_Button UsagePage_i8(0x9) +#define UsagePage_Ordinal UsagePage_i8(0xa) +#define UsagePage_TelephonyDevice UsagePage_i8(0xb) +#define UsagePage_Consumer UsagePage_i8(0xc) +#define UsagePage_Digitizers UsagePage_i8(0xd) +#define UsagePage_Haptics UsagePage_i8(0xe) +#define UsagePage_PhysicalInputDevice UsagePage_i8(0xf) +#define UsagePage_Unicode UsagePage_i8(0x10) +#define UsagePage_SoC UsagePage_i8(0x11) +#define UsagePage_EyeandHeadTrackers UsagePage_i8(0x12) +#define UsagePage_AuxiliaryDisplay UsagePage_i8(0x14) +#define UsagePage_Sensors UsagePage_i8(0x20) +#define UsagePage_MedicalInstrument UsagePage_i8(0x40) +#define UsagePage_BrailleDisplay UsagePage_i8(0x41) +#define UsagePage_LightingAndIllumination UsagePage_i8(0x59) +#define UsagePage_Monitor UsagePage_i8(0x80) +#define UsagePage_MonitorEnumerated UsagePage_i8(0x81) +#define UsagePage_VESAVirtualControls UsagePage_i8(0x82) +#define UsagePage_Power UsagePage_i8(0x84) +#define UsagePage_BatterySystem UsagePage_i8(0x85) +#define UsagePage_BarcodeScanner UsagePage_i8(0x8c) +#define UsagePage_Scales UsagePage_i8(0x8d) +#define UsagePage_MagneticStripeReader UsagePage_i8(0x8e) +#define UsagePage_CameraControl UsagePage_i8(0x90) +#define UsagePage_Arcade UsagePage_i8(0x91) +#define UsagePage_FIDOAlliance UsagePage_i16(0xf1d0) +#define UsagePage_Vendor(u_) \ + UsagePage_i16((u_) + must_be(((u_) & 0xff00) == 0xff00, "not a 0xff00 vendor page")) + +#define Usage_GD_Pointer Usage_i8(0x1) +#define Usage_GD_Mouse Usage_i8(0x2) +#define Usage_GD_Joystick Usage_i8(0x4) +#define Usage_GD_Gamepad Usage_i8(0x5) +#define Usage_GD_Keyboard Usage_i8(0x6) +#define Usage_GD_Keypad Usage_i8(0x7) +#define Usage_GD_MultiaxisController Usage_i8(0x8) +#define Usage_GD_TabletPCSystemControls Usage_i8(0x9) +#define Usage_GD_WaterCoolingDevice Usage_i8(0xa) +#define Usage_GD_ComputerChassisDevice Usage_i8(0xb) +#define Usage_GD_WirelessRadioControls Usage_i8(0xc) +#define Usage_GD_PortableDeviceControl Usage_i8(0xd) +#define Usage_GD_SystemMultiAxisController Usage_i8(0xe) +#define Usage_GD_SpatialController Usage_i8(0xf) +#define Usage_GD_AssistiveControl Usage_i8(0x10) +#define Usage_GD_DeviceDock Usage_i8(0x11) +#define Usage_GD_DockableDevice Usage_i8(0x12) +#define Usage_GD_CallStateManagementControl Usage_i8(0x13) +#define Usage_GD_X Usage_i8(0x30) +#define Usage_GD_Y Usage_i8(0x31) +#define Usage_GD_Z Usage_i8(0x32) +#define Usage_GD_Rx Usage_i8(0x33) +#define Usage_GD_Ry Usage_i8(0x34) +#define Usage_GD_Rz Usage_i8(0x35) +#define Usage_GD_Slider Usage_i8(0x36) +#define Usage_GD_Dial Usage_i8(0x37) +#define Usage_GD_Wheel Usage_i8(0x38) +#define Usage_GD_HatSwitch Usage_i8(0x39) +#define Usage_GD_CountedBuffer Usage_i8(0x3a) +#define Usage_GD_ByteCount Usage_i8(0x3b) +#define Usage_GD_MotionWakeup Usage_i8(0x3c) +#define Usage_GD_Start Usage_i8(0x3d) +#define Usage_GD_Select Usage_i8(0x3e) +#define Usage_GD_Vx Usage_i8(0x40) +#define Usage_GD_Vy Usage_i8(0x41) +#define Usage_GD_Vz Usage_i8(0x42) +#define Usage_GD_Vbrx Usage_i8(0x43) +#define Usage_GD_Vbry Usage_i8(0x44) +#define Usage_GD_Vbrz Usage_i8(0x45) +#define Usage_GD_Vno Usage_i8(0x46) +#define Usage_GD_FeatureNotification Usage_i8(0x47) +#define Usage_GD_ResolutionMultiplier Usage_i8(0x48) +#define Usage_GD_Qx Usage_i8(0x49) +#define Usage_GD_Qy Usage_i8(0x4a) +#define Usage_GD_Qz Usage_i8(0x4b) +#define Usage_GD_Qw Usage_i8(0x4c) +#define Usage_GD_SystemControl Usage_i8(0x80) +#define Usage_GD_SystemPowerDown Usage_i8(0x81) +#define Usage_GD_SystemSleep Usage_i8(0x82) +#define Usage_GD_SystemWakeUp Usage_i8(0x83) +#define Usage_GD_SystemContextMenu Usage_i8(0x84) +#define Usage_GD_SystemMainMenu Usage_i8(0x85) +#define Usage_GD_SystemAppMenu Usage_i8(0x86) +#define Usage_GD_SystemMenuHelp Usage_i8(0x87) +#define Usage_GD_SystemMenuExit Usage_i8(0x88) +#define Usage_GD_SystemMenuSelect Usage_i8(0x89) +#define Usage_GD_SystemMenuRight Usage_i8(0x8a) +#define Usage_GD_SystemMenuLeft Usage_i8(0x8b) +#define Usage_GD_SystemMenuUp Usage_i8(0x8c) +#define Usage_GD_SystemMenuDown Usage_i8(0x8d) +#define Usage_GD_SystemColdRestart Usage_i8(0x8e) +#define Usage_GD_SystemWarmRestart Usage_i8(0x8f) +#define Usage_GD_DpadUp Usage_i8(0x90) +#define Usage_GD_DpadDown Usage_i8(0x91) +#define Usage_GD_DpadRight Usage_i8(0x92) +#define Usage_GD_DpadLeft Usage_i8(0x93) +#define Usage_GD_IndexTrigger Usage_i8(0x94) +#define Usage_GD_PalmTrigger Usage_i8(0x95) +#define Usage_GD_Thumbstick Usage_i8(0x96) +#define Usage_GD_SystemFunctionShift Usage_i8(0x97) +#define Usage_GD_SystemFunctionShiftLock Usage_i8(0x98) +#define Usage_GD_SystemFunctionShiftLockIndicator Usage_i8(0x99) +#define Usage_GD_SystemDismissNotification Usage_i8(0x9a) +#define Usage_GD_SystemDoNotDisturb Usage_i8(0x9b) +#define Usage_GD_SystemDock Usage_i8(0xa0) +#define Usage_GD_SystemUndock Usage_i8(0xa1) +#define Usage_GD_SystemSetup Usage_i8(0xa2) +#define Usage_GD_SystemBreak Usage_i8(0xa3) +#define Usage_GD_SystemDebuggerBreak Usage_i8(0xa4) +#define Usage_GD_ApplicationBreak Usage_i8(0xa5) +#define Usage_GD_ApplicationDebuggerBreak Usage_i8(0xa6) +#define Usage_GD_SystemSpeakerMute Usage_i8(0xa7) +#define Usage_GD_SystemHibernate Usage_i8(0xa8) +#define Usage_GD_SystemMicrophoneMute Usage_i8(0xa9) +#define Usage_GD_SystemDisplayInvert Usage_i8(0xb0) +#define Usage_GD_SystemDisplayInternal Usage_i8(0xb1) +#define Usage_GD_SystemDisplayExternal Usage_i8(0xb2) +#define Usage_GD_SystemDisplayBoth Usage_i8(0xb3) +#define Usage_GD_SystemDisplayDual Usage_i8(0xb4) +#define Usage_GD_SystemDisplayToggleIntExtMode Usage_i8(0xb5) +#define Usage_GD_SystemDisplaySwapPrimarySecondary Usage_i8(0xb6) +#define Usage_GD_SystemDisplayToggleLCDAutoscale Usage_i8(0xb7) +#define Usage_GD_SensorZone Usage_i8(0xc0) +#define Usage_GD_RPM Usage_i8(0xc1) +#define Usage_GD_CoolantLevel Usage_i8(0xc2) +#define Usage_GD_CoolantCriticalLevel Usage_i8(0xc3) +#define Usage_GD_CoolantPump Usage_i8(0xc4) +#define Usage_GD_ChassisEnclosure Usage_i8(0xc5) +#define Usage_GD_WirelessRadioButton Usage_i8(0xc6) +#define Usage_GD_WirelessRadioLED Usage_i8(0xc7) +#define Usage_GD_WirelessRadioSliderSwitch Usage_i8(0xc8) +#define Usage_GD_SystemDisplayRotationLockButton Usage_i8(0xc9) +#define Usage_GD_SystemDisplayRotationLockSliderSwitch Usage_i8(0xca) +#define Usage_GD_ControlEnable Usage_i8(0xcb) +#define Usage_GD_DockableDeviceUniqueID Usage_i8(0xd0) +#define Usage_GD_DockableDeviceVendorID Usage_i8(0xd1) +#define Usage_GD_DockableDevicePrimaryUsagePage Usage_i8(0xd2) +#define Usage_GD_DockableDevicePrimaryUsageID Usage_i8(0xd3) +#define Usage_GD_DockableDeviceDockingState Usage_i8(0xd4) +#define Usage_GD_DockableDeviceDisplayOcclusion Usage_i8(0xd5) +#define Usage_GD_DockableDeviceObjectType Usage_i8(0xd6) +#define Usage_GD_CallActiveLED Usage_i8(0xe0) +#define Usage_GD_CallMuteToggle Usage_i8(0xe1) +#define Usage_GD_CallMuteLED Usage_i8(0xe2) +#define Usage_SC_FlightSimulationDevice Usage_i8(0x1) +#define Usage_SC_AutomobileSimulationDevice Usage_i8(0x2) +#define Usage_SC_TankSimulationDevice Usage_i8(0x3) +#define Usage_SC_SpaceshipSimulationDevice Usage_i8(0x4) +#define Usage_SC_SubmarineSimulationDevice Usage_i8(0x5) +#define Usage_SC_SailingSimulationDevice Usage_i8(0x6) +#define Usage_SC_MotorcycleSimulationDevice Usage_i8(0x7) +#define Usage_SC_SportsSimulationDevice Usage_i8(0x8) +#define Usage_SC_AirplaneSimulationDevice Usage_i8(0x9) +#define Usage_SC_HelicopterSimulationDevice Usage_i8(0xa) +#define Usage_SC_MagicCarpetSimulationDevice Usage_i8(0xb) +#define Usage_SC_BicycleSimulationDevice Usage_i8(0xc) +#define Usage_SC_FlightControlStick Usage_i8(0x20) +#define Usage_SC_FlightStick Usage_i8(0x21) +#define Usage_SC_CyclicControl Usage_i8(0x22) +#define Usage_SC_CyclicTrim Usage_i8(0x23) +#define Usage_SC_FlightYoke Usage_i8(0x24) +#define Usage_SC_TrackControl Usage_i8(0x25) +#define Usage_SC_Aileron Usage_i8(0xb0) +#define Usage_SC_AileronTrim Usage_i8(0xb1) +#define Usage_SC_AntiTorqueControl Usage_i8(0xb2) +#define Usage_SC_AutopilotEnable Usage_i8(0xb3) +#define Usage_SC_ChaffRelease Usage_i8(0xb4) +#define Usage_SC_CollectiveControl Usage_i8(0xb5) +#define Usage_SC_DiveBrake Usage_i8(0xb6) +#define Usage_SC_ElectronicCountermeasures Usage_i8(0xb7) +#define Usage_SC_Elevator Usage_i8(0xb8) +#define Usage_SC_ElevatorTrim Usage_i8(0xb9) +#define Usage_SC_Rudder Usage_i8(0xba) +#define Usage_SC_Throttle Usage_i8(0xbb) +#define Usage_SC_FlightCommunications Usage_i8(0xbc) +#define Usage_SC_FlareRelease Usage_i8(0xbd) +#define Usage_SC_LandingGear Usage_i8(0xbe) +#define Usage_SC_ToeBrake Usage_i8(0xbf) +#define Usage_SC_Trigger Usage_i8(0xc0) +#define Usage_SC_WeaponsArm Usage_i8(0xc1) +#define Usage_SC_WeaponsSelect Usage_i8(0xc2) +#define Usage_SC_WingFlaps Usage_i8(0xc3) +#define Usage_SC_Accelerator Usage_i8(0xc4) +#define Usage_SC_Brake Usage_i8(0xc5) +#define Usage_SC_Clutch Usage_i8(0xc6) +#define Usage_SC_Shifter Usage_i8(0xc7) +#define Usage_SC_Steering Usage_i8(0xc8) +#define Usage_SC_TurretDirection Usage_i8(0xc9) +#define Usage_SC_BarrelElevation Usage_i8(0xca) +#define Usage_SC_DivePlane Usage_i8(0xcb) +#define Usage_SC_Ballast Usage_i8(0xcc) +#define Usage_SC_BicycleCrank Usage_i8(0xcd) +#define Usage_SC_HandleBars Usage_i8(0xce) +#define Usage_SC_FrontBrake Usage_i8(0xcf) +#define Usage_SC_RearBrake Usage_i8(0xd0) +#define Usage_VRC_Belt Usage_i8(0x1) +#define Usage_VRC_BodySuit Usage_i8(0x2) +#define Usage_VRC_Flexor Usage_i8(0x3) +#define Usage_VRC_Glove Usage_i8(0x4) +#define Usage_VRC_HeadTracker Usage_i8(0x5) +#define Usage_VRC_HeadMountedDisplay Usage_i8(0x6) +#define Usage_VRC_HandTracker Usage_i8(0x7) +#define Usage_VRC_Oculometer Usage_i8(0x8) +#define Usage_VRC_Vest Usage_i8(0x9) +#define Usage_VRC_AnimatronicDevice Usage_i8(0xa) +#define Usage_VRC_StereoEnable Usage_i8(0x20) +#define Usage_VRC_DisplayEnable Usage_i8(0x21) +#define Usage_SC_BaseballBat Usage_i8(0x1) +#define Usage_SC_GolfClub Usage_i8(0x2) +#define Usage_SC_RowingMachine Usage_i8(0x3) +#define Usage_SC_Treadmill Usage_i8(0x4) +#define Usage_SC_Oar Usage_i8(0x30) +#define Usage_SC_Slope Usage_i8(0x31) +#define Usage_SC_Rate Usage_i8(0x32) +#define Usage_SC_StickSpeed Usage_i8(0x33) +#define Usage_SC_StickFaceAngle Usage_i8(0x34) +#define Usage_SC_StickHeelToe Usage_i8(0x35) +#define Usage_SC_StickFollowThrough Usage_i8(0x36) +#define Usage_SC_StickTempo Usage_i8(0x37) +#define Usage_SC_StickType Usage_i8(0x38) +#define Usage_SC_StickHeight Usage_i8(0x39) +#define Usage_SC_Putter Usage_i8(0x50) +#define Usage_SC_OneIron Usage_i8(0x51) +#define Usage_SC_TwoIron Usage_i8(0x52) +#define Usage_SC_ThreeIron Usage_i8(0x53) +#define Usage_SC_FourIron Usage_i8(0x54) +#define Usage_SC_FiveIron Usage_i8(0x55) +#define Usage_SC_SixIron Usage_i8(0x56) +#define Usage_SC_SevenIron Usage_i8(0x57) +#define Usage_SC_EightIron Usage_i8(0x58) +#define Usage_SC_NineIron Usage_i8(0x59) +#define Usage_SC_One0Iron Usage_i8(0x5a) +#define Usage_SC_One1Iron Usage_i8(0x5b) +#define Usage_SC_SandWedge Usage_i8(0x5c) +#define Usage_SC_LoftWedge Usage_i8(0x5d) +#define Usage_SC_PowerWedge Usage_i8(0x5e) +#define Usage_SC_OneWood Usage_i8(0x5f) +#define Usage_SC_ThreeWood Usage_i8(0x60) +#define Usage_SC_FiveWood Usage_i8(0x61) +#define Usage_SC_SevenWood Usage_i8(0x62) +#define Usage_SC_NineWood Usage_i8(0x63) +#define Usage_GC_ThreeDGameController Usage_i8(0x1) +#define Usage_GC_PinballDevice Usage_i8(0x2) +#define Usage_GC_GunDevice Usage_i8(0x3) +#define Usage_GC_PointofView Usage_i8(0x20) +#define Usage_GC_TurnRightLeft Usage_i8(0x21) +#define Usage_GC_PitchForwardBackward Usage_i8(0x22) +#define Usage_GC_RollRightLeft Usage_i8(0x23) +#define Usage_GC_MoveRightLeft Usage_i8(0x24) +#define Usage_GC_MoveForwardBackward Usage_i8(0x25) +#define Usage_GC_MoveUpDown Usage_i8(0x26) +#define Usage_GC_LeanRightLeft Usage_i8(0x27) +#define Usage_GC_LeanForwardBackward Usage_i8(0x28) +#define Usage_GC_HeightofPOV Usage_i8(0x29) +#define Usage_GC_Flipper Usage_i8(0x2a) +#define Usage_GC_SecondaryFlipper Usage_i8(0x2b) +#define Usage_GC_Bump Usage_i8(0x2c) +#define Usage_GC_NewGame Usage_i8(0x2d) +#define Usage_GC_ShootBall Usage_i8(0x2e) +#define Usage_GC_Player Usage_i8(0x2f) +#define Usage_GC_GunBolt Usage_i8(0x30) +#define Usage_GC_GunClip Usage_i8(0x31) +#define Usage_GC_GunSelector Usage_i8(0x32) +#define Usage_GC_GunSingleShot Usage_i8(0x33) +#define Usage_GC_GunBurst Usage_i8(0x34) +#define Usage_GC_GunAutomatic Usage_i8(0x35) +#define Usage_GC_GunSafety Usage_i8(0x36) +#define Usage_GC_GamepadFireJump Usage_i8(0x37) +#define Usage_GC_GamepadTrigger Usage_i8(0x39) +#define Usage_GC_FormfittingGamepad Usage_i8(0x3a) +#define Usage_GDC_BackgroundNonuserControls Usage_i8(0x1) +#define Usage_GDC_BatteryStrength Usage_i8(0x20) +#define Usage_GDC_WirelessChannel Usage_i8(0x21) +#define Usage_GDC_WirelessID Usage_i8(0x22) +#define Usage_GDC_DiscoverWirelessControl Usage_i8(0x23) +#define Usage_GDC_SecurityCodeCharacterEntered Usage_i8(0x24) +#define Usage_GDC_SecurityCodeCharacterErased Usage_i8(0x25) +#define Usage_GDC_SecurityCodeCleared Usage_i8(0x26) +#define Usage_GDC_SequenceID Usage_i8(0x27) +#define Usage_GDC_SequenceIDReset Usage_i8(0x28) +#define Usage_GDC_RFSignalStrength Usage_i8(0x29) +#define Usage_GDC_SoftwareVersion Usage_i8(0x2a) +#define Usage_GDC_ProtocolVersion Usage_i8(0x2b) +#define Usage_GDC_HardwareVersion Usage_i8(0x2c) +#define Usage_GDC_Major Usage_i8(0x2d) +#define Usage_GDC_Minor Usage_i8(0x2e) +#define Usage_GDC_Revision Usage_i8(0x2f) +#define Usage_GDC_Handedness Usage_i8(0x30) +#define Usage_GDC_EitherHand Usage_i8(0x31) +#define Usage_GDC_LeftHand Usage_i8(0x32) +#define Usage_GDC_RightHand Usage_i8(0x33) +#define Usage_GDC_BothHands Usage_i8(0x34) +#define Usage_GDC_GripPoseOffset Usage_i8(0x40) +#define Usage_GDC_PointerPoseOffset Usage_i8(0x41) +#define Usage_KK_ErrorRollOver Usage_i8(0x1) +#define Usage_KK_POSTFail Usage_i8(0x2) +#define Usage_KK_ErrorUndefined Usage_i8(0x3) +#define Usage_KK_KeyboardA Usage_i8(0x4) +#define Usage_KK_KeyboardB Usage_i8(0x5) +#define Usage_KK_KeyboardC Usage_i8(0x6) +#define Usage_KK_KeyboardD Usage_i8(0x7) +#define Usage_KK_KeyboardE Usage_i8(0x8) +#define Usage_KK_KeyboardF Usage_i8(0x9) +#define Usage_KK_KeyboardG Usage_i8(0xa) +#define Usage_KK_KeyboardH Usage_i8(0xb) +#define Usage_KK_KeyboardI Usage_i8(0xc) +#define Usage_KK_KeyboardJ Usage_i8(0xd) +#define Usage_KK_KeyboardK Usage_i8(0xe) +#define Usage_KK_KeyboardL Usage_i8(0xf) +#define Usage_KK_KeyboardM Usage_i8(0x10) +#define Usage_KK_KeyboardN Usage_i8(0x11) +#define Usage_KK_KeyboardO Usage_i8(0x12) +#define Usage_KK_KeyboardP Usage_i8(0x13) +#define Usage_KK_KeyboardQ Usage_i8(0x14) +#define Usage_KK_KeyboardR Usage_i8(0x15) +#define Usage_KK_KeyboardS Usage_i8(0x16) +#define Usage_KK_KeyboardT Usage_i8(0x17) +#define Usage_KK_KeyboardU Usage_i8(0x18) +#define Usage_KK_KeyboardV Usage_i8(0x19) +#define Usage_KK_KeyboardW Usage_i8(0x1a) +#define Usage_KK_KeyboardX Usage_i8(0x1b) +#define Usage_KK_KeyboardY Usage_i8(0x1c) +#define Usage_KK_KeyboardZ Usage_i8(0x1d) +#define Usage_KK_Keyboard1andBang Usage_i8(0x1e) +#define Usage_KK_Keyboard2andAt Usage_i8(0x1f) +#define Usage_KK_Keyboard3andHash Usage_i8(0x20) +#define Usage_KK_Keyboard4andDollar Usage_i8(0x21) +#define Usage_KK_Keyboard5andPercent Usage_i8(0x22) +#define Usage_KK_Keyboard6andCaret Usage_i8(0x23) +#define Usage_KK_Keyboard7andAmpersand Usage_i8(0x24) +#define Usage_KK_Keyboard8andStar Usage_i8(0x25) +#define Usage_KK_Keyboard9andLeftBracket Usage_i8(0x26) +#define Usage_KK_Keyboard0andRightBracket Usage_i8(0x27) +#define Usage_KK_KeyboardReturnEnter Usage_i8(0x28) +#define Usage_KK_KeyboardEscape Usage_i8(0x29) +#define Usage_KK_KeyboardDelete Usage_i8(0x2a) +#define Usage_KK_KeyboardTab Usage_i8(0x2b) +#define Usage_KK_KeyboardSpacebar Usage_i8(0x2c) +#define Usage_KK_KeyboardDashandUnderscore Usage_i8(0x2d) +#define Usage_KK_KeyboardEqualsandPlus Usage_i8(0x2e) +#define Usage_KK_KeyboardLeftBrace Usage_i8(0x2f) +#define Usage_KK_KeyboardRightBrace Usage_i8(0x30) +#define Usage_KK_KeyboardBackslashandPipe Usage_i8(0x31) +#define Usage_KK_KeyboardNonUSHashandTilde Usage_i8(0x32) +#define Usage_KK_KeyboardSemiColonandColon Usage_i8(0x33) +#define Usage_KK_KeyboardLeftAposandDouble Usage_i8(0x34) +#define Usage_KK_KeyboardGraveAccentandTilde Usage_i8(0x35) +#define Usage_KK_KeyboardCommaandLessThan Usage_i8(0x36) +#define Usage_KK_KeyboardPeriodandGreaterThan Usage_i8(0x37) +#define Usage_KK_KeyboardForwardSlashandQuestionMark Usage_i8(0x38) +#define Usage_KK_KeyboardCapsLock Usage_i8(0x39) +#define Usage_KK_KeyboardF1 Usage_i8(0x3a) +#define Usage_KK_KeyboardF2 Usage_i8(0x3b) +#define Usage_KK_KeyboardF3 Usage_i8(0x3c) +#define Usage_KK_KeyboardF4 Usage_i8(0x3d) +#define Usage_KK_KeyboardF5 Usage_i8(0x3e) +#define Usage_KK_KeyboardF6 Usage_i8(0x3f) +#define Usage_KK_KeyboardF7 Usage_i8(0x40) +#define Usage_KK_KeyboardF8 Usage_i8(0x41) +#define Usage_KK_KeyboardF9 Usage_i8(0x42) +#define Usage_KK_KeyboardF10 Usage_i8(0x43) +#define Usage_KK_KeyboardF11 Usage_i8(0x44) +#define Usage_KK_KeyboardF12 Usage_i8(0x45) +#define Usage_KK_KeyboardPrintScreen Usage_i8(0x46) +#define Usage_KK_KeyboardScrollLock Usage_i8(0x47) +#define Usage_KK_KeyboardPause Usage_i8(0x48) +#define Usage_KK_KeyboardInsert Usage_i8(0x49) +#define Usage_KK_KeyboardHome Usage_i8(0x4a) +#define Usage_KK_KeyboardPageUp Usage_i8(0x4b) +#define Usage_KK_KeyboardDeleteForward Usage_i8(0x4c) +#define Usage_KK_KeyboardEnd Usage_i8(0x4d) +#define Usage_KK_KeyboardPageDown Usage_i8(0x4e) +#define Usage_KK_KeyboardRightArrow Usage_i8(0x4f) +#define Usage_KK_KeyboardLeftArrow Usage_i8(0x50) +#define Usage_KK_KeyboardDownArrow Usage_i8(0x51) +#define Usage_KK_KeyboardUpArrow Usage_i8(0x52) +#define Usage_KK_KeypadNumLockandClear Usage_i8(0x53) +#define Usage_KK_KeypadForwardSlash Usage_i8(0x54) +#define Usage_KK_KeypadStar Usage_i8(0x55) +#define Usage_KK_KeypadDash Usage_i8(0x56) +#define Usage_KK_KeypadPlus Usage_i8(0x57) +#define Usage_KK_KeypadENTER Usage_i8(0x58) +#define Usage_KK_Keypad1andEnd Usage_i8(0x59) +#define Usage_KK_Keypad2andDownArrow Usage_i8(0x5a) +#define Usage_KK_Keypad3andPageDn Usage_i8(0x5b) +#define Usage_KK_Keypad4andLeftArrow Usage_i8(0x5c) +#define Usage_KK_Keypad5 Usage_i8(0x5d) +#define Usage_KK_Keypad6andRightArrow Usage_i8(0x5e) +#define Usage_KK_Keypad7andHome Usage_i8(0x5f) +#define Usage_KK_Keypad8andUpArrow Usage_i8(0x60) +#define Usage_KK_Keypad9andPageUp Usage_i8(0x61) +#define Usage_KK_Keypad0andInsert Usage_i8(0x62) +#define Usage_KK_KeypadPeriodandDelete Usage_i8(0x63) +#define Usage_KK_KeyboardNonUSBackslashandPipe Usage_i8(0x64) +#define Usage_KK_KeyboardApplication Usage_i8(0x65) +#define Usage_KK_KeyboardPower Usage_i8(0x66) +#define Usage_KK_KeypadEquals Usage_i8(0x67) +#define Usage_KK_KeyboardF13 Usage_i8(0x68) +#define Usage_KK_KeyboardF14 Usage_i8(0x69) +#define Usage_KK_KeyboardF15 Usage_i8(0x6a) +#define Usage_KK_KeyboardF16 Usage_i8(0x6b) +#define Usage_KK_KeyboardF17 Usage_i8(0x6c) +#define Usage_KK_KeyboardF18 Usage_i8(0x6d) +#define Usage_KK_KeyboardF19 Usage_i8(0x6e) +#define Usage_KK_KeyboardF20 Usage_i8(0x6f) +#define Usage_KK_KeyboardF21 Usage_i8(0x70) +#define Usage_KK_KeyboardF22 Usage_i8(0x71) +#define Usage_KK_KeyboardF23 Usage_i8(0x72) +#define Usage_KK_KeyboardF24 Usage_i8(0x73) +#define Usage_KK_KeyboardExecute Usage_i8(0x74) +#define Usage_KK_KeyboardHelp Usage_i8(0x75) +#define Usage_KK_KeyboardMenu Usage_i8(0x76) +#define Usage_KK_KeyboardSelect Usage_i8(0x77) +#define Usage_KK_KeyboardStop Usage_i8(0x78) +#define Usage_KK_KeyboardAgain Usage_i8(0x79) +#define Usage_KK_KeyboardUndo Usage_i8(0x7a) +#define Usage_KK_KeyboardCut Usage_i8(0x7b) +#define Usage_KK_KeyboardCopy Usage_i8(0x7c) +#define Usage_KK_KeyboardPaste Usage_i8(0x7d) +#define Usage_KK_KeyboardFind Usage_i8(0x7e) +#define Usage_KK_KeyboardMute Usage_i8(0x7f) +#define Usage_KK_KeyboardVolumeUp Usage_i8(0x80) +#define Usage_KK_KeyboardVolumeDown Usage_i8(0x81) +#define Usage_KK_KeyboardLockingCapsLock Usage_i8(0x82) +#define Usage_KK_KeyboardLockingNumLock Usage_i8(0x83) +#define Usage_KK_KeyboardLockingScrollLock Usage_i8(0x84) +#define Usage_KK_KeypadComma Usage_i8(0x85) +#define Usage_KK_KeypadEqualSign Usage_i8(0x86) +#define Usage_KK_KeyboardInternational1 Usage_i8(0x87) +#define Usage_KK_KeyboardInternational2 Usage_i8(0x88) +#define Usage_KK_KeyboardInternational3 Usage_i8(0x89) +#define Usage_KK_KeyboardInternational4 Usage_i8(0x8a) +#define Usage_KK_KeyboardInternational5 Usage_i8(0x8b) +#define Usage_KK_KeyboardInternational6 Usage_i8(0x8c) +#define Usage_KK_KeyboardInternational7 Usage_i8(0x8d) +#define Usage_KK_KeyboardInternational8 Usage_i8(0x8e) +#define Usage_KK_KeyboardInternational9 Usage_i8(0x8f) +#define Usage_KK_KeyboardLANG1 Usage_i8(0x90) +#define Usage_KK_KeyboardLANG2 Usage_i8(0x91) +#define Usage_KK_KeyboardLANG3 Usage_i8(0x92) +#define Usage_KK_KeyboardLANG4 Usage_i8(0x93) +#define Usage_KK_KeyboardLANG5 Usage_i8(0x94) +#define Usage_KK_KeyboardLANG6 Usage_i8(0x95) +#define Usage_KK_KeyboardLANG7 Usage_i8(0x96) +#define Usage_KK_KeyboardLANG8 Usage_i8(0x97) +#define Usage_KK_KeyboardLANG9 Usage_i8(0x98) +#define Usage_KK_KeyboardAlternateErase Usage_i8(0x99) +#define Usage_KK_KeyboardSysReqAttention Usage_i8(0x9a) +#define Usage_KK_KeyboardCancel Usage_i8(0x9b) +#define Usage_KK_KeyboardClear Usage_i8(0x9c) +#define Usage_KK_KeyboardPrior Usage_i8(0x9d) +#define Usage_KK_KeyboardReturn Usage_i8(0x9e) +#define Usage_KK_KeyboardSeparator Usage_i8(0x9f) +#define Usage_KK_KeyboardOut Usage_i8(0xa0) +#define Usage_KK_KeyboardOper Usage_i8(0xa1) +#define Usage_KK_KeyboardClearAgain Usage_i8(0xa2) +#define Usage_KK_KeyboardCrSelProps Usage_i8(0xa3) +#define Usage_KK_KeyboardExSel Usage_i8(0xa4) +#define Usage_KK_KeypadDouble0 Usage_i8(0xb0) +#define Usage_KK_KeypadTriple0 Usage_i8(0xb1) +#define Usage_KK_ThousandsSeparator Usage_i8(0xb2) +#define Usage_KK_DecimalSeparator Usage_i8(0xb3) +#define Usage_KK_CurrencyUnit Usage_i8(0xb4) +#define Usage_KK_CurrencySubunit Usage_i8(0xb5) +#define Usage_KK_KeypadLeftBracket Usage_i8(0xb6) +#define Usage_KK_KeypadRightBracket Usage_i8(0xb7) +#define Usage_KK_KeypadLeftBrace Usage_i8(0xb8) +#define Usage_KK_KeypadRightBrace Usage_i8(0xb9) +#define Usage_KK_KeypadTab Usage_i8(0xba) +#define Usage_KK_KeypadBackspace Usage_i8(0xbb) +#define Usage_KK_KeypadA Usage_i8(0xbc) +#define Usage_KK_KeypadB Usage_i8(0xbd) +#define Usage_KK_KeypadC Usage_i8(0xbe) +#define Usage_KK_KeypadD Usage_i8(0xbf) +#define Usage_KK_KeypadE Usage_i8(0xc0) +#define Usage_KK_KeypadF Usage_i8(0xc1) +#define Usage_KK_KeypadXOR Usage_i8(0xc2) +#define Usage_KK_KeypadCaret Usage_i8(0xc3) +#define Usage_KK_KeypadPercentage Usage_i8(0xc4) +#define Usage_KK_KeypadLess Usage_i8(0xc5) +#define Usage_KK_KeypadGreater Usage_i8(0xc6) +#define Usage_KK_KeypadAmpersand Usage_i8(0xc7) +#define Usage_KK_KeypadDoubleAmpersand Usage_i8(0xc8) +#define Usage_KK_KeypadBar Usage_i8(0xc9) +#define Usage_KK_KeypadDoubleBar Usage_i8(0xca) +#define Usage_KK_KeypadColon Usage_i8(0xcb) +#define Usage_KK_KeypadHash Usage_i8(0xcc) +#define Usage_KK_KeypadSpace Usage_i8(0xcd) +#define Usage_KK_KeypadAt Usage_i8(0xce) +#define Usage_KK_KeypadBang Usage_i8(0xcf) +#define Usage_KK_KeypadMemoryStore Usage_i8(0xd0) +#define Usage_KK_KeypadMemoryRecall Usage_i8(0xd1) +#define Usage_KK_KeypadMemoryClear Usage_i8(0xd2) +#define Usage_KK_KeypadMemoryAdd Usage_i8(0xd3) +#define Usage_KK_KeypadMemorySubtract Usage_i8(0xd4) +#define Usage_KK_KeypadMemoryMultiply Usage_i8(0xd5) +#define Usage_KK_KeypadMemoryDivide Usage_i8(0xd6) +#define Usage_KK_KeypadPlusMinus Usage_i8(0xd7) +#define Usage_KK_KeypadClear Usage_i8(0xd8) +#define Usage_KK_KeypadClearEntry Usage_i8(0xd9) +#define Usage_KK_KeypadBinary Usage_i8(0xda) +#define Usage_KK_KeypadOctal Usage_i8(0xdb) +#define Usage_KK_KeypadDecimal Usage_i8(0xdc) +#define Usage_KK_KeypadHexadecimal Usage_i8(0xdd) +#define Usage_KK_KeyboardLeftControl Usage_i8(0xe0) +#define Usage_KK_KeyboardLeftShift Usage_i8(0xe1) +#define Usage_KK_KeyboardLeftAlt Usage_i8(0xe2) +#define Usage_KK_KeyboardLeftGUI Usage_i8(0xe3) +#define Usage_KK_KeyboardRightControl Usage_i8(0xe4) +#define Usage_KK_KeyboardRightShift Usage_i8(0xe5) +#define Usage_KK_KeyboardRightAlt Usage_i8(0xe6) +#define Usage_KK_KeyboardRightGUI Usage_i8(0xe7) +#define Usage_LED_NumLock Usage_i8(0x1) +#define Usage_LED_CapsLock Usage_i8(0x2) +#define Usage_LED_ScrollLock Usage_i8(0x3) +#define Usage_LED_Compose Usage_i8(0x4) +#define Usage_LED_Kana Usage_i8(0x5) +#define Usage_LED_Power Usage_i8(0x6) +#define Usage_LED_Shift Usage_i8(0x7) +#define Usage_LED_DoNotDisturb Usage_i8(0x8) +#define Usage_LED_Mute Usage_i8(0x9) +#define Usage_LED_ToneEnable Usage_i8(0xa) +#define Usage_LED_HighCutFilter Usage_i8(0xb) +#define Usage_LED_LowCutFilter Usage_i8(0xc) +#define Usage_LED_EqualizerEnable Usage_i8(0xd) +#define Usage_LED_SoundFieldOn Usage_i8(0xe) +#define Usage_LED_SurroundOn Usage_i8(0xf) +#define Usage_LED_Repeat Usage_i8(0x10) +#define Usage_LED_Stereo Usage_i8(0x11) +#define Usage_LED_SamplingRateDetect Usage_i8(0x12) +#define Usage_LED_Spinning Usage_i8(0x13) +#define Usage_LED_CAV Usage_i8(0x14) +#define Usage_LED_CLV Usage_i8(0x15) +#define Usage_LED_RecordingFormatDetect Usage_i8(0x16) +#define Usage_LED_OffHook Usage_i8(0x17) +#define Usage_LED_Ring Usage_i8(0x18) +#define Usage_LED_MessageWaiting Usage_i8(0x19) +#define Usage_LED_DataMode Usage_i8(0x1a) +#define Usage_LED_BatteryOperation Usage_i8(0x1b) +#define Usage_LED_BatteryOK Usage_i8(0x1c) +#define Usage_LED_BatteryLow Usage_i8(0x1d) +#define Usage_LED_Speaker Usage_i8(0x1e) +#define Usage_LED_Headset Usage_i8(0x1f) +#define Usage_LED_Hold Usage_i8(0x20) +#define Usage_LED_Microphone Usage_i8(0x21) +#define Usage_LED_Coverage Usage_i8(0x22) +#define Usage_LED_NightMode Usage_i8(0x23) +#define Usage_LED_SendCalls Usage_i8(0x24) +#define Usage_LED_CallPickup Usage_i8(0x25) +#define Usage_LED_Conference Usage_i8(0x26) +#define Usage_LED_Standby Usage_i8(0x27) +#define Usage_LED_CameraOn Usage_i8(0x28) +#define Usage_LED_CameraOff Usage_i8(0x29) +#define Usage_LED_OnLine Usage_i8(0x2a) +#define Usage_LED_OffLine Usage_i8(0x2b) +#define Usage_LED_Busy Usage_i8(0x2c) +#define Usage_LED_Ready Usage_i8(0x2d) +#define Usage_LED_PaperOut Usage_i8(0x2e) +#define Usage_LED_PaperJam Usage_i8(0x2f) +#define Usage_LED_Remote Usage_i8(0x30) +#define Usage_LED_Forward Usage_i8(0x31) +#define Usage_LED_Reverse Usage_i8(0x32) +#define Usage_LED_Stop Usage_i8(0x33) +#define Usage_LED_Rewind Usage_i8(0x34) +#define Usage_LED_FastForward Usage_i8(0x35) +#define Usage_LED_Play Usage_i8(0x36) +#define Usage_LED_Pause Usage_i8(0x37) +#define Usage_LED_Record Usage_i8(0x38) +#define Usage_LED_Error Usage_i8(0x39) +#define Usage_LED_UsageSelectedIndicator Usage_i8(0x3a) +#define Usage_LED_UsageInUseIndicator Usage_i8(0x3b) +#define Usage_LED_UsageMultiModeIndicator Usage_i8(0x3c) +#define Usage_LED_IndicatorOn Usage_i8(0x3d) +#define Usage_LED_IndicatorFlash Usage_i8(0x3e) +#define Usage_LED_IndicatorSlowBlink Usage_i8(0x3f) +#define Usage_LED_IndicatorFastBlink Usage_i8(0x40) +#define Usage_LED_IndicatorOff Usage_i8(0x41) +#define Usage_LED_FlashOnTime Usage_i8(0x42) +#define Usage_LED_SlowBlinkOnTime Usage_i8(0x43) +#define Usage_LED_SlowBlinkOffTime Usage_i8(0x44) +#define Usage_LED_FastBlinkOnTime Usage_i8(0x45) +#define Usage_LED_FastBlinkOffTime Usage_i8(0x46) +#define Usage_LED_UsageIndicatorColor Usage_i8(0x47) +#define Usage_LED_IndicatorRed Usage_i8(0x48) +#define Usage_LED_IndicatorGreen Usage_i8(0x49) +#define Usage_LED_IndicatorAmber Usage_i8(0x4a) +#define Usage_LED_GenericIndicator Usage_i8(0x4b) +#define Usage_LED_SystemSuspend Usage_i8(0x4c) +#define Usage_LED_ExternalPowerConnected Usage_i8(0x4d) +#define Usage_LED_IndicatorBlue Usage_i8(0x4e) +#define Usage_LED_IndicatorOrange Usage_i8(0x4f) +#define Usage_LED_GoodStatus Usage_i8(0x50) +#define Usage_LED_WarningStatus Usage_i8(0x51) +#define Usage_LED_RGBLED Usage_i8(0x52) +#define Usage_LED_RedLEDChannel Usage_i8(0x53) +#define Usage_LED_BlueLEDChannel Usage_i8(0x54) +#define Usage_LED_GreenLEDChannel Usage_i8(0x55) +#define Usage_LED_LEDIntensity Usage_i8(0x56) +#define Usage_LED_SystemMicrophoneMute Usage_i8(0x57) +#define Usage_LED_PlayerIndicator Usage_i8(0x60) +#define Usage_LED_Player1 Usage_i8(0x61) +#define Usage_LED_Player2 Usage_i8(0x62) +#define Usage_LED_Player3 Usage_i8(0x63) +#define Usage_LED_Player4 Usage_i8(0x64) +#define Usage_LED_Player5 Usage_i8(0x65) +#define Usage_LED_Player6 Usage_i8(0x66) +#define Usage_LED_Player7 Usage_i8(0x67) +#define Usage_LED_Player8 Usage_i8(0x68) +#define Usage_TD_Phone Usage_i8(0x1) +#define Usage_TD_AnsweringMachine Usage_i8(0x2) +#define Usage_TD_MessageControls Usage_i8(0x3) +#define Usage_TD_Handset Usage_i8(0x4) +#define Usage_TD_Headset Usage_i8(0x5) +#define Usage_TD_TelephonyKeyPad Usage_i8(0x6) +#define Usage_TD_ProgrammableButton Usage_i8(0x7) +#define Usage_TD_HookSwitch Usage_i8(0x20) +#define Usage_TD_Flash Usage_i8(0x21) +#define Usage_TD_Feature Usage_i8(0x22) +#define Usage_TD_Hold Usage_i8(0x23) +#define Usage_TD_Redial Usage_i8(0x24) +#define Usage_TD_Transfer Usage_i8(0x25) +#define Usage_TD_Drop Usage_i8(0x26) +#define Usage_TD_Park Usage_i8(0x27) +#define Usage_TD_ForwardCalls Usage_i8(0x28) +#define Usage_TD_AlternateFunction Usage_i8(0x29) +#define Usage_TD_Line Usage_i8(0x2a) +#define Usage_TD_SpeakerPhone Usage_i8(0x2b) +#define Usage_TD_Conference Usage_i8(0x2c) +#define Usage_TD_RingEnable Usage_i8(0x2d) +#define Usage_TD_RingSelect Usage_i8(0x2e) +#define Usage_TD_PhoneMute Usage_i8(0x2f) +#define Usage_TD_CallerID Usage_i8(0x30) +#define Usage_TD_Send Usage_i8(0x31) +#define Usage_TD_SpeedDial Usage_i8(0x50) +#define Usage_TD_StoreNumber Usage_i8(0x51) +#define Usage_TD_RecallNumber Usage_i8(0x52) +#define Usage_TD_PhoneDirectory Usage_i8(0x53) +#define Usage_TD_VoiceMail Usage_i8(0x70) +#define Usage_TD_ScreenCalls Usage_i8(0x71) +#define Usage_TD_DoNotDisturb Usage_i8(0x72) +#define Usage_TD_Message Usage_i8(0x73) +#define Usage_TD_AnswerOnOff Usage_i8(0x74) +#define Usage_TD_InsideDialTone Usage_i8(0x90) +#define Usage_TD_OutsideDialTone Usage_i8(0x91) +#define Usage_TD_InsideRingTone Usage_i8(0x92) +#define Usage_TD_OutsideRingTone Usage_i8(0x93) +#define Usage_TD_PriorityRingTone Usage_i8(0x94) +#define Usage_TD_InsideRingback Usage_i8(0x95) +#define Usage_TD_PriorityRingback Usage_i8(0x96) +#define Usage_TD_LineBusyTone Usage_i8(0x97) +#define Usage_TD_ReorderTone Usage_i8(0x98) +#define Usage_TD_CallWaitingTone Usage_i8(0x99) +#define Usage_TD_ConfirmationTone1 Usage_i8(0x9a) +#define Usage_TD_ConfirmationTone2 Usage_i8(0x9b) +#define Usage_TD_TonesOff Usage_i8(0x9c) +#define Usage_TD_OutsideRingback Usage_i8(0x9d) +#define Usage_TD_Ringer Usage_i8(0x9e) +#define Usage_TD_PhoneKey0 Usage_i8(0xb0) +#define Usage_TD_PhoneKey1 Usage_i8(0xb1) +#define Usage_TD_PhoneKey2 Usage_i8(0xb2) +#define Usage_TD_PhoneKey3 Usage_i8(0xb3) +#define Usage_TD_PhoneKey4 Usage_i8(0xb4) +#define Usage_TD_PhoneKey5 Usage_i8(0xb5) +#define Usage_TD_PhoneKey6 Usage_i8(0xb6) +#define Usage_TD_PhoneKey7 Usage_i8(0xb7) +#define Usage_TD_PhoneKey8 Usage_i8(0xb8) +#define Usage_TD_PhoneKey9 Usage_i8(0xb9) +#define Usage_TD_PhoneKeyStar Usage_i8(0xba) +#define Usage_TD_PhoneKeyPound Usage_i8(0xbb) +#define Usage_TD_PhoneKeyA Usage_i8(0xbc) +#define Usage_TD_PhoneKeyB Usage_i8(0xbd) +#define Usage_TD_PhoneKeyC Usage_i8(0xbe) +#define Usage_TD_PhoneKeyD Usage_i8(0xbf) +#define Usage_TD_PhoneCallHistoryKey Usage_i8(0xc0) +#define Usage_TD_PhoneCallerIDKey Usage_i8(0xc1) +#define Usage_TD_PhoneSettingsKey Usage_i8(0xc2) +#define Usage_TD_HostControl Usage_i8(0xf0) +#define Usage_TD_HostAvailable Usage_i8(0xf1) +#define Usage_TD_HostCallActive Usage_i8(0xf2) +#define Usage_TD_ActivateHandsetAudio Usage_i8(0xf3) +#define Usage_TD_RingType Usage_i8(0xf4) +#define Usage_TD_RedialablePhoneNumber Usage_i8(0xf5) +#define Usage_TD_StopRingTone Usage_i8(0xf8) +#define Usage_TD_PSTNRingTone Usage_i8(0xf9) +#define Usage_TD_HostRingTone Usage_i8(0xfa) +#define Usage_TD_AlertSoundError Usage_i8(0xfb) +#define Usage_TD_AlertSoundConfirm Usage_i8(0xfc) +#define Usage_TD_AlertSoundNotification Usage_i8(0xfd) +#define Usage_TD_SilentRing Usage_i8(0xfe) +#define Usage_TD_EmailMessageWaiting Usage_i16(0x108) +#define Usage_TD_VoicemailMessageWaiting Usage_i16(0x109) +#define Usage_TD_HostHold Usage_i16(0x10a) +#define Usage_TD_IncomingCallHistoryCount Usage_i16(0x110) +#define Usage_TD_OutgoingCallHistoryCount Usage_i16(0x111) +#define Usage_TD_IncomingCallHistory Usage_i16(0x112) +#define Usage_TD_OutgoingCallHistory Usage_i16(0x113) +#define Usage_TD_PhoneLocale Usage_i16(0x114) +#define Usage_TD_PhoneTimeSecond Usage_i16(0x140) +#define Usage_TD_PhoneTimeMinute Usage_i16(0x141) +#define Usage_TD_PhoneTimeHour Usage_i16(0x142) +#define Usage_TD_PhoneDateDay Usage_i16(0x143) +#define Usage_TD_PhoneDateMonth Usage_i16(0x144) +#define Usage_TD_PhoneDateYear Usage_i16(0x145) +#define Usage_TD_HandsetNickname Usage_i16(0x146) +#define Usage_TD_AddressBookID Usage_i16(0x147) +#define Usage_TD_CallDuration Usage_i16(0x14a) +#define Usage_TD_DualModePhone Usage_i16(0x14b) +#define Usage_Con_ConsumerControl Usage_i8(0x1) +#define Usage_Con_NumericKeyPad Usage_i8(0x2) +#define Usage_Con_ProgrammableButtons Usage_i8(0x3) +#define Usage_Con_Microphone Usage_i8(0x4) +#define Usage_Con_Headphone Usage_i8(0x5) +#define Usage_Con_GraphicEqualizer Usage_i8(0x6) +#define Usage_Con_Plus10 Usage_i8(0x20) +#define Usage_Con_Plus100 Usage_i8(0x21) +#define Usage_Con_AMPM Usage_i8(0x22) +#define Usage_Con_Power Usage_i8(0x30) +#define Usage_Con_Reset Usage_i8(0x31) +#define Usage_Con_Sleep Usage_i8(0x32) +#define Usage_Con_SleepAfter Usage_i8(0x33) +#define Usage_Con_SleepMode Usage_i8(0x34) +#define Usage_Con_Illumination Usage_i8(0x35) +#define Usage_Con_FunctionButtons Usage_i8(0x36) +#define Usage_Con_Menu Usage_i8(0x40) +#define Usage_Con_MenuPick Usage_i8(0x41) +#define Usage_Con_MenuUp Usage_i8(0x42) +#define Usage_Con_MenuDown Usage_i8(0x43) +#define Usage_Con_MenuLeft Usage_i8(0x44) +#define Usage_Con_MenuRight Usage_i8(0x45) +#define Usage_Con_MenuEscape Usage_i8(0x46) +#define Usage_Con_MenuValueIncrease Usage_i8(0x47) +#define Usage_Con_MenuValueDecrease Usage_i8(0x48) +#define Usage_Con_DataOnScreen Usage_i8(0x60) +#define Usage_Con_ClosedCaption Usage_i8(0x61) +#define Usage_Con_ClosedCaptionSelect Usage_i8(0x62) +#define Usage_Con_VCRTV Usage_i8(0x63) +#define Usage_Con_BroadcastMode Usage_i8(0x64) +#define Usage_Con_Snapshot Usage_i8(0x65) +#define Usage_Con_Still Usage_i8(0x66) +#define Usage_Con_PictureinPictureToggle Usage_i8(0x67) +#define Usage_Con_PictureinPictureSwap Usage_i8(0x68) +#define Usage_Con_RedMenuButton Usage_i8(0x69) +#define Usage_Con_GreenMenuButton Usage_i8(0x6a) +#define Usage_Con_BlueMenuButton Usage_i8(0x6b) +#define Usage_Con_YellowMenuButton Usage_i8(0x6c) +#define Usage_Con_Aspect Usage_i8(0x6d) +#define Usage_Con_ThreeDModeSelect Usage_i8(0x6e) +#define Usage_Con_DisplayBrightnessIncrement Usage_i8(0x6f) +#define Usage_Con_DisplayBrightnessDecrement Usage_i8(0x70) +#define Usage_Con_DisplayBrightness Usage_i8(0x71) +#define Usage_Con_DisplayBacklightToggle Usage_i8(0x72) +#define Usage_Con_DisplaySetBrightnesstoMinimum Usage_i8(0x73) +#define Usage_Con_DisplaySetBrightnesstoMaximum Usage_i8(0x74) +#define Usage_Con_DisplaySetAutoBrightness Usage_i8(0x75) +#define Usage_Con_CameraAccessEnabled Usage_i8(0x76) +#define Usage_Con_CameraAccessDisabled Usage_i8(0x77) +#define Usage_Con_CameraAccessToggle Usage_i8(0x78) +#define Usage_Con_KeyboardBrightnessIncrement Usage_i8(0x79) +#define Usage_Con_KeyboardBrightnessDecrement Usage_i8(0x7a) +#define Usage_Con_KeyboardBacklightSetLevel Usage_i8(0x7b) +#define Usage_Con_KeyboardBacklightOOC Usage_i8(0x7c) +#define Usage_Con_KeyboardBacklightSetMinimum Usage_i8(0x7d) +#define Usage_Con_KeyboardBacklightSetMaximum Usage_i8(0x7e) +#define Usage_Con_KeyboardBacklightAuto Usage_i8(0x7f) +#define Usage_Con_Selection Usage_i8(0x80) +#define Usage_Con_AssignSelection Usage_i8(0x81) +#define Usage_Con_ModeStep Usage_i8(0x82) +#define Usage_Con_RecallLast Usage_i8(0x83) +#define Usage_Con_EnterChannel Usage_i8(0x84) +#define Usage_Con_OrderMovie Usage_i8(0x85) +#define Usage_Con_Channel Usage_i8(0x86) +#define Usage_Con_MediaSelection Usage_i8(0x87) +#define Usage_Con_MediaSelectComputer Usage_i8(0x88) +#define Usage_Con_MediaSelectTV Usage_i8(0x89) +#define Usage_Con_MediaSelectWWW Usage_i8(0x8a) +#define Usage_Con_MediaSelectDVD Usage_i8(0x8b) +#define Usage_Con_MediaSelectTelephone Usage_i8(0x8c) +#define Usage_Con_MediaSelectProgramGuide Usage_i8(0x8d) +#define Usage_Con_MediaSelectVideoPhone Usage_i8(0x8e) +#define Usage_Con_MediaSelectGames Usage_i8(0x8f) +#define Usage_Con_MediaSelectMessages Usage_i8(0x90) +#define Usage_Con_MediaSelectCD Usage_i8(0x91) +#define Usage_Con_MediaSelectVCR Usage_i8(0x92) +#define Usage_Con_MediaSelectTuner Usage_i8(0x93) +#define Usage_Con_Quit Usage_i8(0x94) +#define Usage_Con_Help Usage_i8(0x95) +#define Usage_Con_MediaSelectTape Usage_i8(0x96) +#define Usage_Con_MediaSelectCable Usage_i8(0x97) +#define Usage_Con_MediaSelectSatellite Usage_i8(0x98) +#define Usage_Con_MediaSelectSecurity Usage_i8(0x99) +#define Usage_Con_MediaSelectHome Usage_i8(0x9a) +#define Usage_Con_MediaSelectCall Usage_i8(0x9b) +#define Usage_Con_ChannelIncrement Usage_i8(0x9c) +#define Usage_Con_ChannelDecrement Usage_i8(0x9d) +#define Usage_Con_MediaSelectSAP Usage_i8(0x9e) +#define Usage_Con_VCRPlus Usage_i8(0xa0) +#define Usage_Con_Once Usage_i8(0xa1) +#define Usage_Con_Daily Usage_i8(0xa2) +#define Usage_Con_Weekly Usage_i8(0xa3) +#define Usage_Con_Monthly Usage_i8(0xa4) +#define Usage_Con_Play Usage_i8(0xb0) +#define Usage_Con_Pause Usage_i8(0xb1) +#define Usage_Con_Record Usage_i8(0xb2) +#define Usage_Con_FastForward Usage_i8(0xb3) +#define Usage_Con_Rewind Usage_i8(0xb4) +#define Usage_Con_ScanNextTrack Usage_i8(0xb5) +#define Usage_Con_ScanPreviousTrack Usage_i8(0xb6) +#define Usage_Con_Stop Usage_i8(0xb7) +#define Usage_Con_Eject Usage_i8(0xb8) +#define Usage_Con_RandomPlay Usage_i8(0xb9) +#define Usage_Con_SelectDisc Usage_i8(0xba) +#define Usage_Con_EnterDisc Usage_i8(0xbb) +#define Usage_Con_Repeat Usage_i8(0xbc) +#define Usage_Con_Tracking Usage_i8(0xbd) +#define Usage_Con_TrackNormal Usage_i8(0xbe) +#define Usage_Con_SlowTracking Usage_i8(0xbf) +#define Usage_Con_FrameForward Usage_i8(0xc0) +#define Usage_Con_FrameBack Usage_i8(0xc1) +#define Usage_Con_Mark Usage_i8(0xc2) +#define Usage_Con_ClearMark Usage_i8(0xc3) +#define Usage_Con_RepeatFromMark Usage_i8(0xc4) +#define Usage_Con_ReturnToMark Usage_i8(0xc5) +#define Usage_Con_SearchMarkForward Usage_i8(0xc6) +#define Usage_Con_SearchMarkBackwards Usage_i8(0xc7) +#define Usage_Con_CounterReset Usage_i8(0xc8) +#define Usage_Con_ShowCounter Usage_i8(0xc9) +#define Usage_Con_TrackingIncrement Usage_i8(0xca) +#define Usage_Con_TrackingDecrement Usage_i8(0xcb) +#define Usage_Con_StopEject Usage_i8(0xcc) +#define Usage_Con_PlayPause Usage_i8(0xcd) +#define Usage_Con_PlaySkip Usage_i8(0xce) +#define Usage_Con_VoiceCommand Usage_i8(0xcf) +#define Usage_Con_InvokeCaptureInterface Usage_i8(0xd0) +#define Usage_Con_StartorStopGameRecording Usage_i8(0xd1) +#define Usage_Con_HistoricalGameCapture Usage_i8(0xd2) +#define Usage_Con_CaptureGameScreenshot Usage_i8(0xd3) +#define Usage_Con_ShoworHideRecordingIndicator Usage_i8(0xd4) +#define Usage_Con_StartorStopMicrophoneCapture Usage_i8(0xd5) +#define Usage_Con_StartorStopCameraCapture Usage_i8(0xd6) +#define Usage_Con_StartorStopGameBroadcast Usage_i8(0xd7) +#define Usage_Con_StartorStopVoiceDictationSession Usage_i8(0xd8) +#define Usage_Con_InvokeDismissEmojiPicker Usage_i8(0xd9) +#define Usage_Con_Volume Usage_i8(0xe0) +#define Usage_Con_Balance Usage_i8(0xe1) +#define Usage_Con_Mute Usage_i8(0xe2) +#define Usage_Con_Bass Usage_i8(0xe3) +#define Usage_Con_Treble Usage_i8(0xe4) +#define Usage_Con_BassBoost Usage_i8(0xe5) +#define Usage_Con_SurroundMode Usage_i8(0xe6) +#define Usage_Con_Loudness Usage_i8(0xe7) +#define Usage_Con_MPX Usage_i8(0xe8) +#define Usage_Con_VolumeIncrement Usage_i8(0xe9) +#define Usage_Con_VolumeDecrement Usage_i8(0xea) +#define Usage_Con_SpeedSelect Usage_i8(0xf0) +#define Usage_Con_PlaybackSpeed Usage_i8(0xf1) +#define Usage_Con_StandardPlay Usage_i8(0xf2) +#define Usage_Con_LongPlay Usage_i8(0xf3) +#define Usage_Con_ExtendedPlay Usage_i8(0xf4) +#define Usage_Con_Slow Usage_i8(0xf5) +#define Usage_Con_FanEnable Usage_i16(0x100) +#define Usage_Con_FanSpeed Usage_i16(0x101) +#define Usage_Con_LightEnable Usage_i16(0x102) +#define Usage_Con_LightIlluminationLevel Usage_i16(0x103) +#define Usage_Con_ClimateControlEnable Usage_i16(0x104) +#define Usage_Con_RoomTemperature Usage_i16(0x105) +#define Usage_Con_SecurityEnable Usage_i16(0x106) +#define Usage_Con_FireAlarm Usage_i16(0x107) +#define Usage_Con_PoliceAlarm Usage_i16(0x108) +#define Usage_Con_Proximity Usage_i16(0x109) +#define Usage_Con_Motion Usage_i16(0x10a) +#define Usage_Con_DuressAlarm Usage_i16(0x10b) +#define Usage_Con_HoldupAlarm Usage_i16(0x10c) +#define Usage_Con_MedicalAlarm Usage_i16(0x10d) +#define Usage_Con_BalanceRight Usage_i16(0x150) +#define Usage_Con_BalanceLeft Usage_i16(0x151) +#define Usage_Con_BassIncrement Usage_i16(0x152) +#define Usage_Con_BassDecrement Usage_i16(0x153) +#define Usage_Con_TrebleIncrement Usage_i16(0x154) +#define Usage_Con_TrebleDecrement Usage_i16(0x155) +#define Usage_Con_SpeakerSystem Usage_i16(0x160) +#define Usage_Con_ChannelLeft Usage_i16(0x161) +#define Usage_Con_ChannelRight Usage_i16(0x162) +#define Usage_Con_ChannelCenter Usage_i16(0x163) +#define Usage_Con_ChannelFront Usage_i16(0x164) +#define Usage_Con_ChannelCenterFront Usage_i16(0x165) +#define Usage_Con_ChannelSide Usage_i16(0x166) +#define Usage_Con_ChannelSurround Usage_i16(0x167) +#define Usage_Con_ChannelLowFrequencyEnhancement Usage_i16(0x168) +#define Usage_Con_ChannelTop Usage_i16(0x169) +#define Usage_Con_ChannelUnknown Usage_i16(0x16a) +#define Usage_Con_Subchannel Usage_i16(0x170) +#define Usage_Con_SubchannelIncrement Usage_i16(0x171) +#define Usage_Con_SubchannelDecrement Usage_i16(0x172) +#define Usage_Con_AlternateAudioIncrement Usage_i16(0x173) +#define Usage_Con_AlternateAudioDecrement Usage_i16(0x174) +#define Usage_Con_ApplicationLaunchButtons Usage_i16(0x180) +#define Usage_Con_ALLaunchButtonConfigurationTool Usage_i16(0x181) +#define Usage_Con_ALProgrammableButtonConfiguration Usage_i16(0x182) +#define Usage_Con_ALConsumerControlConfiguration Usage_i16(0x183) +#define Usage_Con_ALWordProcessor Usage_i16(0x184) +#define Usage_Con_ALTextEditor Usage_i16(0x185) +#define Usage_Con_ALSpreadsheet Usage_i16(0x186) +#define Usage_Con_ALGraphicsEditor Usage_i16(0x187) +#define Usage_Con_ALPresentationApp Usage_i16(0x188) +#define Usage_Con_ALDatabaseApp Usage_i16(0x189) +#define Usage_Con_ALEmailReader Usage_i16(0x18a) +#define Usage_Con_ALNewsreader Usage_i16(0x18b) +#define Usage_Con_ALVoicemail Usage_i16(0x18c) +#define Usage_Con_ALContactsAddressBook Usage_i16(0x18d) +#define Usage_Con_ALCalendarSchedule Usage_i16(0x18e) +#define Usage_Con_ALTaskProjectManager Usage_i16(0x18f) +#define Usage_Con_ALLogJournalTimecard Usage_i16(0x190) +#define Usage_Con_ALCheckbookFinance Usage_i16(0x191) +#define Usage_Con_ALCalculator Usage_i16(0x192) +#define Usage_Con_ALAVCapturePlayback Usage_i16(0x193) +#define Usage_Con_ALLocalMachineBrowser Usage_i16(0x194) +#define Usage_Con_ALLANWANBrowser Usage_i16(0x195) +#define Usage_Con_ALInternetBrowser Usage_i16(0x196) +#define Usage_Con_ALRemoteNetworkingISPConnect Usage_i16(0x197) +#define Usage_Con_ALNetworkConference Usage_i16(0x198) +#define Usage_Con_ALNetworkChat Usage_i16(0x199) +#define Usage_Con_ALTelephonyDialer Usage_i16(0x19a) +#define Usage_Con_ALLogon Usage_i16(0x19b) +#define Usage_Con_ALLogoff Usage_i16(0x19c) +#define Usage_Con_ALLogonLogoff Usage_i16(0x19d) +#define Usage_Con_ALTerminalLockScreensaver Usage_i16(0x19e) +#define Usage_Con_ALControlPanel Usage_i16(0x19f) +#define Usage_Con_ALCommandLineProcessorRun Usage_i16(0x1a0) +#define Usage_Con_ALProcessTaskManager Usage_i16(0x1a1) +#define Usage_Con_ALSelectTaskApplication Usage_i16(0x1a2) +#define Usage_Con_ALNextTaskApplication Usage_i16(0x1a3) +#define Usage_Con_ALPreviousTaskApplication Usage_i16(0x1a4) +#define Usage_Con_ALPreemptiveHaltTaskApplication Usage_i16(0x1a5) +#define Usage_Con_ALIntegratedHelpCenter Usage_i16(0x1a6) +#define Usage_Con_ALDocuments Usage_i16(0x1a7) +#define Usage_Con_ALThesaurus Usage_i16(0x1a8) +#define Usage_Con_ALDictionary Usage_i16(0x1a9) +#define Usage_Con_ALDesktop Usage_i16(0x1aa) +#define Usage_Con_ALSpellCheck Usage_i16(0x1ab) +#define Usage_Con_ALGrammarCheck Usage_i16(0x1ac) +#define Usage_Con_ALWirelessStatus Usage_i16(0x1ad) +#define Usage_Con_ALKeyboardLayout Usage_i16(0x1ae) +#define Usage_Con_ALVirusProtection Usage_i16(0x1af) +#define Usage_Con_ALEncryption Usage_i16(0x1b0) +#define Usage_Con_ALScreenSaver Usage_i16(0x1b1) +#define Usage_Con_ALAlarms Usage_i16(0x1b2) +#define Usage_Con_ALClock Usage_i16(0x1b3) +#define Usage_Con_ALFileBrowser Usage_i16(0x1b4) +#define Usage_Con_ALPowerStatus Usage_i16(0x1b5) +#define Usage_Con_ALImageBrowser Usage_i16(0x1b6) +#define Usage_Con_ALAudioBrowser Usage_i16(0x1b7) +#define Usage_Con_ALMovieBrowser Usage_i16(0x1b8) +#define Usage_Con_ALDigitalRightsManager Usage_i16(0x1b9) +#define Usage_Con_ALDigitalWallet Usage_i16(0x1ba) +#define Usage_Con_ALInstantMessaging Usage_i16(0x1bc) +#define Usage_Con_ALOEMFeaturesTipsTutorialBrowser Usage_i16(0x1bd) +#define Usage_Con_ALOEMHelp Usage_i16(0x1be) +#define Usage_Con_ALOnlineCommunity Usage_i16(0x1bf) +#define Usage_Con_ALEntertainmentContentBrowser Usage_i16(0x1c0) +#define Usage_Con_ALOnlineShoppingBrowser Usage_i16(0x1c1) +#define Usage_Con_ALSmartCardInformationHelp Usage_i16(0x1c2) +#define Usage_Con_ALMarketMonitorFinanceBrowser Usage_i16(0x1c3) +#define Usage_Con_ALCustomizedCorporateNewsBrowser Usage_i16(0x1c4) +#define Usage_Con_ALOnlineActivityBrowser Usage_i16(0x1c5) +#define Usage_Con_ALResearchSearchBrowser Usage_i16(0x1c6) +#define Usage_Con_ALAudioPlayer Usage_i16(0x1c7) +#define Usage_Con_ALMessageStatus Usage_i16(0x1c8) +#define Usage_Con_ALContactSync Usage_i16(0x1c9) +#define Usage_Con_ALNavigation Usage_i16(0x1ca) +#define Usage_Con_ALContextawareDesktopAssistant Usage_i16(0x1cb) +#define Usage_Con_GenericGUIApplicationControls Usage_i16(0x200) +#define Usage_Con_ACNew Usage_i16(0x201) +#define Usage_Con_ACOpen Usage_i16(0x202) +#define Usage_Con_ACClose Usage_i16(0x203) +#define Usage_Con_ACExit Usage_i16(0x204) +#define Usage_Con_ACMaximize Usage_i16(0x205) +#define Usage_Con_ACMinimize Usage_i16(0x206) +#define Usage_Con_ACSave Usage_i16(0x207) +#define Usage_Con_ACPrint Usage_i16(0x208) +#define Usage_Con_ACProperties Usage_i16(0x209) +#define Usage_Con_ACUndo Usage_i16(0x21a) +#define Usage_Con_ACCopy Usage_i16(0x21b) +#define Usage_Con_ACCut Usage_i16(0x21c) +#define Usage_Con_ACPaste Usage_i16(0x21d) +#define Usage_Con_ACSelectAll Usage_i16(0x21e) +#define Usage_Con_ACFind Usage_i16(0x21f) +#define Usage_Con_ACFindandReplace Usage_i16(0x220) +#define Usage_Con_ACSearch Usage_i16(0x221) +#define Usage_Con_ACGoTo Usage_i16(0x222) +#define Usage_Con_ACHome Usage_i16(0x223) +#define Usage_Con_ACBack Usage_i16(0x224) +#define Usage_Con_ACForward Usage_i16(0x225) +#define Usage_Con_ACStop Usage_i16(0x226) +#define Usage_Con_ACRefresh Usage_i16(0x227) +#define Usage_Con_ACPreviousLink Usage_i16(0x228) +#define Usage_Con_ACNextLink Usage_i16(0x229) +#define Usage_Con_ACBookmarks Usage_i16(0x22a) +#define Usage_Con_ACHistory Usage_i16(0x22b) +#define Usage_Con_ACSubscriptions Usage_i16(0x22c) +#define Usage_Con_ACZoomIn Usage_i16(0x22d) +#define Usage_Con_ACZoomOut Usage_i16(0x22e) +#define Usage_Con_ACZoom Usage_i16(0x22f) +#define Usage_Con_ACFullScreenView Usage_i16(0x230) +#define Usage_Con_ACNormalView Usage_i16(0x231) +#define Usage_Con_ACViewToggle Usage_i16(0x232) +#define Usage_Con_ACScrollUp Usage_i16(0x233) +#define Usage_Con_ACScrollDown Usage_i16(0x234) +#define Usage_Con_ACScroll Usage_i16(0x235) +#define Usage_Con_ACPanLeft Usage_i16(0x236) +#define Usage_Con_ACPanRight Usage_i16(0x237) +#define Usage_Con_ACPan Usage_i16(0x238) +#define Usage_Con_ACNewWindow Usage_i16(0x239) +#define Usage_Con_ACTileHorizontally Usage_i16(0x23a) +#define Usage_Con_ACTileVertically Usage_i16(0x23b) +#define Usage_Con_ACFormat Usage_i16(0x23c) +#define Usage_Con_ACEdit Usage_i16(0x23d) +#define Usage_Con_ACBold Usage_i16(0x23e) +#define Usage_Con_ACItalics Usage_i16(0x23f) +#define Usage_Con_ACUnderline Usage_i16(0x240) +#define Usage_Con_ACStrikethrough Usage_i16(0x241) +#define Usage_Con_ACSubscript Usage_i16(0x242) +#define Usage_Con_ACSuperscript Usage_i16(0x243) +#define Usage_Con_ACAllCaps Usage_i16(0x244) +#define Usage_Con_ACRotate Usage_i16(0x245) +#define Usage_Con_ACResize Usage_i16(0x246) +#define Usage_Con_ACFlipHorizontal Usage_i16(0x247) +#define Usage_Con_ACFlipVertical Usage_i16(0x248) +#define Usage_Con_ACMirrorHorizontal Usage_i16(0x249) +#define Usage_Con_ACMirrorVertical Usage_i16(0x24a) +#define Usage_Con_ACFontSelect Usage_i16(0x24b) +#define Usage_Con_ACFontColor Usage_i16(0x24c) +#define Usage_Con_ACFontSize Usage_i16(0x24d) +#define Usage_Con_ACJustifyLeft Usage_i16(0x24e) +#define Usage_Con_ACJustifyCenterH Usage_i16(0x24f) +#define Usage_Con_ACJustifyRight Usage_i16(0x250) +#define Usage_Con_ACJustifyBlockH Usage_i16(0x251) +#define Usage_Con_ACJustifyTop Usage_i16(0x252) +#define Usage_Con_ACJustifyCenterV Usage_i16(0x253) +#define Usage_Con_ACJustifyBottom Usage_i16(0x254) +#define Usage_Con_ACJustifyBlockV Usage_i16(0x255) +#define Usage_Con_ACIndentDecrease Usage_i16(0x256) +#define Usage_Con_ACIndentIncrease Usage_i16(0x257) +#define Usage_Con_ACNumberedList Usage_i16(0x258) +#define Usage_Con_ACRestartNumbering Usage_i16(0x259) +#define Usage_Con_ACBulletedList Usage_i16(0x25a) +#define Usage_Con_ACPromote Usage_i16(0x25b) +#define Usage_Con_ACDemote Usage_i16(0x25c) +#define Usage_Con_ACYes Usage_i16(0x25d) +#define Usage_Con_ACNo Usage_i16(0x25e) +#define Usage_Con_ACCancel Usage_i16(0x25f) +#define Usage_Con_ACCatalog Usage_i16(0x260) +#define Usage_Con_ACBuyCheckout Usage_i16(0x261) +#define Usage_Con_ACAddtoCart Usage_i16(0x262) +#define Usage_Con_ACExpand Usage_i16(0x263) +#define Usage_Con_ACExpandAll Usage_i16(0x264) +#define Usage_Con_ACCollapse Usage_i16(0x265) +#define Usage_Con_ACCollapseAll Usage_i16(0x266) +#define Usage_Con_ACPrintPreview Usage_i16(0x267) +#define Usage_Con_ACPasteSpecial Usage_i16(0x268) +#define Usage_Con_ACInsertMode Usage_i16(0x269) +#define Usage_Con_ACDelete Usage_i16(0x26a) +#define Usage_Con_ACLock Usage_i16(0x26b) +#define Usage_Con_ACUnlock Usage_i16(0x26c) +#define Usage_Con_ACProtect Usage_i16(0x26d) +#define Usage_Con_ACUnprotect Usage_i16(0x26e) +#define Usage_Con_ACAttachComment Usage_i16(0x26f) +#define Usage_Con_ACDeleteComment Usage_i16(0x270) +#define Usage_Con_ACViewComment Usage_i16(0x271) +#define Usage_Con_ACSelectWord Usage_i16(0x272) +#define Usage_Con_ACSelectSentence Usage_i16(0x273) +#define Usage_Con_ACSelectParagraph Usage_i16(0x274) +#define Usage_Con_ACSelectColumn Usage_i16(0x275) +#define Usage_Con_ACSelectRow Usage_i16(0x276) +#define Usage_Con_ACSelectTable Usage_i16(0x277) +#define Usage_Con_ACSelectObject Usage_i16(0x278) +#define Usage_Con_ACRedoRepeat Usage_i16(0x279) +#define Usage_Con_ACSort Usage_i16(0x27a) +#define Usage_Con_ACSortAscending Usage_i16(0x27b) +#define Usage_Con_ACSortDescending Usage_i16(0x27c) +#define Usage_Con_ACFilter Usage_i16(0x27d) +#define Usage_Con_ACSetClock Usage_i16(0x27e) +#define Usage_Con_ACViewClock Usage_i16(0x27f) +#define Usage_Con_ACSelectTimeZone Usage_i16(0x280) +#define Usage_Con_ACEditTimeZones Usage_i16(0x281) +#define Usage_Con_ACSetAlarm Usage_i16(0x282) +#define Usage_Con_ACClearAlarm Usage_i16(0x283) +#define Usage_Con_ACSnoozeAlarm Usage_i16(0x284) +#define Usage_Con_ACResetAlarm Usage_i16(0x285) +#define Usage_Con_ACSynchronize Usage_i16(0x286) +#define Usage_Con_ACSendReceive Usage_i16(0x287) +#define Usage_Con_ACSendTo Usage_i16(0x288) +#define Usage_Con_ACReply Usage_i16(0x289) +#define Usage_Con_ACReplyAll Usage_i16(0x28a) +#define Usage_Con_ACForwardMsg Usage_i16(0x28b) +#define Usage_Con_ACSend Usage_i16(0x28c) +#define Usage_Con_ACAttachFile Usage_i16(0x28d) +#define Usage_Con_ACUpload Usage_i16(0x28e) +#define Usage_Con_ACDownloadSaveTargetAs Usage_i16(0x28f) +#define Usage_Con_ACSetBorders Usage_i16(0x290) +#define Usage_Con_ACInsertRow Usage_i16(0x291) +#define Usage_Con_ACInsertColumn Usage_i16(0x292) +#define Usage_Con_ACInsertFile Usage_i16(0x293) +#define Usage_Con_ACInsertPicture Usage_i16(0x294) +#define Usage_Con_ACInsertObject Usage_i16(0x295) +#define Usage_Con_ACInsertSymbol Usage_i16(0x296) +#define Usage_Con_ACSaveandClose Usage_i16(0x297) +#define Usage_Con_ACRename Usage_i16(0x298) +#define Usage_Con_ACMerge Usage_i16(0x299) +#define Usage_Con_ACSplit Usage_i16(0x29a) +#define Usage_Con_ACDisributeHorizontally Usage_i16(0x29b) +#define Usage_Con_ACDistributeVertically Usage_i16(0x29c) +#define Usage_Con_ACNextKeyboardLayoutSelect Usage_i16(0x29d) +#define Usage_Con_ACNavigationGuidance Usage_i16(0x29e) +#define Usage_Con_ACDesktopShowAllWindows Usage_i16(0x29f) +#define Usage_Con_ACSoftKeyLeft Usage_i16(0x2a0) +#define Usage_Con_ACSoftKeyRight Usage_i16(0x2a1) +#define Usage_Con_ACDesktopShowAllApplications Usage_i16(0x2a2) +#define Usage_Con_ACIdleKeepAlive Usage_i16(0x2b0) +#define Usage_Con_ExtendedKeyboardAttributesCollection Usage_i16(0x2c0) +#define Usage_Con_KeyboardFormFactor Usage_i16(0x2c1) +#define Usage_Con_KeyboardKeyType Usage_i16(0x2c2) +#define Usage_Con_KeyboardPhysicalLayout Usage_i16(0x2c3) +#define Usage_Con_VendorSpecificKeyboardPhysicalLayout Usage_i16(0x2c4) +#define Usage_Con_KeyboardIETFLanguageTagIndex Usage_i16(0x2c5) +#define Usage_Con_ImplementedKeyboardInputAssistControls Usage_i16(0x2c6) +#define Usage_Con_KeyboardInputAssistPrevious Usage_i16(0x2c7) +#define Usage_Con_KeyboardInputAssistNext Usage_i16(0x2c8) +#define Usage_Con_KeyboardInputAssistPreviousGroup Usage_i16(0x2c9) +#define Usage_Con_KeyboardInputAssistNextGroup Usage_i16(0x2ca) +#define Usage_Con_KeyboardInputAssistAccept Usage_i16(0x2cb) +#define Usage_Con_KeyboardInputAssistCancel Usage_i16(0x2cc) +#define Usage_Con_PrivacyScreenToggle Usage_i16(0x2d0) +#define Usage_Con_PrivacyScreenLevelDecrement Usage_i16(0x2d1) +#define Usage_Con_PrivacyScreenLevelIncrement Usage_i16(0x2d2) +#define Usage_Con_PrivacyScreenLevelMinimum Usage_i16(0x2d3) +#define Usage_Con_PrivacyScreenLevelMaximum Usage_i16(0x2d4) +#define Usage_Con_ContactEdited Usage_i16(0x500) +#define Usage_Con_ContactAdded Usage_i16(0x501) +#define Usage_Con_ContactRecordActive Usage_i16(0x502) +#define Usage_Con_ContactIndex Usage_i16(0x503) +#define Usage_Con_ContactNickname Usage_i16(0x504) +#define Usage_Con_ContactFirstName Usage_i16(0x505) +#define Usage_Con_ContactLastName Usage_i16(0x506) +#define Usage_Con_ContactFullName Usage_i16(0x507) +#define Usage_Con_ContactPhoneNumberPersonal Usage_i16(0x508) +#define Usage_Con_ContactPhoneNumberBusiness Usage_i16(0x509) +#define Usage_Con_ContactPhoneNumberMobile Usage_i16(0x50a) +#define Usage_Con_ContactPhoneNumberPager Usage_i16(0x50b) +#define Usage_Con_ContactPhoneNumberFax Usage_i16(0x50c) +#define Usage_Con_ContactPhoneNumberOther Usage_i16(0x50d) +#define Usage_Con_ContactEmailPersonal Usage_i16(0x50e) +#define Usage_Con_ContactEmailBusiness Usage_i16(0x50f) +#define Usage_Con_ContactEmailOther Usage_i16(0x510) +#define Usage_Con_ContactEmailMain Usage_i16(0x511) +#define Usage_Con_ContactSpeedDialNumber Usage_i16(0x512) +#define Usage_Con_ContactStatusFlag Usage_i16(0x513) +#define Usage_Con_ContactMisc Usage_i16(0x514) +#define Usage_Dig_Digitizer Usage_i8(0x1) +#define Usage_Dig_Pen Usage_i8(0x2) +#define Usage_Dig_LightPen Usage_i8(0x3) +#define Usage_Dig_TouchScreen Usage_i8(0x4) +#define Usage_Dig_TouchPad Usage_i8(0x5) +#define Usage_Dig_Whiteboard Usage_i8(0x6) +#define Usage_Dig_CoordinateMeasuringMachine Usage_i8(0x7) +#define Usage_Dig_ThreeDDigitizer Usage_i8(0x8) +#define Usage_Dig_StereoPlotter Usage_i8(0x9) +#define Usage_Dig_ArticulatedArm Usage_i8(0xa) +#define Usage_Dig_Armature Usage_i8(0xb) +#define Usage_Dig_MultiplePointDigitizer Usage_i8(0xc) +#define Usage_Dig_FreeSpaceWand Usage_i8(0xd) +#define Usage_Dig_DeviceConfiguration Usage_i8(0xe) +#define Usage_Dig_CapacitiveHeatMapDigitizer Usage_i8(0xf) +#define Usage_Dig_Stylus Usage_i8(0x20) +#define Usage_Dig_Puck Usage_i8(0x21) +#define Usage_Dig_Finger Usage_i8(0x22) +#define Usage_Dig_Devicesettings Usage_i8(0x23) +#define Usage_Dig_CharacterGesture Usage_i8(0x24) +#define Usage_Dig_TipPressure Usage_i8(0x30) +#define Usage_Dig_BarrelPressure Usage_i8(0x31) +#define Usage_Dig_InRange Usage_i8(0x32) +#define Usage_Dig_Touch Usage_i8(0x33) +#define Usage_Dig_Untouch Usage_i8(0x34) +#define Usage_Dig_Tap Usage_i8(0x35) +#define Usage_Dig_Quality Usage_i8(0x36) +#define Usage_Dig_DataValid Usage_i8(0x37) +#define Usage_Dig_TransducerIndex Usage_i8(0x38) +#define Usage_Dig_TabletFunctionKeys Usage_i8(0x39) +#define Usage_Dig_ProgramChangeKeys Usage_i8(0x3a) +#define Usage_Dig_BatteryStrength Usage_i8(0x3b) +#define Usage_Dig_Invert Usage_i8(0x3c) +#define Usage_Dig_XTilt Usage_i8(0x3d) +#define Usage_Dig_YTilt Usage_i8(0x3e) +#define Usage_Dig_Azimuth Usage_i8(0x3f) +#define Usage_Dig_Altitude Usage_i8(0x40) +#define Usage_Dig_Twist Usage_i8(0x41) +#define Usage_Dig_TipSwitch Usage_i8(0x42) +#define Usage_Dig_SecondaryTipSwitch Usage_i8(0x43) +#define Usage_Dig_BarrelSwitch Usage_i8(0x44) +#define Usage_Dig_Eraser Usage_i8(0x45) +#define Usage_Dig_TabletPick Usage_i8(0x46) +#define Usage_Dig_TouchValid Usage_i8(0x47) +#define Usage_Dig_Width Usage_i8(0x48) +#define Usage_Dig_Height Usage_i8(0x49) +#define Usage_Dig_ContactIdentifier Usage_i8(0x51) +#define Usage_Dig_DeviceMode Usage_i8(0x52) +#define Usage_Dig_DeviceIdentifier Usage_i8(0x53) +#define Usage_Dig_ContactCount Usage_i8(0x54) +#define Usage_Dig_ContactCountMaximum Usage_i8(0x55) +#define Usage_Dig_ScanTime Usage_i8(0x56) +#define Usage_Dig_SurfaceSwitch Usage_i8(0x57) +#define Usage_Dig_ButtonSwitch Usage_i8(0x58) +#define Usage_Dig_PadType Usage_i8(0x59) +#define Usage_Dig_SecondaryBarrelSwitch Usage_i8(0x5a) +#define Usage_Dig_TransducerSerialNumber Usage_i8(0x5b) +#define Usage_Dig_PreferredColor Usage_i8(0x5c) +#define Usage_Dig_PreferredColorisLocked Usage_i8(0x5d) +#define Usage_Dig_PreferredLineWidth Usage_i8(0x5e) +#define Usage_Dig_PreferredLineWidthisLocked Usage_i8(0x5f) +#define Usage_Dig_LatencyMode Usage_i8(0x60) +#define Usage_Dig_GestureCharacterQuality Usage_i8(0x61) +#define Usage_Dig_CharacterGestureDataLength Usage_i8(0x62) +#define Usage_Dig_CharacterGestureData Usage_i8(0x63) +#define Usage_Dig_GestureCharacterEncoding Usage_i8(0x64) +#define Usage_Dig_UTF8CharacterGestureEncoding Usage_i8(0x65) +#define Usage_Dig_UTF16LittleEndianCharacterGestureEncoding Usage_i8(0x66) +#define Usage_Dig_UTF16BigEndianCharacterGestureEncoding Usage_i8(0x67) +#define Usage_Dig_UTF32LittleEndianCharacterGestureEncoding Usage_i8(0x68) +#define Usage_Dig_UTF32BigEndianCharacterGestureEncoding Usage_i8(0x69) +#define Usage_Dig_CapacitiveHeatMapProtocolVendorID Usage_i8(0x6a) +#define Usage_Dig_CapacitiveHeatMapProtocolVersion Usage_i8(0x6b) +#define Usage_Dig_CapacitiveHeatMapFrameData Usage_i8(0x6c) +#define Usage_Dig_GestureCharacterEnable Usage_i8(0x6d) +#define Usage_Dig_TransducerSerialNumberPart2 Usage_i8(0x6e) +#define Usage_Dig_NoPreferredColor Usage_i8(0x6f) +#define Usage_Dig_PreferredLineStyle Usage_i8(0x70) +#define Usage_Dig_PreferredLineStyleisLocked Usage_i8(0x71) +#define Usage_Dig_Ink Usage_i8(0x72) +#define Usage_Dig_Pencil Usage_i8(0x73) +#define Usage_Dig_Highlighter Usage_i8(0x74) +#define Usage_Dig_ChiselMarker Usage_i8(0x75) +#define Usage_Dig_Brush Usage_i8(0x76) +#define Usage_Dig_NoPreference Usage_i8(0x77) +#define Usage_Dig_DigitizerDiagnostic Usage_i8(0x80) +#define Usage_Dig_DigitizerError Usage_i8(0x81) +#define Usage_Dig_ErrNormalStatus Usage_i8(0x82) +#define Usage_Dig_ErrTransducersExceeded Usage_i8(0x83) +#define Usage_Dig_ErrFullTransFeaturesUnavailable Usage_i8(0x84) +#define Usage_Dig_ErrChargeLow Usage_i8(0x85) +#define Usage_Dig_TransducerSoftwareInfo Usage_i8(0x90) +#define Usage_Dig_TransducerVendorId Usage_i8(0x91) +#define Usage_Dig_TransducerProductId Usage_i8(0x92) +#define Usage_Dig_DeviceSupportedProtocols Usage_i8(0x93) +#define Usage_Dig_TransducerSupportedProtocols Usage_i8(0x94) +#define Usage_Dig_NoProtocol Usage_i8(0x95) +#define Usage_Dig_WacomAESProtocol Usage_i8(0x96) +#define Usage_Dig_USIProtocol Usage_i8(0x97) +#define Usage_Dig_MicrosoftPenProtocol Usage_i8(0x98) +#define Usage_Dig_SupportedReportRates Usage_i8(0xa0) +#define Usage_Dig_ReportRate Usage_i8(0xa1) +#define Usage_Dig_TransducerConnected Usage_i8(0xa2) +#define Usage_Dig_SwitchDisabled Usage_i8(0xa3) +#define Usage_Dig_SwitchUnimplemented Usage_i8(0xa4) +#define Usage_Dig_TransducerSwitches Usage_i8(0xa5) +#define Usage_Dig_TransducerIndexSelector Usage_i8(0xa6) +#define Usage_Dig_ButtonPressThreshold Usage_i8(0xb0) +#define Usage_Hap_SimpleHapticController Usage_i8(0x1) +#define Usage_Hap_WaveformList Usage_i8(0x10) +#define Usage_Hap_DurationList Usage_i8(0x11) +#define Usage_Hap_AutoTrigger Usage_i8(0x20) +#define Usage_Hap_ManualTrigger Usage_i8(0x21) +#define Usage_Hap_AutoTriggerAssociatedControl Usage_i8(0x22) +#define Usage_Hap_Intensity Usage_i8(0x23) +#define Usage_Hap_RepeatCount Usage_i8(0x24) +#define Usage_Hap_RetriggerPeriod Usage_i8(0x25) +#define Usage_Hap_WaveformVendorPage Usage_i8(0x26) +#define Usage_Hap_WaveformVendorID Usage_i8(0x27) +#define Usage_Hap_WaveformCutoffTime Usage_i8(0x28) +#define Usage_Hap_WaveformNone Usage_i16(0x1001) +#define Usage_Hap_WaveformStop Usage_i16(0x1002) +#define Usage_Hap_WaveformClick Usage_i16(0x1003) +#define Usage_Hap_WaveformBuzzContinuous Usage_i16(0x1004) +#define Usage_Hap_WaveformRumbleContinuous Usage_i16(0x1005) +#define Usage_Hap_WaveformPress Usage_i16(0x1006) +#define Usage_Hap_WaveformRelease Usage_i16(0x1007) +#define Usage_Hap_WaveformHover Usage_i16(0x1008) +#define Usage_Hap_WaveformSuccess Usage_i16(0x1009) +#define Usage_Hap_WaveformError Usage_i16(0x100a) +#define Usage_Hap_WaveformInkContinuous Usage_i16(0x100b) +#define Usage_Hap_WaveformPencilContinuous Usage_i16(0x100c) +#define Usage_Hap_WaveformMarkerContinuous Usage_i16(0x100d) +#define Usage_Hap_WaveformChiselMarkerContinuous Usage_i16(0x100e) +#define Usage_Hap_WaveformBrushContinuous Usage_i16(0x100f) +#define Usage_Hap_WaveformEraserContinuous Usage_i16(0x1010) +#define Usage_Hap_WaveformSparkleContinuous Usage_i16(0x1011) +#define Usage_PID_PhysicalInputDevice Usage_i8(0x1) +#define Usage_PID_Normal Usage_i8(0x20) +#define Usage_PID_SetEffectReport Usage_i8(0x21) +#define Usage_PID_EffectParameterBlockIndex Usage_i8(0x22) +#define Usage_PID_ParameterBlockOffset Usage_i8(0x23) +#define Usage_PID_ROMFlag Usage_i8(0x24) +#define Usage_PID_EffectType Usage_i8(0x25) +#define Usage_PID_ETConstantForce Usage_i8(0x26) +#define Usage_PID_ETRamp Usage_i8(0x27) +#define Usage_PID_ETCustomForce Usage_i8(0x28) +#define Usage_PID_ETSquare Usage_i8(0x30) +#define Usage_PID_ETSine Usage_i8(0x31) +#define Usage_PID_ETTriangle Usage_i8(0x32) +#define Usage_PID_ETSawtoothUp Usage_i8(0x33) +#define Usage_PID_ETSawtoothDown Usage_i8(0x34) +#define Usage_PID_ETSpring Usage_i8(0x40) +#define Usage_PID_ETDamper Usage_i8(0x41) +#define Usage_PID_ETInertia Usage_i8(0x42) +#define Usage_PID_ETFriction Usage_i8(0x43) +#define Usage_PID_Duration Usage_i8(0x50) +#define Usage_PID_SamplePeriod Usage_i8(0x51) +#define Usage_PID_Gain Usage_i8(0x52) +#define Usage_PID_TriggerButton Usage_i8(0x53) +#define Usage_PID_TriggerRepeatInterval Usage_i8(0x54) +#define Usage_PID_AxesEnable Usage_i8(0x55) +#define Usage_PID_DirectionEnable Usage_i8(0x56) +#define Usage_PID_Direction Usage_i8(0x57) +#define Usage_PID_TypeSpecificBlockOffset Usage_i8(0x58) +#define Usage_PID_BlockType Usage_i8(0x59) +#define Usage_PID_SetEnvelopeReport Usage_i8(0x5a) +#define Usage_PID_AttackLevel Usage_i8(0x5b) +#define Usage_PID_AttackTime Usage_i8(0x5c) +#define Usage_PID_FadeLevel Usage_i8(0x5d) +#define Usage_PID_FadeTime Usage_i8(0x5e) +#define Usage_PID_SetConditionReport Usage_i8(0x5f) +#define Usage_PID_CenterPointOffset Usage_i8(0x60) +#define Usage_PID_PositiveCoefficient Usage_i8(0x61) +#define Usage_PID_NegativeCoefficient Usage_i8(0x62) +#define Usage_PID_PositiveSaturation Usage_i8(0x63) +#define Usage_PID_NegativeSaturation Usage_i8(0x64) +#define Usage_PID_DeadBand Usage_i8(0x65) +#define Usage_PID_DownloadForceSample Usage_i8(0x66) +#define Usage_PID_IsochCustomForceEnable Usage_i8(0x67) +#define Usage_PID_CustomForceDataReport Usage_i8(0x68) +#define Usage_PID_CustomForceData Usage_i8(0x69) +#define Usage_PID_CustomForceVendorDefinedData Usage_i8(0x6a) +#define Usage_PID_SetCustomForceReport Usage_i8(0x6b) +#define Usage_PID_CustomForceDataOffset Usage_i8(0x6c) +#define Usage_PID_SampleCount Usage_i8(0x6d) +#define Usage_PID_SetPeriodicReport Usage_i8(0x6e) +#define Usage_PID_Offset Usage_i8(0x6f) +#define Usage_PID_Magnitude Usage_i8(0x70) +#define Usage_PID_Phase Usage_i8(0x71) +#define Usage_PID_Period Usage_i8(0x72) +#define Usage_PID_SetConstantForceReport Usage_i8(0x73) +#define Usage_PID_SetRampForceReport Usage_i8(0x74) +#define Usage_PID_RampStart Usage_i8(0x75) +#define Usage_PID_RampEnd Usage_i8(0x76) +#define Usage_PID_EffectOperationReport Usage_i8(0x77) +#define Usage_PID_EffectOperation Usage_i8(0x78) +#define Usage_PID_OpEffectStart Usage_i8(0x79) +#define Usage_PID_OpEffectStartSolo Usage_i8(0x7a) +#define Usage_PID_OpEffectStop Usage_i8(0x7b) +#define Usage_PID_LoopCount Usage_i8(0x7c) +#define Usage_PID_DeviceGainReport Usage_i8(0x7d) +#define Usage_PID_DeviceGain Usage_i8(0x7e) +#define Usage_PID_ParameterBlockPoolsReport Usage_i8(0x7f) +#define Usage_PID_RAMPoolSize Usage_i8(0x80) +#define Usage_PID_ROMPoolSize Usage_i8(0x81) +#define Usage_PID_ROMEffectBlockCount Usage_i8(0x82) +#define Usage_PID_SimultaneousEffectsMax Usage_i8(0x83) +#define Usage_PID_PoolAlignment Usage_i8(0x84) +#define Usage_PID_ParameterBlockMoveReport Usage_i8(0x85) +#define Usage_PID_MoveSource Usage_i8(0x86) +#define Usage_PID_MoveDestination Usage_i8(0x87) +#define Usage_PID_MoveLength Usage_i8(0x88) +#define Usage_PID_EffectParameterBlockLoadReport Usage_i8(0x89) +#define Usage_PID_EffectParameterBlockLoadStatus Usage_i8(0x8b) +#define Usage_PID_BlockLoadSuccess Usage_i8(0x8c) +#define Usage_PID_BlockLoadFull Usage_i8(0x8d) +#define Usage_PID_BlockLoadError Usage_i8(0x8e) +#define Usage_PID_BlockHandle Usage_i8(0x8f) +#define Usage_PID_EffectParameterBlockFreeReport Usage_i8(0x90) +#define Usage_PID_TypeSpecificBlockHandle Usage_i8(0x91) +#define Usage_PID_PIDStateReport Usage_i8(0x92) +#define Usage_PID_EffectPlaying Usage_i8(0x94) +#define Usage_PID_PIDDeviceControlReport Usage_i8(0x95) +#define Usage_PID_PIDDeviceControl Usage_i8(0x96) +#define Usage_PID_DCEnableActuators Usage_i8(0x97) +#define Usage_PID_DCDisableActuators Usage_i8(0x98) +#define Usage_PID_DCStopAllEffects Usage_i8(0x99) +#define Usage_PID_DCReset Usage_i8(0x9a) +#define Usage_PID_DCPause Usage_i8(0x9b) +#define Usage_PID_DCContinue Usage_i8(0x9c) +#define Usage_PID_DevicePaused Usage_i8(0x9f) +#define Usage_PID_ActuatorsEnabled Usage_i8(0xa0) +#define Usage_PID_SafetySwitch Usage_i8(0xa4) +#define Usage_PID_ActuatorOverrideSwitch Usage_i8(0xa5) +#define Usage_PID_ActuatorPower Usage_i8(0xa6) +#define Usage_PID_StartDelay Usage_i8(0xa7) +#define Usage_PID_ParameterBlockSize Usage_i8(0xa8) +#define Usage_PID_DeviceManagedPool Usage_i8(0xa9) +#define Usage_PID_SharedParameterBlocks Usage_i8(0xaa) +#define Usage_PID_CreateNewEffectParameterBlockReport Usage_i8(0xab) +#define Usage_PID_RAMPoolAvailable Usage_i8(0xac) +#define Usage_SC_SocControl Usage_i8(0x1) +#define Usage_SC_FirmwareTransfer Usage_i8(0x2) +#define Usage_SC_FirmwareFileId Usage_i8(0x3) +#define Usage_SC_FileOffsetInBytes Usage_i8(0x4) +#define Usage_SC_FileTransferSizeMaxInBytes Usage_i8(0x5) +#define Usage_SC_FilePayload Usage_i8(0x6) +#define Usage_SC_FilePayloadSizeInBytes Usage_i8(0x7) +#define Usage_SC_FilePayloadContainsLastBytes Usage_i8(0x8) +#define Usage_SC_FileTransferStop Usage_i8(0x9) +#define Usage_SC_FileTransferTillEnd Usage_i8(0xa) +#define Usage_EHT_EyeTracker Usage_i8(0x1) +#define Usage_EHT_HeadTracker Usage_i8(0x2) +#define Usage_EHT_TrackingData Usage_i8(0x10) +#define Usage_EHT_Capabilities Usage_i8(0x11) +#define Usage_EHT_Configuration Usage_i8(0x12) +#define Usage_EHT_Status Usage_i8(0x13) +#define Usage_EHT_Control Usage_i8(0x14) +#define Usage_EHT_SensorTimestamp Usage_i8(0x20) +#define Usage_EHT_PositionX Usage_i8(0x21) +#define Usage_EHT_PositionY Usage_i8(0x22) +#define Usage_EHT_PositionZ Usage_i8(0x23) +#define Usage_EHT_GazePoint Usage_i8(0x24) +#define Usage_EHT_LeftEyePosition Usage_i8(0x25) +#define Usage_EHT_RightEyePosition Usage_i8(0x26) +#define Usage_EHT_HeadPosition Usage_i8(0x27) +#define Usage_EHT_HeadDirectionPoint Usage_i8(0x28) +#define Usage_EHT_RotationaboutXaxis Usage_i8(0x29) +#define Usage_EHT_RotationaboutYaxis Usage_i8(0x2a) +#define Usage_EHT_RotationaboutZaxis Usage_i8(0x2b) +#define Usage_EHT_TrackerQuality Usage_i16(0x100) +#define Usage_EHT_MinimumTrackingDistance Usage_i16(0x101) +#define Usage_EHT_OptimumTrackingDistance Usage_i16(0x102) +#define Usage_EHT_MaximumTrackingDistance Usage_i16(0x103) +#define Usage_EHT_MaximumScreenPlaneWidth Usage_i16(0x104) +#define Usage_EHT_MaximumScreenPlaneHeight Usage_i16(0x105) +#define Usage_EHT_DisplayManufacturerID Usage_i16(0x200) +#define Usage_EHT_DisplayProductID Usage_i16(0x201) +#define Usage_EHT_DisplaySerialNumber Usage_i16(0x202) +#define Usage_EHT_DisplayManufacturerDate Usage_i16(0x203) +#define Usage_EHT_CalibratedScreenWidth Usage_i16(0x204) +#define Usage_EHT_CalibratedScreenHeight Usage_i16(0x205) +#define Usage_EHT_SamplingFrequency Usage_i16(0x300) +#define Usage_EHT_ConfigurationStatus Usage_i16(0x301) +#define Usage_EHT_DeviceModeRequest Usage_i16(0x400) +#define Usage_AD_AlphanumericDisplay Usage_i8(0x1) +#define Usage_AD_AuxiliaryDisplay Usage_i8(0x2) +#define Usage_AD_DisplayAttributesReport Usage_i8(0x20) +#define Usage_AD_ASCIICharacterSet Usage_i8(0x21) +#define Usage_AD_DataReadBack Usage_i8(0x22) +#define Usage_AD_FontReadBack Usage_i8(0x23) +#define Usage_AD_DisplayControlReport Usage_i8(0x24) +#define Usage_AD_ClearDisplay Usage_i8(0x25) +#define Usage_AD_DisplayEnable Usage_i8(0x26) +#define Usage_AD_ScreenSaverDelay Usage_i8(0x27) +#define Usage_AD_ScreenSaverEnable Usage_i8(0x28) +#define Usage_AD_VerticalScroll Usage_i8(0x29) +#define Usage_AD_HorizontalScroll Usage_i8(0x2a) +#define Usage_AD_CharacterReport Usage_i8(0x2b) +#define Usage_AD_DisplayData Usage_i8(0x2c) +#define Usage_AD_DisplayStatus Usage_i8(0x2d) +#define Usage_AD_StatNotReady Usage_i8(0x2e) +#define Usage_AD_StatReady Usage_i8(0x2f) +#define Usage_AD_ErrNotaloadablecharacter Usage_i8(0x30) +#define Usage_AD_ErrFontdatacannotberead Usage_i8(0x31) +#define Usage_AD_CursorPositionReport Usage_i8(0x32) +#define Usage_AD_Row Usage_i8(0x33) +#define Usage_AD_Column Usage_i8(0x34) +#define Usage_AD_Rows Usage_i8(0x35) +#define Usage_AD_Columns Usage_i8(0x36) +#define Usage_AD_CursorPixelPositioning Usage_i8(0x37) +#define Usage_AD_CursorMode Usage_i8(0x38) +#define Usage_AD_CursorEnable Usage_i8(0x39) +#define Usage_AD_CursorBlink Usage_i8(0x3a) +#define Usage_AD_FontReport Usage_i8(0x3b) +#define Usage_AD_FontData Usage_i8(0x3c) +#define Usage_AD_CharacterWidth Usage_i8(0x3d) +#define Usage_AD_CharacterHeight Usage_i8(0x3e) +#define Usage_AD_CharacterSpacingHorizontal Usage_i8(0x3f) +#define Usage_AD_CharacterSpacingVertical Usage_i8(0x40) +#define Usage_AD_UnicodeCharacterSet Usage_i8(0x41) +#define Usage_AD_Font7Segment Usage_i8(0x42) +#define Usage_AD_SevenSegmentDirectMap Usage_i8(0x43) +#define Usage_AD_Font14Segment Usage_i8(0x44) +#define Usage_AD_One4SegmentDirectMap Usage_i8(0x45) +#define Usage_AD_DisplayBrightness Usage_i8(0x46) +#define Usage_AD_DisplayContrast Usage_i8(0x47) +#define Usage_AD_CharacterAttribute Usage_i8(0x48) +#define Usage_AD_AttributeReadback Usage_i8(0x49) +#define Usage_AD_AttributeData Usage_i8(0x4a) +#define Usage_AD_CharAttrEnhance Usage_i8(0x4b) +#define Usage_AD_CharAttrUnderline Usage_i8(0x4c) +#define Usage_AD_CharAttrBlink Usage_i8(0x4d) +#define Usage_AD_BitmapSizeX Usage_i8(0x80) +#define Usage_AD_BitmapSizeY Usage_i8(0x81) +#define Usage_AD_MaxBlitSize Usage_i8(0x82) +#define Usage_AD_BitDepthFormat Usage_i8(0x83) +#define Usage_AD_DisplayOrientation Usage_i8(0x84) +#define Usage_AD_PaletteReport Usage_i8(0x85) +#define Usage_AD_PaletteDataSize Usage_i8(0x86) +#define Usage_AD_PaletteDataOffset Usage_i8(0x87) +#define Usage_AD_PaletteData Usage_i8(0x88) +#define Usage_AD_BlitReport Usage_i8(0x8a) +#define Usage_AD_BlitRectangleX1 Usage_i8(0x8b) +#define Usage_AD_BlitRectangleY1 Usage_i8(0x8c) +#define Usage_AD_BlitRectangleX2 Usage_i8(0x8d) +#define Usage_AD_BlitRectangleY2 Usage_i8(0x8e) +#define Usage_AD_BlitData Usage_i8(0x8f) +#define Usage_AD_SoftButton Usage_i8(0x90) +#define Usage_AD_SoftButtonID Usage_i8(0x91) +#define Usage_AD_SoftButtonSide Usage_i8(0x92) +#define Usage_AD_SoftButtonOffset1 Usage_i8(0x93) +#define Usage_AD_SoftButtonOffset2 Usage_i8(0x94) +#define Usage_AD_SoftButtonReport Usage_i8(0x95) +#define Usage_AD_SoftKeys Usage_i8(0xc2) +#define Usage_AD_DisplayDataExtensions Usage_i8(0xcc) +#define Usage_AD_CharacterMapping Usage_i8(0xcf) +#define Usage_AD_UnicodeEquivalent Usage_i8(0xdd) +#define Usage_AD_CharacterPageMapping Usage_i8(0xdf) +#define Usage_AD_RequestReport Usage_i16(0xff) +#define Usage_Sen_Sensor Usage_i8(0x1) +#define Usage_Sen_Biometric Usage_i8(0x10) +#define Usage_Sen_BiometricHumanPresence Usage_i8(0x11) +#define Usage_Sen_BiometricHumanProximity Usage_i8(0x12) +#define Usage_Sen_BiometricHumanTouch Usage_i8(0x13) +#define Usage_Sen_BiometricBloodPressure Usage_i8(0x14) +#define Usage_Sen_BiometricBodyTemperature Usage_i8(0x15) +#define Usage_Sen_BiometricHeartRate Usage_i8(0x16) +#define Usage_Sen_BiometricHeartRateVariability Usage_i8(0x17) +#define Usage_Sen_BiometricPeripheralOxygenSaturation Usage_i8(0x18) +#define Usage_Sen_BiometricRespiratoryRate Usage_i8(0x19) +#define Usage_Sen_Electrical Usage_i8(0x20) +#define Usage_Sen_ElectricalCapacitance Usage_i8(0x21) +#define Usage_Sen_ElectricalCurrent Usage_i8(0x22) +#define Usage_Sen_ElectricalPower Usage_i8(0x23) +#define Usage_Sen_ElectricalInductance Usage_i8(0x24) +#define Usage_Sen_ElectricalResistance Usage_i8(0x25) +#define Usage_Sen_ElectricalVoltage Usage_i8(0x26) +#define Usage_Sen_ElectricalPotentiometer Usage_i8(0x27) +#define Usage_Sen_ElectricalFrequency Usage_i8(0x28) +#define Usage_Sen_ElectricalPeriod Usage_i8(0x29) +#define Usage_Sen_Environmental Usage_i8(0x30) +#define Usage_Sen_EnvironmentalAtmosphericPressure Usage_i8(0x31) +#define Usage_Sen_EnvironmentalHumidity Usage_i8(0x32) +#define Usage_Sen_EnvironmentalTemperature Usage_i8(0x33) +#define Usage_Sen_EnvironmentalWindDirection Usage_i8(0x34) +#define Usage_Sen_EnvironmentalWindSpeed Usage_i8(0x35) +#define Usage_Sen_EnvironmentalAirQuality Usage_i8(0x36) +#define Usage_Sen_EnvironmentalHeatIndex Usage_i8(0x37) +#define Usage_Sen_EnvironmentalSurfaceTemperature Usage_i8(0x38) +#define Usage_Sen_EnvironmentalVolatileOrganicCompounds Usage_i8(0x39) +#define Usage_Sen_EnvironmentalObjectPresence Usage_i8(0x3a) +#define Usage_Sen_EnvironmentalObjectProximity Usage_i8(0x3b) +#define Usage_Sen_Light Usage_i8(0x40) +#define Usage_Sen_LightAmbientLight Usage_i8(0x41) +#define Usage_Sen_LightConsumerInfrared Usage_i8(0x42) +#define Usage_Sen_LightInfraredLight Usage_i8(0x43) +#define Usage_Sen_LightVisibleLight Usage_i8(0x44) +#define Usage_Sen_LightUltravioletLight Usage_i8(0x45) +#define Usage_Sen_Location Usage_i8(0x50) +#define Usage_Sen_LocationBroadcast Usage_i8(0x51) +#define Usage_Sen_LocationDeadReckoning Usage_i8(0x52) +#define Usage_Sen_LocationGPSGlobalPositioningSystem Usage_i8(0x53) +#define Usage_Sen_LocationLookup Usage_i8(0x54) +#define Usage_Sen_LocationOther Usage_i8(0x55) +#define Usage_Sen_LocationStatic Usage_i8(0x56) +#define Usage_Sen_LocationTriangulation Usage_i8(0x57) +#define Usage_Sen_Mechanical Usage_i8(0x60) +#define Usage_Sen_MechanicalBooleanSwitch Usage_i8(0x61) +#define Usage_Sen_MechanicalBooleanSwitchArray Usage_i8(0x62) +#define Usage_Sen_MechanicalMultivalueSwitch Usage_i8(0x63) +#define Usage_Sen_MechanicalForce Usage_i8(0x64) +#define Usage_Sen_MechanicalPressure Usage_i8(0x65) +#define Usage_Sen_MechanicalStrain Usage_i8(0x66) +#define Usage_Sen_MechanicalWeight Usage_i8(0x67) +#define Usage_Sen_MechanicalHapticVibrator Usage_i8(0x68) +#define Usage_Sen_MechanicalHallEffectSwitch Usage_i8(0x69) +#define Usage_Sen_Motion Usage_i8(0x70) +#define Usage_Sen_MotionAccelerometer1D Usage_i8(0x71) +#define Usage_Sen_MotionAccelerometer2D Usage_i8(0x72) +#define Usage_Sen_MotionAccelerometer3D Usage_i8(0x73) +#define Usage_Sen_MotionGyrometer1D Usage_i8(0x74) +#define Usage_Sen_MotionGyrometer2D Usage_i8(0x75) +#define Usage_Sen_MotionGyrometer3D Usage_i8(0x76) +#define Usage_Sen_MotionMotionDetector Usage_i8(0x77) +#define Usage_Sen_MotionSpeedometer Usage_i8(0x78) +#define Usage_Sen_MotionAccelerometer Usage_i8(0x79) +#define Usage_Sen_MotionGyrometer Usage_i8(0x7a) +#define Usage_Sen_MotionGravityVector Usage_i8(0x7b) +#define Usage_Sen_MotionLinearAccelerometer Usage_i8(0x7c) +#define Usage_Sen_Orientation Usage_i8(0x80) +#define Usage_Sen_OrientationCompass1D Usage_i8(0x81) +#define Usage_Sen_OrientationCompass2D Usage_i8(0x82) +#define Usage_Sen_OrientationCompass3D Usage_i8(0x83) +#define Usage_Sen_OrientationInclinometer1D Usage_i8(0x84) +#define Usage_Sen_OrientationInclinometer2D Usage_i8(0x85) +#define Usage_Sen_OrientationInclinometer3D Usage_i8(0x86) +#define Usage_Sen_OrientationDistance1D Usage_i8(0x87) +#define Usage_Sen_OrientationDistance2D Usage_i8(0x88) +#define Usage_Sen_OrientationDistance3D Usage_i8(0x89) +#define Usage_Sen_OrientationDeviceOrientation Usage_i8(0x8a) +#define Usage_Sen_OrientationCompass Usage_i8(0x8b) +#define Usage_Sen_OrientationInclinometer Usage_i8(0x8c) +#define Usage_Sen_OrientationDistance Usage_i8(0x8d) +#define Usage_Sen_OrientationRelativeOrientation Usage_i8(0x8e) +#define Usage_Sen_OrientationSimpleOrientation Usage_i8(0x8f) +#define Usage_Sen_Scanner Usage_i8(0x90) +#define Usage_Sen_ScannerBarcode Usage_i8(0x91) +#define Usage_Sen_ScannerRFID Usage_i8(0x92) +#define Usage_Sen_ScannerNFC Usage_i8(0x93) +#define Usage_Sen_Time Usage_i8(0xa0) +#define Usage_Sen_TimeAlarmTimer Usage_i8(0xa1) +#define Usage_Sen_TimeRealTimeClock Usage_i8(0xa2) +#define Usage_Sen_PersonalActivity Usage_i8(0xb0) +#define Usage_Sen_PersonalActivityActivityDetection Usage_i8(0xb1) +#define Usage_Sen_PersonalActivityDevicePosition Usage_i8(0xb2) +#define Usage_Sen_PersonalActivityFloorTracker Usage_i8(0xb3) +#define Usage_Sen_PersonalActivityPedometer Usage_i8(0xb4) +#define Usage_Sen_PersonalActivityStepDetection Usage_i8(0xb5) +#define Usage_Sen_OrientationExtended Usage_i8(0xc0) +#define Usage_Sen_OrientationExtendedGeomagneticOrientation Usage_i8(0xc1) +#define Usage_Sen_OrientationExtendedMagnetometer Usage_i8(0xc2) +#define Usage_Sen_Gesture Usage_i8(0xd0) +#define Usage_Sen_GestureChassisFlipGesture Usage_i8(0xd1) +#define Usage_Sen_GestureHingeFoldGesture Usage_i8(0xd2) +#define Usage_Sen_Other Usage_i8(0xe0) +#define Usage_Sen_OtherCustom Usage_i8(0xe1) +#define Usage_Sen_OtherGeneric Usage_i8(0xe2) +#define Usage_Sen_OtherGenericEnumerator Usage_i8(0xe3) +#define Usage_Sen_OtherHingeAngle Usage_i8(0xe4) +#define Usage_Sen_VendorReserved1 Usage_i8(0xf0) +#define Usage_Sen_VendorReserved2 Usage_i8(0xf1) +#define Usage_Sen_VendorReserved3 Usage_i8(0xf2) +#define Usage_Sen_VendorReserved4 Usage_i8(0xf3) +#define Usage_Sen_VendorReserved5 Usage_i8(0xf4) +#define Usage_Sen_VendorReserved6 Usage_i8(0xf5) +#define Usage_Sen_VendorReserved7 Usage_i8(0xf6) +#define Usage_Sen_VendorReserved8 Usage_i8(0xf7) +#define Usage_Sen_VendorReserved9 Usage_i8(0xf8) +#define Usage_Sen_VendorReserved10 Usage_i8(0xf9) +#define Usage_Sen_VendorReserved11 Usage_i8(0xfa) +#define Usage_Sen_VendorReserved12 Usage_i8(0xfb) +#define Usage_Sen_VendorReserved13 Usage_i8(0xfc) +#define Usage_Sen_VendorReserved14 Usage_i8(0xfd) +#define Usage_Sen_VendorReserved15 Usage_i8(0xfe) +#define Usage_Sen_VendorReserved16 Usage_i16(0xff) +#define Usage_Sen_Event Usage_i16(0x200) +#define Usage_Sen_EventSensorState Usage_i16(0x201) +#define Usage_Sen_EventSensorEvent Usage_i16(0x202) +#define Usage_Sen_Property Usage_i16(0x300) +#define Usage_Sen_PropertyFriendlyName Usage_i16(0x301) +#define Usage_Sen_PropertyPersistentUniqueID Usage_i16(0x302) +#define Usage_Sen_PropertySensorStatus Usage_i16(0x303) +#define Usage_Sen_PropertyMinimumReportInterval Usage_i16(0x304) +#define Usage_Sen_PropertySensorManufacturer Usage_i16(0x305) +#define Usage_Sen_PropertySensorModel Usage_i16(0x306) +#define Usage_Sen_PropertySensorSerialNumber Usage_i16(0x307) +#define Usage_Sen_PropertySensorDescription Usage_i16(0x308) +#define Usage_Sen_PropertySensorConnectionType Usage_i16(0x309) +#define Usage_Sen_PropertySensorDevicePath Usage_i16(0x30a) +#define Usage_Sen_PropertyHardwareRevision Usage_i16(0x30b) +#define Usage_Sen_PropertyFirmwareVersion Usage_i16(0x30c) +#define Usage_Sen_PropertyReleaseDate Usage_i16(0x30d) +#define Usage_Sen_PropertyReportInterval Usage_i16(0x30e) +#define Usage_Sen_PropertyChangeSensitivityAbsolute Usage_i16(0x30f) +#define Usage_Sen_PropertyChangeSensitivityPercentofRange Usage_i16(0x310) +#define Usage_Sen_PropertyChangeSensitivityPercentRelative Usage_i16(0x311) +#define Usage_Sen_PropertyAccuracy Usage_i16(0x312) +#define Usage_Sen_PropertyResolution Usage_i16(0x313) +#define Usage_Sen_PropertyMaximum Usage_i16(0x314) +#define Usage_Sen_PropertyMinimum Usage_i16(0x315) +#define Usage_Sen_PropertyReportingState Usage_i16(0x316) +#define Usage_Sen_PropertySamplingRate Usage_i16(0x317) +#define Usage_Sen_PropertyResponseCurve Usage_i16(0x318) +#define Usage_Sen_PropertyPowerState Usage_i16(0x319) +#define Usage_Sen_PropertyMaximumFIFOEvents Usage_i16(0x31a) +#define Usage_Sen_PropertyReportLatency Usage_i16(0x31b) +#define Usage_Sen_PropertyFlushFIFOEvents Usage_i16(0x31c) +#define Usage_Sen_PropertyMaximumPowerConsumption Usage_i16(0x31d) +#define Usage_Sen_PropertyIsPrimary Usage_i16(0x31e) +#define Usage_Sen_PropertyHumanPresenceDetectionType Usage_i16(0x31f) +#define Usage_Sen_DataFieldLocation Usage_i16(0x400) +#define Usage_Sen_DataFieldAltitudeAntennaSeaLevel Usage_i16(0x402) +#define Usage_Sen_DataFieldDifferentialReferenceStationID Usage_i16(0x403) +#define Usage_Sen_DataFieldAltitudeEllipsoidError Usage_i16(0x404) +#define Usage_Sen_DataFieldAltitudeEllipsoid Usage_i16(0x405) +#define Usage_Sen_DataFieldAltitudeSeaLevelError Usage_i16(0x406) +#define Usage_Sen_DataFieldAltitudeSeaLevel Usage_i16(0x407) +#define Usage_Sen_DataFieldDifferentialGPSDataAge Usage_i16(0x408) +#define Usage_Sen_DataFieldErrorRadius Usage_i16(0x409) +#define Usage_Sen_DataFieldFixQuality Usage_i16(0x40a) +#define Usage_Sen_DataFieldFixType Usage_i16(0x40b) +#define Usage_Sen_DataFieldGeoidalSeparation Usage_i16(0x40c) +#define Usage_Sen_DataFieldGPSOperationMode Usage_i16(0x40d) +#define Usage_Sen_DataFieldGPSSelectionMode Usage_i16(0x40e) +#define Usage_Sen_DataFieldGPSStatus Usage_i16(0x40f) +#define Usage_Sen_DataFieldPositionDilutionofPrecision Usage_i16(0x410) +#define Usage_Sen_DataFieldHorizontalDilutionofPrecision Usage_i16(0x411) +#define Usage_Sen_DataFieldVerticalDilutionofPrecision Usage_i16(0x412) +#define Usage_Sen_DataFieldLatitude Usage_i16(0x413) +#define Usage_Sen_DataFieldLongitude Usage_i16(0x414) +#define Usage_Sen_DataFieldTrueHeading Usage_i16(0x415) +#define Usage_Sen_DataFieldMagneticHeading Usage_i16(0x416) +#define Usage_Sen_DataFieldMagneticVariation Usage_i16(0x417) +#define Usage_Sen_DataFieldSpeed Usage_i16(0x418) +#define Usage_Sen_DataFieldSatellitesinView Usage_i16(0x419) +#define Usage_Sen_DataFieldSatellitesinViewAzimuth Usage_i16(0x41a) +#define Usage_Sen_DataFieldSatellitesinViewElevation Usage_i16(0x41b) +#define Usage_Sen_DataFieldSatellitesinViewIDs Usage_i16(0x41c) +#define Usage_Sen_DataFieldSatellitesinViewPRNs Usage_i16(0x41d) +#define Usage_Sen_DataFieldSatellitesinViewSNRatios Usage_i16(0x41e) +#define Usage_Sen_DataFieldSatellitesUsedCount Usage_i16(0x41f) +#define Usage_Sen_DataFieldSatellitesUsedPRNs Usage_i16(0x420) +#define Usage_Sen_DataFieldNMEASentence Usage_i16(0x421) +#define Usage_Sen_DataFieldAddressLine1 Usage_i16(0x422) +#define Usage_Sen_DataFieldAddressLine2 Usage_i16(0x423) +#define Usage_Sen_DataFieldCity Usage_i16(0x424) +#define Usage_Sen_DataFieldStateorProvince Usage_i16(0x425) +#define Usage_Sen_DataFieldCountryorRegion Usage_i16(0x426) +#define Usage_Sen_DataFieldPostalCode Usage_i16(0x427) +#define Usage_Sen_PropertyLocation Usage_i16(0x42a) +#define Usage_Sen_PropertyLocationDesiredAccuracy Usage_i16(0x42b) +#define Usage_Sen_DataFieldEnvironmental Usage_i16(0x430) +#define Usage_Sen_DataFieldAtmosphericPressure Usage_i16(0x431) +#define Usage_Sen_DataFieldRelativeHumidity Usage_i16(0x433) +#define Usage_Sen_DataFieldTemperature Usage_i16(0x434) +#define Usage_Sen_DataFieldWindDirection Usage_i16(0x435) +#define Usage_Sen_DataFieldWindSpeed Usage_i16(0x436) +#define Usage_Sen_DataFieldAirQualityIndex Usage_i16(0x437) +#define Usage_Sen_DataFieldEquivalentCO2 Usage_i16(0x438) +#define Usage_Sen_DataFieldVolatileOrganicCompoundConcentration Usage_i16(0x439) +#define Usage_Sen_DataFieldObjectPresence Usage_i16(0x43a) +#define Usage_Sen_DataFieldObjectProximityRange Usage_i16(0x43b) +#define Usage_Sen_DataFieldObjectProximityOutofRange Usage_i16(0x43c) +#define Usage_Sen_PropertyEnvironmental Usage_i16(0x440) +#define Usage_Sen_PropertyReferencePressure Usage_i16(0x441) +#define Usage_Sen_DataFieldMotion Usage_i16(0x450) +#define Usage_Sen_DataFieldMotionState Usage_i16(0x451) +#define Usage_Sen_DataFieldAcceleration Usage_i16(0x452) +#define Usage_Sen_DataFieldAccelerationAxisX Usage_i16(0x453) +#define Usage_Sen_DataFieldAccelerationAxisY Usage_i16(0x454) +#define Usage_Sen_DataFieldAccelerationAxisZ Usage_i16(0x455) +#define Usage_Sen_DataFieldAngularVelocity Usage_i16(0x456) +#define Usage_Sen_DataFieldAngularVelocityaboutXAxis Usage_i16(0x457) +#define Usage_Sen_DataFieldAngularVelocityaboutYAxis Usage_i16(0x458) +#define Usage_Sen_DataFieldAngularVelocityaboutZAxis Usage_i16(0x459) +#define Usage_Sen_DataFieldAngularPosition Usage_i16(0x45a) +#define Usage_Sen_DataFieldAngularPositionaboutXAxis Usage_i16(0x45b) +#define Usage_Sen_DataFieldAngularPositionaboutYAxis Usage_i16(0x45c) +#define Usage_Sen_DataFieldAngularPositionaboutZAxis Usage_i16(0x45d) +#define Usage_Sen_DataFieldMotionSpeed Usage_i16(0x45e) +#define Usage_Sen_DataFieldMotionIntensity Usage_i16(0x45f) +#define Usage_Sen_DataFieldOrientation Usage_i16(0x470) +#define Usage_Sen_DataFieldHeading Usage_i16(0x471) +#define Usage_Sen_DataFieldHeadingXAxis Usage_i16(0x472) +#define Usage_Sen_DataFieldHeadingYAxis Usage_i16(0x473) +#define Usage_Sen_DataFieldHeadingZAxis Usage_i16(0x474) +#define Usage_Sen_DataFieldHeadingCompensatedMagneticNorth Usage_i16(0x475) +#define Usage_Sen_DataFieldHeadingCompensatedTrueNorth Usage_i16(0x476) +#define Usage_Sen_DataFieldHeadingMagneticNorth Usage_i16(0x477) +#define Usage_Sen_DataFieldHeadingTrueNorth Usage_i16(0x478) +#define Usage_Sen_DataFieldDistance Usage_i16(0x479) +#define Usage_Sen_DataFieldDistanceXAxis Usage_i16(0x47a) +#define Usage_Sen_DataFieldDistanceYAxis Usage_i16(0x47b) +#define Usage_Sen_DataFieldDistanceZAxis Usage_i16(0x47c) +#define Usage_Sen_DataFieldDistanceOutofRange Usage_i16(0x47d) +#define Usage_Sen_DataFieldTilt Usage_i16(0x47e) +#define Usage_Sen_DataFieldTiltXAxis Usage_i16(0x47f) +#define Usage_Sen_DataFieldTiltYAxis Usage_i16(0x480) +#define Usage_Sen_DataFieldTiltZAxis Usage_i16(0x481) +#define Usage_Sen_DataFieldRotationMatrix Usage_i16(0x482) +#define Usage_Sen_DataFieldQuaternion Usage_i16(0x483) +#define Usage_Sen_DataFieldMagneticFlux Usage_i16(0x484) +#define Usage_Sen_DataFieldMagneticFluxXAxis Usage_i16(0x485) +#define Usage_Sen_DataFieldMagneticFluxYAxis Usage_i16(0x486) +#define Usage_Sen_DataFieldMagneticFluxZAxis Usage_i16(0x487) +#define Usage_Sen_DataFieldMagnetometerAccuracy Usage_i16(0x488) +#define Usage_Sen_DataFieldSimpleOrientationDirection Usage_i16(0x489) +#define Usage_Sen_DataFieldMechanical Usage_i16(0x490) +#define Usage_Sen_DataFieldBooleanSwitchState Usage_i16(0x491) +#define Usage_Sen_DataFieldBooleanSwitchArrayStates Usage_i16(0x492) +#define Usage_Sen_DataFieldMultivalueSwitchValue Usage_i16(0x493) +#define Usage_Sen_DataFieldForce Usage_i16(0x494) +#define Usage_Sen_DataFieldAbsolutePressure Usage_i16(0x495) +#define Usage_Sen_DataFieldGaugePressure Usage_i16(0x496) +#define Usage_Sen_DataFieldStrain Usage_i16(0x497) +#define Usage_Sen_DataFieldWeight Usage_i16(0x498) +#define Usage_Sen_PropertyMechanical Usage_i16(0x4a0) +#define Usage_Sen_PropertyVibrationState Usage_i16(0x4a1) +#define Usage_Sen_PropertyForwardVibrationSpeed Usage_i16(0x4a2) +#define Usage_Sen_PropertyBackwardVibrationSpeed Usage_i16(0x4a3) +#define Usage_Sen_DataFieldBiometric Usage_i16(0x4b0) +#define Usage_Sen_DataFieldHumanPresence Usage_i16(0x4b1) +#define Usage_Sen_DataFieldHumanProximityRange Usage_i16(0x4b2) +#define Usage_Sen_DataFieldHumanProximityOutofRange Usage_i16(0x4b3) +#define Usage_Sen_DataFieldHumanTouchState Usage_i16(0x4b4) +#define Usage_Sen_DataFieldBloodPressure Usage_i16(0x4b5) +#define Usage_Sen_DataFieldBloodPressureDiastolic Usage_i16(0x4b6) +#define Usage_Sen_DataFieldBloodPressureSystolic Usage_i16(0x4b7) +#define Usage_Sen_DataFieldHeartRate Usage_i16(0x4b8) +#define Usage_Sen_DataFieldRestingHeartRate Usage_i16(0x4b9) +#define Usage_Sen_DataFieldHeartbeatInterval Usage_i16(0x4ba) +#define Usage_Sen_DataFieldRespiratoryRate Usage_i16(0x4bb) +#define Usage_Sen_DataFieldSpO2 Usage_i16(0x4bc) +#define Usage_Sen_DataFieldHumanAttentionDetected Usage_i16(0x4bd) +#define Usage_Sen_DataFieldHumanHeadAzimuth Usage_i16(0x4be) +#define Usage_Sen_DataFieldHumanHeadAltitude Usage_i16(0x4bf) +#define Usage_Sen_DataFieldHumanHeadRoll Usage_i16(0x4c0) +#define Usage_Sen_DataFieldHumanHeadPitch Usage_i16(0x4c1) +#define Usage_Sen_DataFieldHumanHeadYaw Usage_i16(0x4c2) +#define Usage_Sen_DataFieldHumanCorrelationId Usage_i16(0x4c3) +#define Usage_Sen_DataFieldLight Usage_i16(0x4d0) +#define Usage_Sen_DataFieldIlluminance Usage_i16(0x4d1) +#define Usage_Sen_DataFieldColorTemperature Usage_i16(0x4d2) +#define Usage_Sen_DataFieldChromaticity Usage_i16(0x4d3) +#define Usage_Sen_DataFieldChromaticityX Usage_i16(0x4d4) +#define Usage_Sen_DataFieldChromaticityY Usage_i16(0x4d5) +#define Usage_Sen_DataFieldConsumerIRSentenceReceive Usage_i16(0x4d6) +#define Usage_Sen_DataFieldInfraredLight Usage_i16(0x4d7) +#define Usage_Sen_DataFieldRedLight Usage_i16(0x4d8) +#define Usage_Sen_DataFieldGreenLight Usage_i16(0x4d9) +#define Usage_Sen_DataFieldBlueLight Usage_i16(0x4da) +#define Usage_Sen_DataFieldUltravioletALight Usage_i16(0x4db) +#define Usage_Sen_DataFieldUltravioletBLight Usage_i16(0x4dc) +#define Usage_Sen_DataFieldUltravioletIndex Usage_i16(0x4dd) +#define Usage_Sen_DataFieldNearInfraredLight Usage_i16(0x4de) +#define Usage_Sen_PropertyLight Usage_i16(0x4df) +#define Usage_Sen_PropertyConsumerIRSentenceSend Usage_i16(0x4e0) +#define Usage_Sen_PropertyAutoBrightnessPreferred Usage_i16(0x4e2) +#define Usage_Sen_PropertyAutoColorPreferred Usage_i16(0x4e3) +#define Usage_Sen_DataFieldScanner Usage_i16(0x4f0) +#define Usage_Sen_DataFieldRFIDTag40Bit Usage_i16(0x4f1) +#define Usage_Sen_DataFieldNFCSentenceReceive Usage_i16(0x4f2) +#define Usage_Sen_PropertyScanner Usage_i16(0x4f8) +#define Usage_Sen_PropertyNFCSentenceSend Usage_i16(0x4f9) +#define Usage_Sen_DataFieldElectrical Usage_i16(0x500) +#define Usage_Sen_DataFieldCapacitance Usage_i16(0x501) +#define Usage_Sen_DataFieldCurrent Usage_i16(0x502) +#define Usage_Sen_DataFieldElectricalPower Usage_i16(0x503) +#define Usage_Sen_DataFieldInductance Usage_i16(0x504) +#define Usage_Sen_DataFieldResistance Usage_i16(0x505) +#define Usage_Sen_DataFieldVoltage Usage_i16(0x506) +#define Usage_Sen_DataFieldFrequency Usage_i16(0x507) +#define Usage_Sen_DataFieldPeriod Usage_i16(0x508) +#define Usage_Sen_DataFieldPercentofRange Usage_i16(0x509) +#define Usage_Sen_DataFieldTime Usage_i16(0x520) +#define Usage_Sen_DataFieldYear Usage_i16(0x521) +#define Usage_Sen_DataFieldMonth Usage_i16(0x522) +#define Usage_Sen_DataFieldDay Usage_i16(0x523) +#define Usage_Sen_DataFieldDayofWeek Usage_i16(0x524) +#define Usage_Sen_DataFieldHour Usage_i16(0x525) +#define Usage_Sen_DataFieldMinute Usage_i16(0x526) +#define Usage_Sen_DataFieldSecond Usage_i16(0x527) +#define Usage_Sen_DataFieldMillisecond Usage_i16(0x528) +#define Usage_Sen_DataFieldTimestamp Usage_i16(0x529) +#define Usage_Sen_DataFieldJulianDayofYear Usage_i16(0x52a) +#define Usage_Sen_DataFieldTimeSinceSystemBoot Usage_i16(0x52b) +#define Usage_Sen_PropertyTime Usage_i16(0x530) +#define Usage_Sen_PropertyTimeZoneOffsetfromUTC Usage_i16(0x531) +#define Usage_Sen_PropertyTimeZoneName Usage_i16(0x532) +#define Usage_Sen_PropertyDaylightSavingsTimeObserved Usage_i16(0x533) +#define Usage_Sen_PropertyTimeTrimAdjustment Usage_i16(0x534) +#define Usage_Sen_PropertyArmAlarm Usage_i16(0x535) +#define Usage_Sen_DataFieldCustom Usage_i16(0x540) +#define Usage_Sen_DataFieldCustomUsage Usage_i16(0x541) +#define Usage_Sen_DataFieldCustomBooleanArray Usage_i16(0x542) +#define Usage_Sen_DataFieldCustomValue Usage_i16(0x543) +#define Usage_Sen_DataFieldCustomValue1 Usage_i16(0x544) +#define Usage_Sen_DataFieldCustomValue2 Usage_i16(0x545) +#define Usage_Sen_DataFieldCustomValue3 Usage_i16(0x546) +#define Usage_Sen_DataFieldCustomValue4 Usage_i16(0x547) +#define Usage_Sen_DataFieldCustomValue5 Usage_i16(0x548) +#define Usage_Sen_DataFieldCustomValue6 Usage_i16(0x549) +#define Usage_Sen_DataFieldCustomValue7 Usage_i16(0x54a) +#define Usage_Sen_DataFieldCustomValue8 Usage_i16(0x54b) +#define Usage_Sen_DataFieldCustomValue9 Usage_i16(0x54c) +#define Usage_Sen_DataFieldCustomValue10 Usage_i16(0x54d) +#define Usage_Sen_DataFieldCustomValue11 Usage_i16(0x54e) +#define Usage_Sen_DataFieldCustomValue12 Usage_i16(0x54f) +#define Usage_Sen_DataFieldCustomValue13 Usage_i16(0x550) +#define Usage_Sen_DataFieldCustomValue14 Usage_i16(0x551) +#define Usage_Sen_DataFieldCustomValue15 Usage_i16(0x552) +#define Usage_Sen_DataFieldCustomValue16 Usage_i16(0x553) +#define Usage_Sen_DataFieldCustomValue17 Usage_i16(0x554) +#define Usage_Sen_DataFieldCustomValue18 Usage_i16(0x555) +#define Usage_Sen_DataFieldCustomValue19 Usage_i16(0x556) +#define Usage_Sen_DataFieldCustomValue20 Usage_i16(0x557) +#define Usage_Sen_DataFieldCustomValue21 Usage_i16(0x558) +#define Usage_Sen_DataFieldCustomValue22 Usage_i16(0x559) +#define Usage_Sen_DataFieldCustomValue23 Usage_i16(0x55a) +#define Usage_Sen_DataFieldCustomValue24 Usage_i16(0x55b) +#define Usage_Sen_DataFieldCustomValue25 Usage_i16(0x55c) +#define Usage_Sen_DataFieldCustomValue26 Usage_i16(0x55d) +#define Usage_Sen_DataFieldCustomValue27 Usage_i16(0x55e) +#define Usage_Sen_DataFieldCustomValue28 Usage_i16(0x55f) +#define Usage_Sen_DataFieldGeneric Usage_i16(0x560) +#define Usage_Sen_DataFieldGenericGUIDorPROPERTYKEY Usage_i16(0x561) +#define Usage_Sen_DataFieldGenericCategoryGUID Usage_i16(0x562) +#define Usage_Sen_DataFieldGenericTypeGUID Usage_i16(0x563) +#define Usage_Sen_DataFieldGenericEventPROPERTYKEY Usage_i16(0x564) +#define Usage_Sen_DataFieldGenericPropertyPROPERTYKEY Usage_i16(0x565) +#define Usage_Sen_DataFieldGenericDataFieldPROPERTYKEY Usage_i16(0x566) +#define Usage_Sen_DataFieldGenericEvent Usage_i16(0x567) +#define Usage_Sen_DataFieldGenericProperty Usage_i16(0x568) +#define Usage_Sen_DataFieldGenericDataField Usage_i16(0x569) +#define Usage_Sen_DataFieldEnumeratorTableRowIndex Usage_i16(0x56a) +#define Usage_Sen_DataFieldEnumeratorTableRowCount Usage_i16(0x56b) +#define Usage_Sen_DataFieldGenericGUIDorPROPERTYKEYkind Usage_i16(0x56c) +#define Usage_Sen_DataFieldGenericGUID Usage_i16(0x56d) +#define Usage_Sen_DataFieldGenericPROPERTYKEY Usage_i16(0x56e) +#define Usage_Sen_DataFieldGenericTopLevelCollectionID Usage_i16(0x56f) +#define Usage_Sen_DataFieldGenericReportID Usage_i16(0x570) +#define Usage_Sen_DataFieldGenericReportItemPositionIndex Usage_i16(0x571) +#define Usage_Sen_DataFieldGenericFirmwareVARTYPE Usage_i16(0x572) +#define Usage_Sen_DataFieldGenericUnitofMeasure Usage_i16(0x573) +#define Usage_Sen_DataFieldGenericUnitExponent Usage_i16(0x574) +#define Usage_Sen_DataFieldGenericReportSize Usage_i16(0x575) +#define Usage_Sen_DataFieldGenericReportCount Usage_i16(0x576) +#define Usage_Sen_PropertyGeneric Usage_i16(0x580) +#define Usage_Sen_PropertyEnumeratorTableRowIndex Usage_i16(0x581) +#define Usage_Sen_PropertyEnumeratorTableRowCount Usage_i16(0x582) +#define Usage_Sen_DataFieldPersonalActivity Usage_i16(0x590) +#define Usage_Sen_DataFieldActivityType Usage_i16(0x591) +#define Usage_Sen_DataFieldActivityState Usage_i16(0x592) +#define Usage_Sen_DataFieldDevicePosition Usage_i16(0x593) +#define Usage_Sen_DataFieldStepCount Usage_i16(0x594) +#define Usage_Sen_DataFieldStepCountReset Usage_i16(0x595) +#define Usage_Sen_DataFieldStepDuration Usage_i16(0x596) +#define Usage_Sen_DataFieldStepType Usage_i16(0x597) +#define Usage_Sen_PropertyMinimumActivityDetectionInterval Usage_i16(0x5a0) +#define Usage_Sen_PropertySupportedActivityTypes Usage_i16(0x5a1) +#define Usage_Sen_PropertySubscribedActivityTypes Usage_i16(0x5a2) +#define Usage_Sen_PropertySupportedStepTypes Usage_i16(0x5a3) +#define Usage_Sen_PropertySubscribedStepTypes Usage_i16(0x5a4) +#define Usage_Sen_PropertyFloorHeight Usage_i16(0x5a5) +#define Usage_Sen_DataFieldCustomTypeID Usage_i16(0x5b0) +#define Usage_Sen_PropertyCustom Usage_i16(0x5c0) +#define Usage_Sen_PropertyCustomValue1 Usage_i16(0x5c1) +#define Usage_Sen_PropertyCustomValue2 Usage_i16(0x5c2) +#define Usage_Sen_PropertyCustomValue3 Usage_i16(0x5c3) +#define Usage_Sen_PropertyCustomValue4 Usage_i16(0x5c4) +#define Usage_Sen_PropertyCustomValue5 Usage_i16(0x5c5) +#define Usage_Sen_PropertyCustomValue6 Usage_i16(0x5c6) +#define Usage_Sen_PropertyCustomValue7 Usage_i16(0x5c7) +#define Usage_Sen_PropertyCustomValue8 Usage_i16(0x5c8) +#define Usage_Sen_PropertyCustomValue9 Usage_i16(0x5c9) +#define Usage_Sen_PropertyCustomValue10 Usage_i16(0x5ca) +#define Usage_Sen_PropertyCustomValue11 Usage_i16(0x5cb) +#define Usage_Sen_PropertyCustomValue12 Usage_i16(0x5cc) +#define Usage_Sen_PropertyCustomValue13 Usage_i16(0x5cd) +#define Usage_Sen_PropertyCustomValue14 Usage_i16(0x5ce) +#define Usage_Sen_PropertyCustomValue15 Usage_i16(0x5cf) +#define Usage_Sen_PropertyCustomValue16 Usage_i16(0x5d0) +#define Usage_Sen_DataFieldHinge Usage_i16(0x5e0) +#define Usage_Sen_DataFieldHingeAngle Usage_i16(0x5e1) +#define Usage_Sen_DataFieldGestureSensor Usage_i16(0x5f0) +#define Usage_Sen_DataFieldGestureState Usage_i16(0x5f1) +#define Usage_Sen_DataFieldHingeFoldInitialAngle Usage_i16(0x5f2) +#define Usage_Sen_DataFieldHingeFoldFinalAngle Usage_i16(0x5f3) +#define Usage_Sen_DataFieldHingeFoldContributingPanel Usage_i16(0x5f4) +#define Usage_Sen_DataFieldHingeFoldType Usage_i16(0x5f5) +#define Usage_Sen_SensorStateUndefined Usage_i16(0x800) +#define Usage_Sen_SensorStateReady Usage_i16(0x801) +#define Usage_Sen_SensorStateNotAvailable Usage_i16(0x802) +#define Usage_Sen_SensorStateNoData Usage_i16(0x803) +#define Usage_Sen_SensorStateInitializing Usage_i16(0x804) +#define Usage_Sen_SensorStateAccessDenied Usage_i16(0x805) +#define Usage_Sen_SensorStateError Usage_i16(0x806) +#define Usage_Sen_SensorEventUnknown Usage_i16(0x810) +#define Usage_Sen_SensorEventStateChanged Usage_i16(0x811) +#define Usage_Sen_SensorEventPropertyChanged Usage_i16(0x812) +#define Usage_Sen_SensorEventDataUpdated Usage_i16(0x813) +#define Usage_Sen_SensorEventPollResponse Usage_i16(0x814) +#define Usage_Sen_SensorEventChangeSensitivity Usage_i16(0x815) +#define Usage_Sen_SensorEventRangeMaximumReached Usage_i16(0x816) +#define Usage_Sen_SensorEventRangeMinimumReached Usage_i16(0x817) +#define Usage_Sen_SensorEventHighThresholdCrossUpward Usage_i16(0x818) +#define Usage_Sen_SensorEventHighThresholdCrossDownward Usage_i16(0x819) +#define Usage_Sen_SensorEventLowThresholdCrossUpward Usage_i16(0x81a) +#define Usage_Sen_SensorEventLowThresholdCrossDownward Usage_i16(0x81b) +#define Usage_Sen_SensorEventZeroThresholdCrossUpward Usage_i16(0x81c) +#define Usage_Sen_SensorEventZeroThresholdCrossDownward Usage_i16(0x81d) +#define Usage_Sen_SensorEventPeriodExceeded Usage_i16(0x81e) +#define Usage_Sen_SensorEventFrequencyExceeded Usage_i16(0x81f) +#define Usage_Sen_SensorEventComplexTrigger Usage_i16(0x820) +#define Usage_Sen_ConnectionTypePCIntegrated Usage_i16(0x830) +#define Usage_Sen_ConnectionTypePCAttached Usage_i16(0x831) +#define Usage_Sen_ConnectionTypePCExternal Usage_i16(0x832) +#define Usage_Sen_ReportingStateReportNoEvents Usage_i16(0x840) +#define Usage_Sen_ReportingStateReportAllEvents Usage_i16(0x841) +#define Usage_Sen_ReportingStateReportThresholdEvents Usage_i16(0x842) +#define Usage_Sen_ReportingStateWakeOnNoEvents Usage_i16(0x843) +#define Usage_Sen_ReportingStateWakeOnAllEvents Usage_i16(0x844) +#define Usage_Sen_ReportingStateWakeOnThresholdEvents Usage_i16(0x845) +#define Usage_Sen_ReportingStateAnytime Usage_i16(0x846) +#define Usage_Sen_PowerStateUndefined Usage_i16(0x850) +#define Usage_Sen_PowerStateD0FullPower Usage_i16(0x851) +#define Usage_Sen_PowerStateD1LowPower Usage_i16(0x852) +#define Usage_Sen_PowerStateD2StandbyPowerwithWakeup Usage_i16(0x853) +#define Usage_Sen_PowerStateD3SleepwithWakeup Usage_i16(0x854) +#define Usage_Sen_PowerStateD4PowerOff Usage_i16(0x855) +#define Usage_Sen_AccuracyDefault Usage_i16(0x860) +#define Usage_Sen_AccuracyHigh Usage_i16(0x861) +#define Usage_Sen_AccuracyMedium Usage_i16(0x862) +#define Usage_Sen_AccuracyLow Usage_i16(0x863) +#define Usage_Sen_FixQualityNoFix Usage_i16(0x870) +#define Usage_Sen_FixQualityGPS Usage_i16(0x871) +#define Usage_Sen_FixQualityDGPS Usage_i16(0x872) +#define Usage_Sen_FixTypeNoFix Usage_i16(0x880) +#define Usage_Sen_FixTypeGPSSPSModeFixValid Usage_i16(0x881) +#define Usage_Sen_FixTypeDGPSSPSModeFixValid Usage_i16(0x882) +#define Usage_Sen_FixTypeGPSPPSModeFixValid Usage_i16(0x883) +#define Usage_Sen_FixTypeRealTimeKinematic Usage_i16(0x884) +#define Usage_Sen_FixTypeFloatRTK Usage_i16(0x885) +#define Usage_Sen_FixTypeEstimateddeadreckoned Usage_i16(0x886) +#define Usage_Sen_FixTypeManualInputMode Usage_i16(0x887) +#define Usage_Sen_FixTypeSimulatorMode Usage_i16(0x888) +#define Usage_Sen_GPSOperationModeManual Usage_i16(0x890) +#define Usage_Sen_GPSOperationModeAutomatic Usage_i16(0x891) +#define Usage_Sen_GPSSelectionModeAutonomous Usage_i16(0x8a0) +#define Usage_Sen_GPSSelectionModeDGPS Usage_i16(0x8a1) +#define Usage_Sen_GPSSelectionModeEstimateddeadreckoned Usage_i16(0x8a2) +#define Usage_Sen_GPSSelectionModeManualInput Usage_i16(0x8a3) +#define Usage_Sen_GPSSelectionModeSimulator Usage_i16(0x8a4) +#define Usage_Sen_GPSSelectionModeDataNotValid Usage_i16(0x8a5) +#define Usage_Sen_GPSStatusDataValid Usage_i16(0x8b0) +#define Usage_Sen_GPSStatusDataNotValid Usage_i16(0x8b1) +#define Usage_Sen_DayofWeekSunday Usage_i16(0x8c0) +#define Usage_Sen_DayofWeekMonday Usage_i16(0x8c1) +#define Usage_Sen_DayofWeekTuesday Usage_i16(0x8c2) +#define Usage_Sen_DayofWeekWednesday Usage_i16(0x8c3) +#define Usage_Sen_DayofWeekThursday Usage_i16(0x8c4) +#define Usage_Sen_DayofWeekFriday Usage_i16(0x8c5) +#define Usage_Sen_DayofWeekSaturday Usage_i16(0x8c6) +#define Usage_Sen_KindCategory Usage_i16(0x8d0) +#define Usage_Sen_KindType Usage_i16(0x8d1) +#define Usage_Sen_KindEvent Usage_i16(0x8d2) +#define Usage_Sen_KindProperty Usage_i16(0x8d3) +#define Usage_Sen_KindDataField Usage_i16(0x8d4) +#define Usage_Sen_MagnetometerAccuracyLow Usage_i16(0x8e0) +#define Usage_Sen_MagnetometerAccuracyMedium Usage_i16(0x8e1) +#define Usage_Sen_MagnetometerAccuracyHigh Usage_i16(0x8e2) +#define Usage_Sen_SimpleOrientationDirectionNotRotated Usage_i16(0x8f0) +#define Usage_Sen_SimpleOrientationDirectionRotated90DegreesCCW Usage_i16(0x8f1) +#define Usage_Sen_SimpleOrientationDirectionRotated180DegreesCCW Usage_i16(0x8f2) +#define Usage_Sen_SimpleOrientationDirectionRotated270DegreesCCW Usage_i16(0x8f3) +#define Usage_Sen_SimpleOrientationDirectionFaceUp Usage_i16(0x8f4) +#define Usage_Sen_SimpleOrientationDirectionFaceDown Usage_i16(0x8f5) +#define Usage_Sen_VT_NULL Usage_i16(0x900) +#define Usage_Sen_VT_BOOL Usage_i16(0x901) +#define Usage_Sen_VT_UI1 Usage_i16(0x902) +#define Usage_Sen_VT_I1 Usage_i16(0x903) +#define Usage_Sen_VT_UI2 Usage_i16(0x904) +#define Usage_Sen_VT_I2 Usage_i16(0x905) +#define Usage_Sen_VT_UI4 Usage_i16(0x906) +#define Usage_Sen_VT_I4 Usage_i16(0x907) +#define Usage_Sen_VT_UI8 Usage_i16(0x908) +#define Usage_Sen_VT_I8 Usage_i16(0x909) +#define Usage_Sen_VT_R4 Usage_i16(0x90a) +#define Usage_Sen_VT_R8 Usage_i16(0x90b) +#define Usage_Sen_VT_WSTR Usage_i16(0x90c) +#define Usage_Sen_VT_STR Usage_i16(0x90d) +#define Usage_Sen_VT_CLSID Usage_i16(0x90e) +#define Usage_Sen_VT_VECTORVT_UI1 Usage_i16(0x90f) +#define Usage_Sen_VT_F16E0 Usage_i16(0x910) +#define Usage_Sen_VT_F16E1 Usage_i16(0x911) +#define Usage_Sen_VT_F16E2 Usage_i16(0x912) +#define Usage_Sen_VT_F16E3 Usage_i16(0x913) +#define Usage_Sen_VT_F16E4 Usage_i16(0x914) +#define Usage_Sen_VT_F16E5 Usage_i16(0x915) +#define Usage_Sen_VT_F16E6 Usage_i16(0x916) +#define Usage_Sen_VT_F16E7 Usage_i16(0x917) +#define Usage_Sen_VT_F16E8 Usage_i16(0x918) +#define Usage_Sen_VT_F16E9 Usage_i16(0x919) +#define Usage_Sen_VT_F16EA Usage_i16(0x91a) +#define Usage_Sen_VT_F16EB Usage_i16(0x91b) +#define Usage_Sen_VT_F16EC Usage_i16(0x91c) +#define Usage_Sen_VT_F16ED Usage_i16(0x91d) +#define Usage_Sen_VT_F16EE Usage_i16(0x91e) +#define Usage_Sen_VT_F16EF Usage_i16(0x91f) +#define Usage_Sen_VT_F32E0 Usage_i16(0x920) +#define Usage_Sen_VT_F32E1 Usage_i16(0x921) +#define Usage_Sen_VT_F32E2 Usage_i16(0x922) +#define Usage_Sen_VT_F32E3 Usage_i16(0x923) +#define Usage_Sen_VT_F32E4 Usage_i16(0x924) +#define Usage_Sen_VT_F32E5 Usage_i16(0x925) +#define Usage_Sen_VT_F32E6 Usage_i16(0x926) +#define Usage_Sen_VT_F32E7 Usage_i16(0x927) +#define Usage_Sen_VT_F32E8 Usage_i16(0x928) +#define Usage_Sen_VT_F32E9 Usage_i16(0x929) +#define Usage_Sen_VT_F32EA Usage_i16(0x92a) +#define Usage_Sen_VT_F32EB Usage_i16(0x92b) +#define Usage_Sen_VT_F32EC Usage_i16(0x92c) +#define Usage_Sen_VT_F32ED Usage_i16(0x92d) +#define Usage_Sen_VT_F32EE Usage_i16(0x92e) +#define Usage_Sen_VT_F32EF Usage_i16(0x92f) +#define Usage_Sen_ActivityTypeUnknown Usage_i16(0x930) +#define Usage_Sen_ActivityTypeStationary Usage_i16(0x931) +#define Usage_Sen_ActivityTypeFidgeting Usage_i16(0x932) +#define Usage_Sen_ActivityTypeWalking Usage_i16(0x933) +#define Usage_Sen_ActivityTypeRunning Usage_i16(0x934) +#define Usage_Sen_ActivityTypeInVehicle Usage_i16(0x935) +#define Usage_Sen_ActivityTypeBiking Usage_i16(0x936) +#define Usage_Sen_ActivityTypeIdle Usage_i16(0x937) +#define Usage_Sen_UnitNotSpecified Usage_i16(0x940) +#define Usage_Sen_UnitLux Usage_i16(0x941) +#define Usage_Sen_UnitDegreesKelvin Usage_i16(0x942) +#define Usage_Sen_UnitDegreesCelsius Usage_i16(0x943) +#define Usage_Sen_UnitPascal Usage_i16(0x944) +#define Usage_Sen_UnitNewton Usage_i16(0x945) +#define Usage_Sen_UnitMetersSecond Usage_i16(0x946) +#define Usage_Sen_UnitKilogram Usage_i16(0x947) +#define Usage_Sen_UnitMeter Usage_i16(0x948) +#define Usage_Sen_UnitMetersSecondSecond Usage_i16(0x949) +#define Usage_Sen_UnitFarad Usage_i16(0x94a) +#define Usage_Sen_UnitAmpere Usage_i16(0x94b) +#define Usage_Sen_UnitWatt Usage_i16(0x94c) +#define Usage_Sen_UnitHenry Usage_i16(0x94d) +#define Usage_Sen_UnitOhm Usage_i16(0x94e) +#define Usage_Sen_UnitVolt Usage_i16(0x94f) +#define Usage_Sen_UnitHertz Usage_i16(0x950) +#define Usage_Sen_UnitBar Usage_i16(0x951) +#define Usage_Sen_UnitDegreesAnticlockwise Usage_i16(0x952) +#define Usage_Sen_UnitDegreesClockwise Usage_i16(0x953) +#define Usage_Sen_UnitDegrees Usage_i16(0x954) +#define Usage_Sen_UnitDegreesSecond Usage_i16(0x955) +#define Usage_Sen_UnitDegreesSecondSecond Usage_i16(0x956) +#define Usage_Sen_UnitKnot Usage_i16(0x957) +#define Usage_Sen_UnitPercent Usage_i16(0x958) +#define Usage_Sen_UnitSecond Usage_i16(0x959) +#define Usage_Sen_UnitMillisecond Usage_i16(0x95a) +#define Usage_Sen_UnitG Usage_i16(0x95b) +#define Usage_Sen_UnitBytes Usage_i16(0x95c) +#define Usage_Sen_UnitMilligauss Usage_i16(0x95d) +#define Usage_Sen_UnitBits Usage_i16(0x95e) +#define Usage_Sen_ActivityStateNoStateChange Usage_i16(0x960) +#define Usage_Sen_ActivityStateStartActivity Usage_i16(0x961) +#define Usage_Sen_ActivityStateEndActivity Usage_i16(0x962) +#define Usage_Sen_Exponent0 Usage_i16(0x970) +#define Usage_Sen_Exponent1 Usage_i16(0x971) +#define Usage_Sen_Exponent2 Usage_i16(0x972) +#define Usage_Sen_Exponent3 Usage_i16(0x973) +#define Usage_Sen_Exponent4 Usage_i16(0x974) +#define Usage_Sen_Exponent5 Usage_i16(0x975) +#define Usage_Sen_Exponent6 Usage_i16(0x976) +#define Usage_Sen_Exponent7 Usage_i16(0x977) +#define Usage_Sen_Exponent8 Usage_i16(0x978) +#define Usage_Sen_Exponent9 Usage_i16(0x979) +#define Usage_Sen_ExponentA Usage_i16(0x97a) +#define Usage_Sen_ExponentB Usage_i16(0x97b) +#define Usage_Sen_ExponentC Usage_i16(0x97c) +#define Usage_Sen_ExponentD Usage_i16(0x97d) +#define Usage_Sen_ExponentE Usage_i16(0x97e) +#define Usage_Sen_ExponentF Usage_i16(0x97f) +#define Usage_Sen_DevicePositionUnknown Usage_i16(0x980) +#define Usage_Sen_DevicePositionUnchanged Usage_i16(0x981) +#define Usage_Sen_DevicePositionOnDesk Usage_i16(0x982) +#define Usage_Sen_DevicePositionInHand Usage_i16(0x983) +#define Usage_Sen_DevicePositionMovinginBag Usage_i16(0x984) +#define Usage_Sen_DevicePositionStationaryinBag Usage_i16(0x985) +#define Usage_Sen_StepTypeUnknown Usage_i16(0x990) +#define Usage_Sen_StepTypeWalking Usage_i16(0x991) +#define Usage_Sen_StepTypeRunning Usage_i16(0x992) +#define Usage_Sen_GestureStateUnknown Usage_i16(0x9a0) +#define Usage_Sen_GestureStateStarted Usage_i16(0x9a1) +#define Usage_Sen_GestureStateCompleted Usage_i16(0x9a2) +#define Usage_Sen_GestureStateCancelled Usage_i16(0x9a3) +#define Usage_Sen_HingeFoldContributingPanelUnknown Usage_i16(0x9b0) +#define Usage_Sen_HingeFoldContributingPanelPanel1 Usage_i16(0x9b1) +#define Usage_Sen_HingeFoldContributingPanelPanel2 Usage_i16(0x9b2) +#define Usage_Sen_HingeFoldContributingPanelBoth Usage_i16(0x9b3) +#define Usage_Sen_HingeFoldTypeUnknown Usage_i16(0x9b4) +#define Usage_Sen_HingeFoldTypeIncreasing Usage_i16(0x9b5) +#define Usage_Sen_HingeFoldTypeDecreasing Usage_i16(0x9b6) +#define Usage_Sen_HumanPresenceDetectionTypeVendorDefinedNonBiometric Usage_i16(0x9c0) +#define Usage_Sen_HumanPresenceDetectionTypeVendorDefinedBiometric Usage_i16(0x9c1) +#define Usage_Sen_HumanPresenceDetectionTypeFacialBiometric Usage_i16(0x9c2) +#define Usage_Sen_HumanPresenceDetectionTypeAudioBiometric Usage_i16(0x9c3) +#define Usage_Sen_ModifierChangeSensitivityAbsolute Usage_i16(0x1000) +#define Usage_Sen_ModifierMaximum Usage_i16(0x2000) +#define Usage_Sen_ModifierMinimum Usage_i16(0x3000) +#define Usage_Sen_ModifierAccuracy Usage_i16(0x4000) +#define Usage_Sen_ModifierResolution Usage_i16(0x5000) +#define Usage_Sen_ModifierThresholdHigh Usage_i16(0x6000) +#define Usage_Sen_ModifierThresholdLow Usage_i16(0x7000) +#define Usage_Sen_ModifierCalibrationOffset Usage_i16(0x8000) +#define Usage_Sen_ModifierCalibrationMultiplier Usage_i16(0x9000) +#define Usage_Sen_ModifierReportInterval Usage_i16(0xa000) +#define Usage_Sen_ModifierFrequencyMax Usage_i16(0xb000) +#define Usage_Sen_ModifierPeriodMax Usage_i16(0xc000) +#define Usage_Sen_ModifierChangeSensitivityPercentofRange Usage_i16(0xd000) +#define Usage_Sen_ModifierChangeSensitivityPercentRelative Usage_i16(0xe000) +#define Usage_Sen_ModifierVendorReserved Usage_i16(0xf000) +#define Usage_MI_MedicalUltrasound Usage_i8(0x1) +#define Usage_MI_VCRAcquisition Usage_i8(0x20) +#define Usage_MI_FreezeThaw Usage_i8(0x21) +#define Usage_MI_ClipStore Usage_i8(0x22) +#define Usage_MI_Update Usage_i8(0x23) +#define Usage_MI_Next Usage_i8(0x24) +#define Usage_MI_Save Usage_i8(0x25) +#define Usage_MI_Print Usage_i8(0x26) +#define Usage_MI_MicrophoneEnable Usage_i8(0x27) +#define Usage_MI_Cine Usage_i8(0x40) +#define Usage_MI_TransmitPower Usage_i8(0x41) +#define Usage_MI_Volume Usage_i8(0x42) +#define Usage_MI_Focus Usage_i8(0x43) +#define Usage_MI_Depth Usage_i8(0x44) +#define Usage_MI_SoftStepPrimary Usage_i8(0x60) +#define Usage_MI_SoftStepSecondary Usage_i8(0x61) +#define Usage_MI_DepthGainCompensation Usage_i8(0x70) +#define Usage_MI_ZoomSelect Usage_i8(0x80) +#define Usage_MI_ZoomAdjust Usage_i8(0x81) +#define Usage_MI_SpectralDopplerModeSelect Usage_i8(0x82) +#define Usage_MI_SpectralDopplerAdjust Usage_i8(0x83) +#define Usage_MI_ColorDopplerModeSelect Usage_i8(0x84) +#define Usage_MI_ColorDopplerAdjust Usage_i8(0x85) +#define Usage_MI_MotionModeSelect Usage_i8(0x86) +#define Usage_MI_MotionModeAdjust Usage_i8(0x87) +#define Usage_MI_TwoDModeSelect Usage_i8(0x88) +#define Usage_MI_TwoDModeAdjust Usage_i8(0x89) +#define Usage_MI_SoftControlSelect Usage_i8(0xa0) +#define Usage_MI_SoftControlAdjust Usage_i8(0xa1) +#define Usage_BD_BrailleDisplay Usage_i8(0x1) +#define Usage_BD_BrailleRow Usage_i8(0x2) +#define Usage_BD_EightDotBrailleCell Usage_i8(0x3) +#define Usage_BD_SixDotBrailleCell Usage_i8(0x4) +#define Usage_BD_NumberofBrailleCells Usage_i8(0x5) +#define Usage_BD_ScreenReaderControl Usage_i8(0x6) +#define Usage_BD_ScreenReaderIdentifier Usage_i8(0x7) +#define Usage_BD_RouterSet1 Usage_i8(0xfa) +#define Usage_BD_RouterSet2 Usage_i8(0xfb) +#define Usage_BD_RouterSet3 Usage_i8(0xfc) +#define Usage_BD_RouterKey Usage_i16(0x100) +#define Usage_BD_RowRouterKey Usage_i16(0x101) +#define Usage_BD_BrailleButtons Usage_i16(0x200) +#define Usage_BD_BrailleKeyboardDot1 Usage_i16(0x201) +#define Usage_BD_BrailleKeyboardDot2 Usage_i16(0x202) +#define Usage_BD_BrailleKeyboardDot3 Usage_i16(0x203) +#define Usage_BD_BrailleKeyboardDot4 Usage_i16(0x204) +#define Usage_BD_BrailleKeyboardDot5 Usage_i16(0x205) +#define Usage_BD_BrailleKeyboardDot6 Usage_i16(0x206) +#define Usage_BD_BrailleKeyboardDot7 Usage_i16(0x207) +#define Usage_BD_BrailleKeyboardDot8 Usage_i16(0x208) +#define Usage_BD_BrailleKeyboardSpace Usage_i16(0x209) +#define Usage_BD_BrailleKeyboardLeftSpace Usage_i16(0x20a) +#define Usage_BD_BrailleKeyboardRightSpace Usage_i16(0x20b) +#define Usage_BD_BrailleFaceControls Usage_i16(0x20c) +#define Usage_BD_BrailleLeftControls Usage_i16(0x20d) +#define Usage_BD_BrailleRightControls Usage_i16(0x20e) +#define Usage_BD_BrailleTopControls Usage_i16(0x20f) +#define Usage_BD_BrailleJoystickCenter Usage_i16(0x210) +#define Usage_BD_BrailleJoystickUp Usage_i16(0x211) +#define Usage_BD_BrailleJoystickDown Usage_i16(0x212) +#define Usage_BD_BrailleJoystickLeft Usage_i16(0x213) +#define Usage_BD_BrailleJoystickRight Usage_i16(0x214) +#define Usage_BD_BrailleDPadCenter Usage_i16(0x215) +#define Usage_BD_BrailleDPadUp Usage_i16(0x216) +#define Usage_BD_BrailleDPadDown Usage_i16(0x217) +#define Usage_BD_BrailleDPadLeft Usage_i16(0x218) +#define Usage_BD_BrailleDPadRight Usage_i16(0x219) +#define Usage_BD_BraillePanLeft Usage_i16(0x21a) +#define Usage_BD_BraillePanRight Usage_i16(0x21b) +#define Usage_BD_BrailleRockerUp Usage_i16(0x21c) +#define Usage_BD_BrailleRockerDown Usage_i16(0x21d) +#define Usage_BD_BrailleRockerPress Usage_i16(0x21e) +#define Usage_LAI_LampArray Usage_i8(0x1) +#define Usage_LAI_LampArrayAttributesReport Usage_i8(0x2) +#define Usage_LAI_LampCount Usage_i8(0x3) +#define Usage_LAI_BoundingBoxWidthInMicrometers Usage_i8(0x4) +#define Usage_LAI_BoundingBoxHeightInMicrometers Usage_i8(0x5) +#define Usage_LAI_BoundingBoxDepthInMicrometers Usage_i8(0x6) +#define Usage_LAI_LampArrayKind Usage_i8(0x7) +#define Usage_LAI_MinUpdateIntervalInMicroseconds Usage_i8(0x8) +#define Usage_LAI_LampAttributesRequestReport Usage_i8(0x20) +#define Usage_LAI_LampId Usage_i8(0x21) +#define Usage_LAI_LampAttributesResponseReport Usage_i8(0x22) +#define Usage_LAI_PositionXInMicrometers Usage_i8(0x23) +#define Usage_LAI_PositionYInMicrometers Usage_i8(0x24) +#define Usage_LAI_PositionZInMicrometers Usage_i8(0x25) +#define Usage_LAI_LampPurposes Usage_i8(0x26) +#define Usage_LAI_UpdateLatencyInMicroseconds Usage_i8(0x27) +#define Usage_LAI_RedLevelCount Usage_i8(0x28) +#define Usage_LAI_GreenLevelCount Usage_i8(0x29) +#define Usage_LAI_BlueLevelCount Usage_i8(0x2a) +#define Usage_LAI_IntensityLevelCount Usage_i8(0x2b) +#define Usage_LAI_IsProgrammable Usage_i8(0x2c) +#define Usage_LAI_InputBinding Usage_i8(0x2d) +#define Usage_LAI_LampMultiUpdateReport Usage_i8(0x50) +#define Usage_LAI_RedUpdateChannel Usage_i8(0x51) +#define Usage_LAI_GreenUpdateChannel Usage_i8(0x52) +#define Usage_LAI_BlueUpdateChannel Usage_i8(0x53) +#define Usage_LAI_IntensityUpdateChannel Usage_i8(0x54) +#define Usage_LAI_LampUpdateFlags Usage_i8(0x55) +#define Usage_LAI_LampRangeUpdateReport Usage_i8(0x60) +#define Usage_LAI_LampIdStart Usage_i8(0x61) +#define Usage_LAI_LampIdEnd Usage_i8(0x62) +#define Usage_LAI_LampArrayControlReport Usage_i8(0x70) +#define Usage_LAI_AutonomousMode Usage_i8(0x71) +#define Usage_Mon_MonitorControl Usage_i8(0x1) +#define Usage_Mon_EDIDInformation Usage_i8(0x2) +#define Usage_Mon_VDIFInformation Usage_i8(0x3) +#define Usage_Mon_VESAVersion Usage_i8(0x4) +#define Usage_VESAVC_Degauss Usage_i8(0x1) +#define Usage_VESAVC_Brightness Usage_i8(0x10) +#define Usage_VESAVC_Contrast Usage_i8(0x12) +#define Usage_VESAVC_RedVideoGain Usage_i8(0x16) +#define Usage_VESAVC_GreenVideoGain Usage_i8(0x18) +#define Usage_VESAVC_BlueVideoGain Usage_i8(0x1a) +#define Usage_VESAVC_Focus Usage_i8(0x1c) +#define Usage_VESAVC_HorizontalPosition Usage_i8(0x20) +#define Usage_VESAVC_HorizontalSize Usage_i8(0x22) +#define Usage_VESAVC_HorizontalPincushion Usage_i8(0x24) +#define Usage_VESAVC_HorizontalPincushionBalance Usage_i8(0x26) +#define Usage_VESAVC_HorizontalMisconvergence Usage_i8(0x28) +#define Usage_VESAVC_HorizontalLinearity Usage_i8(0x2a) +#define Usage_VESAVC_HorizontalLinearityBalance Usage_i8(0x2c) +#define Usage_VESAVC_VerticalPosition Usage_i8(0x30) +#define Usage_VESAVC_VerticalSize Usage_i8(0x32) +#define Usage_VESAVC_VerticalPincushion Usage_i8(0x34) +#define Usage_VESAVC_VerticalPincushionBalance Usage_i8(0x36) +#define Usage_VESAVC_VerticalMisconvergence Usage_i8(0x38) +#define Usage_VESAVC_VerticalLinearity Usage_i8(0x3a) +#define Usage_VESAVC_VerticalLinearityBalance Usage_i8(0x3c) +#define Usage_VESAVC_ParallelogramDistortionKeyBalance Usage_i8(0x40) +#define Usage_VESAVC_TrapezoidalDistortionKey Usage_i8(0x42) +#define Usage_VESAVC_TiltRotation Usage_i8(0x44) +#define Usage_VESAVC_TopCornerDistortionControl Usage_i8(0x46) +#define Usage_VESAVC_TopCornerDistortionBalance Usage_i8(0x48) +#define Usage_VESAVC_BottomCornerDistortionControl Usage_i8(0x4a) +#define Usage_VESAVC_BottomCornerDistortionBalance Usage_i8(0x4c) +#define Usage_VESAVC_HorizontalMoiré Usage_i8(0x56) +#define Usage_VESAVC_VerticalMoiré Usage_i8(0x58) +#define Usage_VESAVC_InputLevelSelect Usage_i8(0x5e) +#define Usage_VESAVC_InputSourceSelect Usage_i8(0x60) +#define Usage_VESAVC_RedVideoBlackLevel Usage_i8(0x6c) +#define Usage_VESAVC_GreenVideoBlackLevel Usage_i8(0x6e) +#define Usage_VESAVC_BlueVideoBlackLevel Usage_i8(0x70) +#define Usage_VESAVC_AutoSizeCenter Usage_i8(0xa2) +#define Usage_VESAVC_PolarityHorizontalSynchronization Usage_i8(0xa4) +#define Usage_VESAVC_PolarityVerticalSynchronization Usage_i8(0xa6) +#define Usage_VESAVC_SynchronizationType Usage_i8(0xa8) +#define Usage_VESAVC_ScreenOrientation Usage_i8(0xaa) +#define Usage_VESAVC_HorizontalFrequency Usage_i8(0xac) +#define Usage_VESAVC_VerticalFrequency Usage_i8(0xae) +#define Usage_VESAVC_Settings Usage_i8(0xb0) +#define Usage_VESAVC_OnScreenDisplay Usage_i8(0xca) +#define Usage_VESAVC_StereoMode Usage_i8(0xd4) +#define Usage_Pow_iName Usage_i8(0x1) +#define Usage_Pow_PresentStatus Usage_i8(0x2) +#define Usage_Pow_ChangedStatus Usage_i8(0x3) +#define Usage_Pow_UPS Usage_i8(0x4) +#define Usage_Pow_PowerSupply Usage_i8(0x5) +#define Usage_Pow_BatterySystem Usage_i8(0x10) +#define Usage_Pow_BatterySystemId Usage_i8(0x11) +#define Usage_Pow_Battery Usage_i8(0x12) +#define Usage_Pow_BatteryId Usage_i8(0x13) +#define Usage_Pow_Charger Usage_i8(0x14) +#define Usage_Pow_ChargerId Usage_i8(0x15) +#define Usage_Pow_PowerConverter Usage_i8(0x16) +#define Usage_Pow_PowerConverterId Usage_i8(0x17) +#define Usage_Pow_OutletSystem Usage_i8(0x18) +#define Usage_Pow_OutletSystemId Usage_i8(0x19) +#define Usage_Pow_Input Usage_i8(0x1a) +#define Usage_Pow_InputId Usage_i8(0x1b) +#define Usage_Pow_Output Usage_i8(0x1c) +#define Usage_Pow_OutputId Usage_i8(0x1d) +#define Usage_Pow_Flow Usage_i8(0x1e) +#define Usage_Pow_FlowId Usage_i8(0x1f) +#define Usage_Pow_Outlet Usage_i8(0x20) +#define Usage_Pow_OutletId Usage_i8(0x21) +#define Usage_Pow_Gang Usage_i8(0x22) +#define Usage_Pow_GangId Usage_i8(0x23) +#define Usage_Pow_PowerSummary Usage_i8(0x24) +#define Usage_Pow_PowerSummaryId Usage_i8(0x25) +#define Usage_Pow_Voltage Usage_i8(0x30) +#define Usage_Pow_Current Usage_i8(0x31) +#define Usage_Pow_Frequency Usage_i8(0x32) +#define Usage_Pow_ApparentPower Usage_i8(0x33) +#define Usage_Pow_ActivePower Usage_i8(0x34) +#define Usage_Pow_PercentLoad Usage_i8(0x35) +#define Usage_Pow_Temperature Usage_i8(0x36) +#define Usage_Pow_Humidity Usage_i8(0x37) +#define Usage_Pow_BadCount Usage_i8(0x38) +#define Usage_Pow_ConfigVoltage Usage_i8(0x40) +#define Usage_Pow_ConfigCurrent Usage_i8(0x41) +#define Usage_Pow_ConfigFrequency Usage_i8(0x42) +#define Usage_Pow_ConfigApparentPower Usage_i8(0x43) +#define Usage_Pow_ConfigActivePower Usage_i8(0x44) +#define Usage_Pow_ConfigPercentLoad Usage_i8(0x45) +#define Usage_Pow_ConfigTemperature Usage_i8(0x46) +#define Usage_Pow_ConfigHumidity Usage_i8(0x47) +#define Usage_Pow_SwitchOnControl Usage_i8(0x50) +#define Usage_Pow_SwitchOffControl Usage_i8(0x51) +#define Usage_Pow_ToggleControl Usage_i8(0x52) +#define Usage_Pow_LowVoltageTransfer Usage_i8(0x53) +#define Usage_Pow_HighVoltageTransfer Usage_i8(0x54) +#define Usage_Pow_DelayBeforeReboot Usage_i8(0x55) +#define Usage_Pow_DelayBeforeStartup Usage_i8(0x56) +#define Usage_Pow_DelayBeforeShutdown Usage_i8(0x57) +#define Usage_Pow_Test Usage_i8(0x58) +#define Usage_Pow_ModuleReset Usage_i8(0x59) +#define Usage_Pow_AudibleAlarmControl Usage_i8(0x5a) +#define Usage_Pow_Present Usage_i8(0x60) +#define Usage_Pow_Good Usage_i8(0x61) +#define Usage_Pow_InternalFailure Usage_i8(0x62) +#define Usage_Pow_VoltagOutOfRange Usage_i8(0x63) +#define Usage_Pow_FrequencyOutOfRange Usage_i8(0x64) +#define Usage_Pow_Overload Usage_i8(0x65) +#define Usage_Pow_OverCharged Usage_i8(0x66) +#define Usage_Pow_OverTemperature Usage_i8(0x67) +#define Usage_Pow_ShutdownRequested Usage_i8(0x68) +#define Usage_Pow_ShutdownImminent Usage_i8(0x69) +#define Usage_Pow_SwitchOnOff Usage_i8(0x6b) +#define Usage_Pow_Switchable Usage_i8(0x6c) +#define Usage_Pow_Used Usage_i8(0x6d) +#define Usage_Pow_Boost Usage_i8(0x6e) +#define Usage_Pow_Buck Usage_i8(0x6f) +#define Usage_Pow_Initialized Usage_i8(0x70) +#define Usage_Pow_Tested Usage_i8(0x71) +#define Usage_Pow_AwaitingPower Usage_i8(0x72) +#define Usage_Pow_CommunicationLost Usage_i8(0x73) +#define Usage_Pow_iManufacturer Usage_i8(0xfd) +#define Usage_Pow_iProduct Usage_i8(0xfe) +#define Usage_Pow_iSerialNumber Usage_i16(0xff) +#define Usage_BS_SmartBatteryBatteryMode Usage_i8(0x1) +#define Usage_BS_SmartBatteryBatteryStatus Usage_i8(0x2) +#define Usage_BS_SmartBatteryAlarmWarning Usage_i8(0x3) +#define Usage_BS_SmartBatteryChargerMode Usage_i8(0x4) +#define Usage_BS_SmartBatteryChargerStatus Usage_i8(0x5) +#define Usage_BS_SmartBatteryChargerSpecInfo Usage_i8(0x6) +#define Usage_BS_SmartBatterySelectorState Usage_i8(0x7) +#define Usage_BS_SmartBatterySelectorPresets Usage_i8(0x8) +#define Usage_BS_SmartBatterySelectorInfo Usage_i8(0x9) +#define Usage_BS_OptionalMfgFunction1 Usage_i8(0x10) +#define Usage_BS_OptionalMfgFunction2 Usage_i8(0x11) +#define Usage_BS_OptionalMfgFunction3 Usage_i8(0x12) +#define Usage_BS_OptionalMfgFunction4 Usage_i8(0x13) +#define Usage_BS_OptionalMfgFunction5 Usage_i8(0x14) +#define Usage_BS_ConnectionToSMBus Usage_i8(0x15) +#define Usage_BS_OutputConnection Usage_i8(0x16) +#define Usage_BS_ChargerConnection Usage_i8(0x17) +#define Usage_BS_BatteryInsertion Usage_i8(0x18) +#define Usage_BS_UseNext Usage_i8(0x19) +#define Usage_BS_OKToUse Usage_i8(0x1a) +#define Usage_BS_BatterySupported Usage_i8(0x1b) +#define Usage_BS_SelectorRevision Usage_i8(0x1c) +#define Usage_BS_ChargingIndicator Usage_i8(0x1d) +#define Usage_BS_ManufacturerAccess Usage_i8(0x28) +#define Usage_BS_RemainingCapacityLimit Usage_i8(0x29) +#define Usage_BS_RemainingTimeLimit Usage_i8(0x2a) +#define Usage_BS_AtRate Usage_i8(0x2b) +#define Usage_BS_CapacityMode Usage_i8(0x2c) +#define Usage_BS_BroadcastToCharger Usage_i8(0x2d) +#define Usage_BS_PrimaryBattery Usage_i8(0x2e) +#define Usage_BS_ChargeController Usage_i8(0x2f) +#define Usage_BS_TerminateCharge Usage_i8(0x40) +#define Usage_BS_TerminateDischarge Usage_i8(0x41) +#define Usage_BS_BelowRemainingCapacityLimit Usage_i8(0x42) +#define Usage_BS_RemainingTimeLimitExpired Usage_i8(0x43) +#define Usage_BS_Charging Usage_i8(0x44) +#define Usage_BS_Discharging Usage_i8(0x45) +#define Usage_BS_FullyCharged Usage_i8(0x46) +#define Usage_BS_FullyDischarged Usage_i8(0x47) +#define Usage_BS_ConditioningFlag Usage_i8(0x48) +#define Usage_BS_AtRateOK Usage_i8(0x49) +#define Usage_BS_SmartBatteryErrorCode Usage_i8(0x4a) +#define Usage_BS_NeedReplacement Usage_i8(0x4b) +#define Usage_BS_AtRateTimeToFull Usage_i8(0x60) +#define Usage_BS_AtRateTimeToEmpty Usage_i8(0x61) +#define Usage_BS_AverageCurrent Usage_i8(0x62) +#define Usage_BS_MaxError Usage_i8(0x63) +#define Usage_BS_RelativeStateOfCharge Usage_i8(0x64) +#define Usage_BS_AbsoluteStateOfCharge Usage_i8(0x65) +#define Usage_BS_RemainingCapacity Usage_i8(0x66) +#define Usage_BS_FullChargeCapacity Usage_i8(0x67) +#define Usage_BS_RunTimeToEmpty Usage_i8(0x68) +#define Usage_BS_AverageTimeToEmpty Usage_i8(0x69) +#define Usage_BS_AverageTimeToFull Usage_i8(0x6a) +#define Usage_BS_CycleCount Usage_i8(0x6b) +#define Usage_BS_BatteryPackModelLevel Usage_i8(0x80) +#define Usage_BS_InternalChargeController Usage_i8(0x81) +#define Usage_BS_PrimaryBatterySupport Usage_i8(0x82) +#define Usage_BS_DesignCapacity Usage_i8(0x83) +#define Usage_BS_SpecificationInfo Usage_i8(0x84) +#define Usage_BS_ManufactureDate Usage_i8(0x85) +#define Usage_BS_SerialNumber Usage_i8(0x86) +#define Usage_BS_iManufacturerName Usage_i8(0x87) +#define Usage_BS_iDeviceName Usage_i8(0x88) +#define Usage_BS_iDeviceChemistry Usage_i8(0x89) +#define Usage_BS_ManufacturerData Usage_i8(0x8a) +#define Usage_BS_Rechargable Usage_i8(0x8b) +#define Usage_BS_WarningCapacityLimit Usage_i8(0x8c) +#define Usage_BS_CapacityGranularity1 Usage_i8(0x8d) +#define Usage_BS_CapacityGranularity2 Usage_i8(0x8e) +#define Usage_BS_iOEMInformation Usage_i8(0x8f) +#define Usage_BS_InhibitCharge Usage_i8(0xc0) +#define Usage_BS_EnablePolling Usage_i8(0xc1) +#define Usage_BS_ResetToZero Usage_i8(0xc2) +#define Usage_BS_ACPresent Usage_i8(0xd0) +#define Usage_BS_BatteryPresent Usage_i8(0xd1) +#define Usage_BS_PowerFail Usage_i8(0xd2) +#define Usage_BS_AlarmInhibited Usage_i8(0xd3) +#define Usage_BS_ThermistorUnderRange Usage_i8(0xd4) +#define Usage_BS_ThermistorHot Usage_i8(0xd5) +#define Usage_BS_ThermistorCold Usage_i8(0xd6) +#define Usage_BS_ThermistorOverRange Usage_i8(0xd7) +#define Usage_BS_VoltageOutOfRange Usage_i8(0xd8) +#define Usage_BS_CurrentOutOfRange Usage_i8(0xd9) +#define Usage_BS_CurrentNotRegulated Usage_i8(0xda) +#define Usage_BS_VoltageNotRegulated Usage_i8(0xdb) +#define Usage_BS_MasterMode Usage_i8(0xdc) +#define Usage_BS_ChargerSelectorSupport Usage_i8(0xf0) +#define Usage_BS_ChargerSpec Usage_i8(0xf1) +#define Usage_BS_Level2 Usage_i8(0xf2) +#define Usage_BS_Level3 Usage_i8(0xf3) +#define Usage_BS_BarcodeBadgeReader Usage_i8(0x1) +#define Usage_BS_BarcodeScanner Usage_i8(0x2) +#define Usage_BS_DumbBarCodeScanner Usage_i8(0x3) +#define Usage_BS_CordlessScannerBase Usage_i8(0x4) +#define Usage_BS_BarCodeScannerCradle Usage_i8(0x5) +#define Usage_BS_AttributeReport Usage_i8(0x10) +#define Usage_BS_SettingsReport Usage_i8(0x11) +#define Usage_BS_ScannedDataReport Usage_i8(0x12) +#define Usage_BS_RawScannedDataReport Usage_i8(0x13) +#define Usage_BS_TriggerReport Usage_i8(0x14) +#define Usage_BS_StatusReport Usage_i8(0x15) +#define Usage_BS_UPCEANControlReport Usage_i8(0x16) +#define Usage_BS_EAN23LabelControlReport Usage_i8(0x17) +#define Usage_BS_Code39ControlReport Usage_i8(0x18) +#define Usage_BS_Interleaved2of5ControlReport Usage_i8(0x19) +#define Usage_BS_Standard2of5ControlReport Usage_i8(0x1a) +#define Usage_BS_MSIPlesseyControlReport Usage_i8(0x1b) +#define Usage_BS_CodabarControlReport Usage_i8(0x1c) +#define Usage_BS_Code128ControlReport Usage_i8(0x1d) +#define Usage_BS_Misc1DControlReport Usage_i8(0x1e) +#define Usage_BS_TwoDControlReport Usage_i8(0x1f) +#define Usage_BS_AimingPointerMode Usage_i8(0x30) +#define Usage_BS_BarCodePresentSensor Usage_i8(0x31) +#define Usage_BS_Class1ALaser Usage_i8(0x32) +#define Usage_BS_Class2Laser Usage_i8(0x33) +#define Usage_BS_HeaterPresent Usage_i8(0x34) +#define Usage_BS_ContactScanner Usage_i8(0x35) +#define Usage_BS_ElectronicArticleSurveillanceNotification Usage_i8(0x36) +#define Usage_BS_ConstantElectronicArticleSurveillance Usage_i8(0x37) +#define Usage_BS_ErrorIndication Usage_i8(0x38) +#define Usage_BS_FixedBeeper Usage_i8(0x39) +#define Usage_BS_GoodDecodeIndication Usage_i8(0x3a) +#define Usage_BS_HandsFreeScanning Usage_i8(0x3b) +#define Usage_BS_IntrinsicallySafe Usage_i8(0x3c) +#define Usage_BS_KlasseEinsLaser Usage_i8(0x3d) +#define Usage_BS_LongRangeScanner Usage_i8(0x3e) +#define Usage_BS_MirrorSpeedControl Usage_i8(0x3f) +#define Usage_BS_NotOnFileIndication Usage_i8(0x40) +#define Usage_BS_ProgrammableBeeper Usage_i8(0x41) +#define Usage_BS_Triggerless Usage_i8(0x42) +#define Usage_BS_Wand Usage_i8(0x43) +#define Usage_BS_WaterResistant Usage_i8(0x44) +#define Usage_BS_MultiRangeScanner Usage_i8(0x45) +#define Usage_BS_ProximitySensor Usage_i8(0x46) +#define Usage_BS_FragmentDecoding Usage_i8(0x4d) +#define Usage_BS_ScannerReadConfidence Usage_i8(0x4e) +#define Usage_BS_DataPrefix Usage_i8(0x4f) +#define Usage_BS_PrefixAIMI Usage_i8(0x50) +#define Usage_BS_PrefixNone Usage_i8(0x51) +#define Usage_BS_PrefixProprietary Usage_i8(0x52) +#define Usage_BS_ActiveTime Usage_i8(0x55) +#define Usage_BS_AimingLaserPattern Usage_i8(0x56) +#define Usage_BS_BarCodePresent Usage_i8(0x57) +#define Usage_BS_BeeperState Usage_i8(0x58) +#define Usage_BS_LaserOnTime Usage_i8(0x59) +#define Usage_BS_LaserState Usage_i8(0x5a) +#define Usage_BS_LockoutTime Usage_i8(0x5b) +#define Usage_BS_MotorState Usage_i8(0x5c) +#define Usage_BS_MotorTimeout Usage_i8(0x5d) +#define Usage_BS_PowerOnResetScanner Usage_i8(0x5e) +#define Usage_BS_PreventReadofBarcodes Usage_i8(0x5f) +#define Usage_BS_InitiateBarcodeRead Usage_i8(0x60) +#define Usage_BS_TriggerState Usage_i8(0x61) +#define Usage_BS_TriggerMode Usage_i8(0x62) +#define Usage_BS_TriggerModeBlinkingLaserOn Usage_i8(0x63) +#define Usage_BS_TriggerModeContinuousLaserOn Usage_i8(0x64) +#define Usage_BS_TriggerModeLaseronwhilePulled Usage_i8(0x65) +#define Usage_BS_TriggerModeLaserstaysonafterrelease Usage_i8(0x66) +#define Usage_BS_CommitParameterstoNVM Usage_i8(0x6d) +#define Usage_BS_ParameterScanning Usage_i8(0x6e) +#define Usage_BS_ParametersChanged Usage_i8(0x6f) +#define Usage_BS_Setparameterdefaultvalues Usage_i8(0x70) +#define Usage_BS_ScannerInCradle Usage_i8(0x75) +#define Usage_BS_ScannerInRange Usage_i8(0x76) +#define Usage_BS_AimDuration Usage_i8(0x7a) +#define Usage_BS_GoodReadLampDuration Usage_i8(0x7b) +#define Usage_BS_GoodReadLampIntensity Usage_i8(0x7c) +#define Usage_BS_GoodReadLED Usage_i8(0x7d) +#define Usage_BS_GoodReadToneFrequency Usage_i8(0x7e) +#define Usage_BS_GoodReadToneLength Usage_i8(0x7f) +#define Usage_BS_GoodReadToneVolume Usage_i8(0x80) +#define Usage_BS_NoReadMessage Usage_i8(0x82) +#define Usage_BS_NotonFileVolume Usage_i8(0x83) +#define Usage_BS_PowerupBeep Usage_i8(0x84) +#define Usage_BS_SoundErrorBeep Usage_i8(0x85) +#define Usage_BS_SoundGoodReadBeep Usage_i8(0x86) +#define Usage_BS_SoundNotOnFileBeep Usage_i8(0x87) +#define Usage_BS_GoodReadWhentoWrite Usage_i8(0x88) +#define Usage_BS_GRWTIAfterDecode Usage_i8(0x89) +#define Usage_BS_GRWTIBeepLampaftertransmit Usage_i8(0x8a) +#define Usage_BS_GRWTINoBeepLampuseatall Usage_i8(0x8b) +#define Usage_BS_BooklandEAN Usage_i8(0x91) +#define Usage_BS_ConvertEAN8to13Type Usage_i8(0x92) +#define Usage_BS_ConvertUPCAtoEAN13 Usage_i8(0x93) +#define Usage_BS_ConvertUPCEtoA Usage_i8(0x94) +#define Usage_BS_EAN13 Usage_i8(0x95) +#define Usage_BS_EAN8 Usage_i8(0x96) +#define Usage_BS_EAN99128Mandatory Usage_i8(0x97) +#define Usage_BS_EAN99P5128Optional Usage_i8(0x98) +#define Usage_BS_EnableEANTwoLabel Usage_i8(0x99) +#define Usage_BS_UPCEAN Usage_i8(0x9a) +#define Usage_BS_UPCEANCouponCode Usage_i8(0x9b) +#define Usage_BS_UPCEANPeriodicals Usage_i8(0x9c) +#define Usage_BS_UPCA Usage_i8(0x9d) +#define Usage_BS_UPCAwith128Mandatory Usage_i8(0x9e) +#define Usage_BS_UPCAwith128Optional Usage_i8(0x9f) +#define Usage_BS_UPCAwithP5Optional Usage_i8(0xa0) +#define Usage_BS_UPCE Usage_i8(0xa1) +#define Usage_BS_UPCE1 Usage_i8(0xa2) +#define Usage_BS_Periodical Usage_i8(0xa9) +#define Usage_BS_PeriodicalAutoDiscriminatePlus2 Usage_i8(0xaa) +#define Usage_BS_PeriodicalOnlyDecodewithPlus2 Usage_i8(0xab) +#define Usage_BS_PeriodicalIgnorePlus2 Usage_i8(0xac) +#define Usage_BS_PeriodicalAutoDiscriminatePlus5 Usage_i8(0xad) +#define Usage_BS_PeriodicalOnlyDecodewithPlus5 Usage_i8(0xae) +#define Usage_BS_PeriodicalIgnorePlus5 Usage_i8(0xaf) +#define Usage_BS_Check Usage_i8(0xb0) +#define Usage_BS_CheckDisablePrice Usage_i8(0xb1) +#define Usage_BS_CheckEnable4digitPrice Usage_i8(0xb2) +#define Usage_BS_CheckEnable5digitPrice Usage_i8(0xb3) +#define Usage_BS_CheckEnableEuropean4digitPrice Usage_i8(0xb4) +#define Usage_BS_CheckEnableEuropean5digitPrice Usage_i8(0xb5) +#define Usage_BS_EANTwoLabel Usage_i8(0xb7) +#define Usage_BS_EANThreeLabel Usage_i8(0xb8) +#define Usage_BS_EAN8FlagDigit1 Usage_i8(0xb9) +#define Usage_BS_EAN8FlagDigit2 Usage_i8(0xba) +#define Usage_BS_EAN8FlagDigit3 Usage_i8(0xbb) +#define Usage_BS_EAN13FlagDigit1 Usage_i8(0xbc) +#define Usage_BS_EAN13FlagDigit2 Usage_i8(0xbd) +#define Usage_BS_EAN13FlagDigit3 Usage_i8(0xbe) +#define Usage_BS_AddEAN23LabelDefinition Usage_i8(0xbf) +#define Usage_BS_ClearallEAN23LabelDefinitions Usage_i8(0xc0) +#define Usage_BS_Codabar Usage_i8(0xc3) +#define Usage_BS_Code128 Usage_i8(0xc4) +#define Usage_BS_Code39 Usage_i8(0xc7) +#define Usage_BS_Code93 Usage_i8(0xc8) +#define Usage_BS_FullASCIIConversion Usage_i8(0xc9) +#define Usage_BS_Interleaved2of5 Usage_i8(0xca) +#define Usage_BS_ItalianPharmacyCode Usage_i8(0xcb) +#define Usage_BS_MSIPlessey Usage_i8(0xcc) +#define Usage_BS_Standard2of5IATA Usage_i8(0xcd) +#define Usage_BS_Standard2of5 Usage_i8(0xce) +#define Usage_BS_TransmitStartStop Usage_i8(0xd3) +#define Usage_BS_TriOptic Usage_i8(0xd4) +#define Usage_BS_UCCEAN128 Usage_i8(0xd5) +#define Usage_BS_CheckDigit Usage_i8(0xd6) +#define Usage_BS_CheckDigitDisable Usage_i8(0xd7) +#define Usage_BS_CheckDigitEnableInterleaved2of5OPCC Usage_i8(0xd8) +#define Usage_BS_CheckDigitEnableInterleaved2of5USS Usage_i8(0xd9) +#define Usage_BS_CheckDigitEnableStandard2of5OPCC Usage_i8(0xda) +#define Usage_BS_CheckDigitEnableStandard2of5USS Usage_i8(0xdb) +#define Usage_BS_CheckDigitEnableOneMSIPlessey Usage_i8(0xdc) +#define Usage_BS_CheckDigitEnableTwoMSIPlessey Usage_i8(0xdd) +#define Usage_BS_CheckDigitCodabarEnable Usage_i8(0xde) +#define Usage_BS_CheckDigitCode39Enable Usage_i8(0xdf) +#define Usage_BS_TransmitCheckDigit Usage_i8(0xf0) +#define Usage_BS_DisableCheckDigitTransmit Usage_i8(0xf1) +#define Usage_BS_EnableCheckDigitTransmit Usage_i8(0xf2) +#define Usage_BS_SymbologyIdentifier1 Usage_i8(0xfb) +#define Usage_BS_SymbologyIdentifier2 Usage_i8(0xfc) +#define Usage_BS_SymbologyIdentifier3 Usage_i8(0xfd) +#define Usage_BS_DecodedData Usage_i8(0xfe) +#define Usage_BS_DecodeDataContinued Usage_i16(0xff) +#define Usage_BS_BarSpaceData Usage_i16(0x100) +#define Usage_BS_ScannerDataAccuracy Usage_i16(0x101) +#define Usage_BS_RawDataPolarity Usage_i16(0x102) +#define Usage_BS_PolarityInvertedBarCode Usage_i16(0x103) +#define Usage_BS_PolarityNormalBarCode Usage_i16(0x104) +#define Usage_BS_MinimumLengthtoDecode Usage_i16(0x106) +#define Usage_BS_MaximumLengthtoDecode Usage_i16(0x107) +#define Usage_BS_DiscreteLengthtoDecode1 Usage_i16(0x108) +#define Usage_BS_DiscreteLengthtoDecode2 Usage_i16(0x109) +#define Usage_BS_DataLengthMethod Usage_i16(0x10a) +#define Usage_BS_DLMethodReadany Usage_i16(0x10b) +#define Usage_BS_DLMethodCheckinRange Usage_i16(0x10c) +#define Usage_BS_DLMethodCheckforDiscrete Usage_i16(0x10d) +#define Usage_BS_AztecCode Usage_i16(0x110) +#define Usage_BS_BC412 Usage_i16(0x111) +#define Usage_BS_ChannelCode Usage_i16(0x112) +#define Usage_BS_Code16 Usage_i16(0x113) +#define Usage_BS_Code32 Usage_i16(0x114) +#define Usage_BS_Code49 Usage_i16(0x115) +#define Usage_BS_CodeOne Usage_i16(0x116) +#define Usage_BS_Colorcode Usage_i16(0x117) +#define Usage_BS_DataMatrix Usage_i16(0x118) +#define Usage_BS_MaxiCode Usage_i16(0x119) +#define Usage_BS_MicroPDF Usage_i16(0x11a) +#define Usage_BS_PDF417 Usage_i16(0x11b) +#define Usage_BS_PosiCode Usage_i16(0x11c) +#define Usage_BS_QRCode Usage_i16(0x11d) +#define Usage_BS_SuperCode Usage_i16(0x11e) +#define Usage_BS_UltraCode Usage_i16(0x11f) +#define Usage_BS_USD5SlugCode Usage_i16(0x120) +#define Usage_BS_VeriCode Usage_i16(0x121) +#define Usage_Sca_Scales Usage_i8(0x1) +#define Usage_Sca_ScaleDevice Usage_i8(0x20) +#define Usage_Sca_ScaleClass Usage_i8(0x21) +#define Usage_Sca_ScaleClassIMetric Usage_i8(0x22) +#define Usage_Sca_ScaleClassIIMetric Usage_i8(0x23) +#define Usage_Sca_ScaleClassIIIMetric Usage_i8(0x24) +#define Usage_Sca_ScaleClassIIILMetric Usage_i8(0x25) +#define Usage_Sca_ScaleClassIVMetric Usage_i8(0x26) +#define Usage_Sca_ScaleClassIIIEnglish Usage_i8(0x27) +#define Usage_Sca_ScaleClassIIILEnglish Usage_i8(0x28) +#define Usage_Sca_ScaleClassIVEnglish Usage_i8(0x29) +#define Usage_Sca_ScaleClassGeneric Usage_i8(0x2a) +#define Usage_Sca_ScaleAttributeReport Usage_i8(0x30) +#define Usage_Sca_ScaleControlReport Usage_i8(0x31) +#define Usage_Sca_ScaleDataReport Usage_i8(0x32) +#define Usage_Sca_ScaleStatusReport Usage_i8(0x33) +#define Usage_Sca_ScaleWeightLimitReport Usage_i8(0x34) +#define Usage_Sca_ScaleStatisticsReport Usage_i8(0x35) +#define Usage_Sca_DataWeight Usage_i8(0x40) +#define Usage_Sca_DataScaling Usage_i8(0x41) +#define Usage_Sca_WeightUnit Usage_i8(0x50) +#define Usage_Sca_WeightUnitMilligram Usage_i8(0x51) +#define Usage_Sca_WeightUnitGram Usage_i8(0x52) +#define Usage_Sca_WeightUnitKilogram Usage_i8(0x53) +#define Usage_Sca_WeightUnitCarats Usage_i8(0x54) +#define Usage_Sca_WeightUnitTaels Usage_i8(0x55) +#define Usage_Sca_WeightUnitGrains Usage_i8(0x56) +#define Usage_Sca_WeightUnitPennyweights Usage_i8(0x57) +#define Usage_Sca_WeightUnitMetricTon Usage_i8(0x58) +#define Usage_Sca_WeightUnitAvoirTon Usage_i8(0x59) +#define Usage_Sca_WeightUnitTroyOunce Usage_i8(0x5a) +#define Usage_Sca_WeightUnitOunce Usage_i8(0x5b) +#define Usage_Sca_WeightUnitPound Usage_i8(0x5c) +#define Usage_Sca_CalibrationCount Usage_i8(0x60) +#define Usage_Sca_ReZeroCount Usage_i8(0x61) +#define Usage_Sca_ScaleStatus Usage_i8(0x70) +#define Usage_Sca_ScaleStatusFault Usage_i8(0x71) +#define Usage_Sca_ScaleStatusStableatCenterofZero Usage_i8(0x72) +#define Usage_Sca_ScaleStatusInMotion Usage_i8(0x73) +#define Usage_Sca_ScaleStatusWeightStable Usage_i8(0x74) +#define Usage_Sca_ScaleStatusUnderZero Usage_i8(0x75) +#define Usage_Sca_ScaleStatusOverWeightLimit Usage_i8(0x76) +#define Usage_Sca_ScaleStatusRequiresCalibration Usage_i8(0x77) +#define Usage_Sca_ScaleStatusRequiresRezeroing Usage_i8(0x78) +#define Usage_Sca_ZeroScale Usage_i8(0x80) +#define Usage_Sca_EnforcedZeroReturn Usage_i8(0x81) +#define Usage_MSR_MSRDeviceReadOnly Usage_i8(0x1) +#define Usage_MSR_Track1Length Usage_i8(0x11) +#define Usage_MSR_Track2Length Usage_i8(0x12) +#define Usage_MSR_Track3Length Usage_i8(0x13) +#define Usage_MSR_TrackJISLength Usage_i8(0x14) +#define Usage_MSR_TrackData Usage_i8(0x20) +#define Usage_MSR_Track1Data Usage_i8(0x21) +#define Usage_MSR_Track2Data Usage_i8(0x22) +#define Usage_MSR_Track3Data Usage_i8(0x23) +#define Usage_MSR_TrackJISData Usage_i8(0x24) +#define Usage_CC_CameraAutofocus Usage_i8(0x20) +#define Usage_CC_CameraShutter Usage_i8(0x21) +#define Usage_Arc_GeneralPurposeIOCard Usage_i8(0x1) +#define Usage_Arc_CoinDoor Usage_i8(0x2) +#define Usage_Arc_WatchdogTimer Usage_i8(0x3) +#define Usage_Arc_GeneralPurposeAnalogInputState Usage_i8(0x30) +#define Usage_Arc_GeneralPurposeDigitalInputState Usage_i8(0x31) +#define Usage_Arc_GeneralPurposeOpticalInputState Usage_i8(0x32) +#define Usage_Arc_GeneralPurposeDigitalOutputState Usage_i8(0x33) +#define Usage_Arc_NumberofCoinDoors Usage_i8(0x34) +#define Usage_Arc_CoinDrawerDropCount Usage_i8(0x35) +#define Usage_Arc_CoinDrawerStart Usage_i8(0x36) +#define Usage_Arc_CoinDrawerService Usage_i8(0x37) +#define Usage_Arc_CoinDrawerTilt Usage_i8(0x38) +#define Usage_Arc_CoinDoorTest Usage_i8(0x39) +#define Usage_Arc_CoinDoorLockout Usage_i8(0x40) +#define Usage_Arc_WatchdogTimeout Usage_i8(0x41) +#define Usage_Arc_WatchdogAction Usage_i8(0x42) +#define Usage_Arc_WatchdogReboot Usage_i8(0x43) +#define Usage_Arc_WatchdogRestart Usage_i8(0x44) +#define Usage_Arc_AlarmInput Usage_i8(0x45) +#define Usage_Arc_CoinDoorCounter Usage_i8(0x46) +#define Usage_Arc_IODirectionMapping Usage_i8(0x47) +#define Usage_Arc_SetIODirectionMapping Usage_i8(0x48) +#define Usage_Arc_ExtendedOpticalInputState Usage_i8(0x49) +#define Usage_Arc_PinPadInputState Usage_i8(0x4a) +#define Usage_Arc_PinPadStatus Usage_i8(0x4b) +#define Usage_Arc_PinPadOutput Usage_i8(0x4c) +#define Usage_Arc_PinPadCommand Usage_i8(0x4d) +#define Usage_FIDOA_U2FAuthenticatorDevice Usage_i8(0x1) +#define Usage_FIDOA_InputReportData Usage_i8(0x20) +#define Usage_FIDOA_OutputReportData Usage_i8(0x21) diff --git a/drivers/hid/hid-a4tech.c b/drivers/hid/hid-a4tech.c index 2cbc32dda7f7..54bfaf61182b 100644 --- a/drivers/hid/hid-a4tech.c +++ b/drivers/hid/hid-a4tech.c @@ -163,4 +163,5 @@ static struct hid_driver a4_driver = { }; module_hid_driver(a4_driver); +MODULE_DESCRIPTION("HID driver for some a4tech \"special\" devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-alps.c b/drivers/hid/hid-alps.c index 669d769ea1dc..ba00f6e6324b 100644 --- a/drivers/hid/hid-alps.c +++ b/drivers/hid/hid-alps.c @@ -8,7 +8,7 @@ #include <linux/input.h> #include <linux/input/mt.h> #include <linux/module.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "hid-ids.h" /* ALPS Device Product ID */ diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index bd022e004356..49812a76b7ed 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -8,6 +8,8 @@ * Copyright (c) 2006-2007 Jiri Kosina * Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com> * Copyright (c) 2019 Paul Pawlowski <paul@mrarm.io> + * Copyright (c) 2023 Orlando Chamberlain <orlandoch.dev@gmail.com> + * Copyright (c) 2024 Aditya Garg <gargaditya08@live.com> */ /* @@ -23,6 +25,7 @@ #include <linux/timer.h> #include <linux/string.h> #include <linux/leds.h> +#include <dt-bindings/leds/common.h> #include "hid-ids.h" @@ -38,12 +41,17 @@ #define APPLE_RDESC_BATTERY BIT(9) #define APPLE_BACKLIGHT_CTL BIT(10) #define APPLE_IS_NON_APPLE BIT(11) +#define APPLE_MAGIC_BACKLIGHT BIT(12) #define APPLE_FLAG_FKEY 0x01 #define HID_COUNTRY_INTERNATIONAL_ISO 13 #define APPLE_BATTERY_TIMEOUT_MS 60000 +#define HID_USAGE_MAGIC_BL 0xff00000f +#define APPLE_MAGIC_REPORT_ID_POWER 3 +#define APPLE_MAGIC_REPORT_ID_BRIGHTNESS 1 + static unsigned int fnmode = 3; module_param(fnmode, uint, 0644); MODULE_PARM_DESC(fnmode, "Mode of fn key on Apple keyboards (0 = disabled, " @@ -81,6 +89,12 @@ struct apple_sc_backlight { struct hid_device *hdev; }; +struct apple_magic_backlight { + struct led_classdev cdev; + struct hid_report *brightness; + struct hid_report *power; +}; + struct apple_sc { struct hid_device *hdev; unsigned long quirks; @@ -460,6 +474,7 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2015) table = magic_keyboard_2015_fn_keys; else if (hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021 || + hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2024 || hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021 || hid->product == USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021) table = apple2021_fn_keys; @@ -531,6 +546,9 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, } } + if (usage->hid == 0xc0301) /* Omoton KB066 quirk */ + code = KEY_F6; + if (usage->code != code) { input_event_with_scancode(input, usage->type, code, usage->hid, value); @@ -606,7 +624,7 @@ static void apple_battery_timer_tick(struct timer_list *t) * MacBook JIS keyboard has wrong logical maximum * Magic Keyboard JIS has wrong logical maximum */ -static __u8 *apple_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *apple_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { struct apple_sc *asc = hid_get_drvdata(hdev); @@ -822,6 +840,66 @@ cleanup_and_exit: return ret; } +static void apple_magic_backlight_report_set(struct hid_report *rep, s32 value, u8 rate) +{ + rep->field[0]->value[0] = value; + rep->field[1]->value[0] = 0x5e; /* Mimic Windows */ + rep->field[1]->value[0] |= rate << 8; + + hid_hw_request(rep->device, rep, HID_REQ_SET_REPORT); +} + +static void apple_magic_backlight_set(struct apple_magic_backlight *backlight, + int brightness, char rate) +{ + apple_magic_backlight_report_set(backlight->power, brightness ? 1 : 0, rate); + if (brightness) + apple_magic_backlight_report_set(backlight->brightness, brightness, rate); +} + +static int apple_magic_backlight_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct apple_magic_backlight *backlight = container_of(led_cdev, + struct apple_magic_backlight, cdev); + + apple_magic_backlight_set(backlight, brightness, 1); + return 0; +} + +static int apple_magic_backlight_init(struct hid_device *hdev) +{ + struct apple_magic_backlight *backlight; + struct hid_report_enum *report_enum; + + /* + * Ensure this usb endpoint is for the keyboard backlight, not touchbar + * backlight. + */ + if (hdev->collection[0].usage != HID_USAGE_MAGIC_BL) + return -ENODEV; + + backlight = devm_kzalloc(&hdev->dev, sizeof(*backlight), GFP_KERNEL); + if (!backlight) + return -ENOMEM; + + report_enum = &hdev->report_enum[HID_FEATURE_REPORT]; + backlight->brightness = report_enum->report_id_hash[APPLE_MAGIC_REPORT_ID_BRIGHTNESS]; + backlight->power = report_enum->report_id_hash[APPLE_MAGIC_REPORT_ID_POWER]; + + if (!backlight->brightness || !backlight->power) + return -ENODEV; + + backlight->cdev.name = ":white:" LED_FUNCTION_KBD_BACKLIGHT; + backlight->cdev.max_brightness = backlight->brightness->field[0]->logical_maximum; + backlight->cdev.brightness_set_blocking = apple_magic_backlight_led_set; + + apple_magic_backlight_set(backlight, 0, 0); + + return devm_led_classdev_register(&hdev->dev, &backlight->cdev); + +} + static int apple_probe(struct hid_device *hdev, const struct hid_device_id *id) { @@ -860,7 +938,18 @@ static int apple_probe(struct hid_device *hdev, if (quirks & APPLE_BACKLIGHT_CTL) apple_backlight_init(hdev); + if (quirks & APPLE_MAGIC_BACKLIGHT) { + ret = apple_magic_backlight_init(hdev); + if (ret) + goto out_err; + } + return 0; + +out_err: + del_timer_sync(&asc->battery_timer); + hid_hw_stop(hdev); + return ret; } static void apple_remove(struct hid_device *hdev) @@ -1065,6 +1154,10 @@ static const struct hid_device_id apple_devices[] = { .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_RDESC_BATTERY }, { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021), .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2024), + .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_RDESC_BATTERY }, + { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2024), + .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021), .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_RDESC_BATTERY }, { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021), @@ -1073,6 +1166,8 @@ static const struct hid_device_id apple_devices[] = { .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK | APPLE_RDESC_BATTERY }, { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021), .driver_data = APPLE_HAS_FN | APPLE_ISO_TILDE_QUIRK }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT), + .driver_data = APPLE_MAGIC_BACKLIGHT }, { } }; @@ -1091,4 +1186,5 @@ static struct hid_driver apple_driver = { }; module_hid_driver(apple_driver); +MODULE_DESCRIPTION("Apple USB HID quirks support for Linux"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index 78cdfb8b9a7a..46e3e42f9eb5 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -335,36 +335,20 @@ static int asus_raw_event(struct hid_device *hdev, if (drvdata->quirks & QUIRK_MEDION_E1239T) return asus_e1239t_event(drvdata, data, size); - if (drvdata->quirks & QUIRK_USE_KBD_BACKLIGHT) { + /* + * Skip these report ID, the device emits a continuous stream associated + * with the AURA mode it is in which looks like an 'echo'. + */ + if (report->id == FEATURE_KBD_LED_REPORT_ID1 || report->id == FEATURE_KBD_LED_REPORT_ID2) + return -1; + if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) { /* - * Skip these report ID, the device emits a continuous stream associated - * with the AURA mode it is in which looks like an 'echo'. + * G713 and G733 send these codes on some keypresses, depending on + * the key pressed it can trigger a shutdown event if not caught. */ - if (report->id == FEATURE_KBD_LED_REPORT_ID1 || - report->id == FEATURE_KBD_LED_REPORT_ID2) { + if (data[0] == 0x02 && data[1] == 0x30) { return -1; - /* Additional report filtering */ - } else if (report->id == FEATURE_KBD_REPORT_ID) { - /* - * G14 and G15 send these codes on some keypresses with no - * discernable reason for doing so. We'll filter them out to avoid - * unmapped warning messages later. - */ - if (data[1] == 0xea || data[1] == 0xec || data[1] == 0x02 || - data[1] == 0x8a || data[1] == 0x9e) { - return -1; - } - } - if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) { - /* - * G713 and G733 send these codes on some keypresses, depending on - * the key pressed it can trigger a shutdown event if not caught. - */ - if(data[0] == 0x02 && data[1] == 0x30) { - return -1; - } } - } if (drvdata->quirks & QUIRK_ROG_CLAYMORE_II_KEYBOARD) { @@ -402,9 +386,9 @@ static int asus_kbd_set_report(struct hid_device *hdev, const u8 *buf, size_t bu return ret; } -static int asus_kbd_init(struct hid_device *hdev) +static int asus_kbd_init(struct hid_device *hdev, u8 report_id) { - const u8 buf[] = { FEATURE_KBD_REPORT_ID, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54, + const u8 buf[] = { report_id, 0x41, 0x53, 0x55, 0x53, 0x20, 0x54, 0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 }; int ret; @@ -416,9 +400,10 @@ static int asus_kbd_init(struct hid_device *hdev) } static int asus_kbd_get_functions(struct hid_device *hdev, - unsigned char *kbd_func) + unsigned char *kbd_func, + u8 report_id) { - const u8 buf[] = { FEATURE_KBD_REPORT_ID, 0x05, 0x20, 0x31, 0x00, 0x08 }; + const u8 buf[] = { report_id, 0x05, 0x20, 0x31, 0x00, 0x08 }; u8 *readbuf; int ret; @@ -447,49 +432,24 @@ static int asus_kbd_get_functions(struct hid_device *hdev, return ret; } -static int rog_nkey_led_init(struct hid_device *hdev) +static int asus_kbd_disable_oobe(struct hid_device *hdev) { - const u8 buf_init_start[] = { FEATURE_KBD_LED_REPORT_ID1, 0xB9 }; - u8 buf_init2[] = { FEATURE_KBD_LED_REPORT_ID1, 0x41, 0x53, 0x55, 0x53, 0x20, - 0x54, 0x65, 0x63, 0x68, 0x2e, 0x49, 0x6e, 0x63, 0x2e, 0x00 }; - u8 buf_init3[] = { FEATURE_KBD_LED_REPORT_ID1, - 0x05, 0x20, 0x31, 0x00, 0x08 }; + const u8 init[][6] = { + { FEATURE_KBD_REPORT_ID, 0x05, 0x20, 0x31, 0x00, 0x08 }, + { FEATURE_KBD_REPORT_ID, 0xBA, 0xC5, 0xC4 }, + { FEATURE_KBD_REPORT_ID, 0xD0, 0x8F, 0x01 }, + { FEATURE_KBD_REPORT_ID, 0xD0, 0x85, 0xFF } + }; int ret; - hid_info(hdev, "Asus initialise N-KEY Device"); - /* The first message is an init start */ - ret = asus_kbd_set_report(hdev, buf_init_start, sizeof(buf_init_start)); - if (ret < 0) { - hid_warn(hdev, "Asus failed to send init start command: %d\n", ret); - return ret; - } - /* Followed by a string */ - ret = asus_kbd_set_report(hdev, buf_init2, sizeof(buf_init2)); - if (ret < 0) { - hid_warn(hdev, "Asus failed to send init command 1.0: %d\n", ret); - return ret; - } - /* Followed by a string */ - ret = asus_kbd_set_report(hdev, buf_init3, sizeof(buf_init3)); - if (ret < 0) { - hid_warn(hdev, "Asus failed to send init command 1.1: %d\n", ret); - return ret; - } - - /* begin second report ID with same data */ - buf_init2[0] = FEATURE_KBD_LED_REPORT_ID2; - buf_init3[0] = FEATURE_KBD_LED_REPORT_ID2; - - ret = asus_kbd_set_report(hdev, buf_init2, sizeof(buf_init2)); - if (ret < 0) { - hid_warn(hdev, "Asus failed to send init command 2.0: %d\n", ret); - return ret; + for (size_t i = 0; i < ARRAY_SIZE(init); i++) { + ret = asus_kbd_set_report(hdev, init[i], sizeof(init[i])); + if (ret < 0) + return ret; } - ret = asus_kbd_set_report(hdev, buf_init3, sizeof(buf_init3)); - if (ret < 0) - hid_warn(hdev, "Asus failed to send init command 2.1: %d\n", ret); - return ret; + hid_info(hdev, "Disabled OOBE for keyboard\n"); + return 0; } static void asus_schedule_work(struct asus_kbd_leds *led) @@ -552,12 +512,19 @@ static void asus_kbd_backlight_work(struct work_struct *work) */ static bool asus_kbd_wmi_led_control_present(struct hid_device *hdev) { + struct asus_drvdata *drvdata = hid_get_drvdata(hdev); u32 value; int ret; if (!IS_ENABLED(CONFIG_ASUS_WMI)) return false; + if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD && + dmi_check_system(asus_use_hid_led_dmi_ids)) { + hid_info(hdev, "using HID for asus::kbd_backlight\n"); + return false; + } + ret = asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, ASUS_WMI_DEVID_KBD_BACKLIGHT, 0, &value); hid_dbg(hdev, "WMI backlight check: rc %d value %x", ret, value); @@ -574,17 +541,33 @@ static int asus_kbd_register_leds(struct hid_device *hdev) int ret; if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD) { - ret = rog_nkey_led_init(hdev); + /* Initialize keyboard */ + ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID); if (ret < 0) return ret; + + /* The LED endpoint is initialised in two HID */ + ret = asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID1); + if (ret < 0) + return ret; + + ret = asus_kbd_init(hdev, FEATURE_KBD_LED_REPORT_ID2); + if (ret < 0) + return ret; + + if (dmi_match(DMI_PRODUCT_FAMILY, "ProArt P16")) { + ret = asus_kbd_disable_oobe(hdev); + if (ret < 0) + return ret; + } } else { /* Initialize keyboard */ - ret = asus_kbd_init(hdev); + ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID); if (ret < 0) return ret; /* Get keyboard functions */ - ret = asus_kbd_get_functions(hdev, &kbd_func); + ret = asus_kbd_get_functions(hdev, &kbd_func, FEATURE_KBD_REPORT_ID); if (ret < 0) return ret; @@ -897,7 +880,10 @@ static int asus_input_mapping(struct hid_device *hdev, 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 */ - + case 0xa5: asus_map_key_clear(KEY_F15); break; /* ROG Ally left back */ + case 0xa6: asus_map_key_clear(KEY_F16); break; /* ROG Ally QAM button */ + case 0xa7: asus_map_key_clear(KEY_F17); break; /* ROG Ally ROG long-press */ + case 0xa8: asus_map_key_clear(KEY_F18); break; /* ROG Ally ROG long-press-release */ default: /* ASUS lazily declares 256 usages, ignore the rest, @@ -1166,7 +1152,7 @@ static const __u8 asus_g752_fixed_rdesc[] = { 0x2A, 0xFF, 0x00, /* Usage Maximum (0xFF) */ }; -static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { struct asus_drvdata *drvdata = hid_get_drvdata(hdev); @@ -1223,7 +1209,7 @@ static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc, if (drvdata->quirks & QUIRK_G752_KEYBOARD && *rsize == 75 && rdesc[61] == 0x15 && rdesc[62] == 0x00) { - /* report is missing usage mninum and maximum */ + /* report is missing usage minimum and maximum */ __u8 *new_rdesc; size_t new_size = *rsize + sizeof(asus_g752_fixed_rdesc); @@ -1250,6 +1236,19 @@ static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc, rdesc[205] = 0x01; } + /* match many more n-key devices */ + if (drvdata->quirks & QUIRK_ROG_NKEY_KEYBOARD && *rsize > 15) { + for (int i = 0; i < *rsize - 15; i++) { + /* offset to the count from 0x5a report part always 14 */ + if (rdesc[i] == 0x85 && rdesc[i + 1] == 0x5a && + rdesc[i + 14] == 0x95 && rdesc[i + 15] == 0x05) { + hid_info(hdev, "Fixing up Asus N-Key report descriptor\n"); + rdesc[i + 15] = 0x01; + break; + } + } + } + return rdesc; } @@ -1277,6 +1276,15 @@ static const struct hid_device_id asus_devices[] = { 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_Z13_LIGHTBAR), + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, + USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY), + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, + USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY_X), + 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, @@ -1319,4 +1327,4 @@ static struct hid_driver asus_driver = { }; module_hid_driver(asus_driver); -MODULE_LICENSE("GPL");
\ No newline at end of file +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-aureal.c b/drivers/hid/hid-aureal.c index ac8946f80e22..896304148a87 100644 --- a/drivers/hid/hid-aureal.c +++ b/drivers/hid/hid-aureal.c @@ -18,7 +18,7 @@ #include "hid-ids.h" -static __u8 *aureal_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *aureal_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { if (*rsize >= 54 && rdesc[52] == 0x25 && rdesc[53] == 0x01) { @@ -41,4 +41,5 @@ static struct hid_driver aureal_driver = { }; module_hid_driver(aureal_driver); +MODULE_DESCRIPTION("HID driver for Aureal Cy se W-01RN USB_V3.1 devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-belkin.c b/drivers/hid/hid-belkin.c index fc0b3bb383cc..75aaed35ee9f 100644 --- a/drivers/hid/hid-belkin.c +++ b/drivers/hid/hid-belkin.c @@ -85,4 +85,5 @@ static struct hid_driver belkin_driver = { }; module_hid_driver(belkin_driver); +MODULE_DESCRIPTION("HID driver for some belkin \"special\" devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-betopff.c b/drivers/hid/hid-betopff.c index 25ed7b9a917e..a6d5f030d023 100644 --- a/drivers/hid/hid-betopff.c +++ b/drivers/hid/hid-betopff.c @@ -162,4 +162,5 @@ static struct hid_driver betop_driver = { }; module_hid_driver(betop_driver); +MODULE_DESCRIPTION("Force feedback support for Betop based devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-bigbenff.c b/drivers/hid/hid-bigbenff.c index a02cb517b4c4..9f05465358d9 100644 --- a/drivers/hid/hid-bigbenff.c +++ b/drivers/hid/hid-bigbenff.c @@ -99,7 +99,7 @@ * - map previously unused analog trigger data to Z/RZ * - simplify feature and output descriptor */ -static __u8 pid0902_rdesc_fixed[] = { +static const __u8 pid0902_rdesc_fixed[] = { 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */ 0x09, 0x05, /* Usage (Game Pad) */ 0xA1, 0x01, /* Collection (Application) */ @@ -464,12 +464,12 @@ error_hw_stop: return error; } -static __u8 *bigben_report_fixup(struct hid_device *hid, __u8 *rdesc, +static const __u8 *bigben_report_fixup(struct hid_device *hid, __u8 *rdesc, unsigned int *rsize) { if (*rsize == PID0902_RDESC_ORIG_SIZE) { - rdesc = pid0902_rdesc_fixed; *rsize = sizeof(pid0902_rdesc_fixed); + return pid0902_rdesc_fixed; } else hid_warn(hid, "unexpected rdesc, please submit for review\n"); return rdesc; @@ -490,4 +490,5 @@ static struct hid_driver bigben_driver = { }; module_hid_driver(bigben_driver); +MODULE_DESCRIPTION("LED & force feedback support for BigBen Interactive"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-cherry.c b/drivers/hid/hid-cherry.c index 6a71187b5cf6..a504632febfc 100644 --- a/drivers/hid/hid-cherry.c +++ b/drivers/hid/hid-cherry.c @@ -22,7 +22,7 @@ * Cherry Cymotion keyboard have an invalid HID report descriptor, * that needs fixing before we can parse it. */ -static __u8 *ch_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *ch_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { if (*rsize >= 18 && rdesc[11] == 0x3c && rdesc[12] == 0x02) { @@ -68,4 +68,5 @@ static struct hid_driver ch_driver = { }; module_hid_driver(ch_driver); +MODULE_DESCRIPTION("HID driver for some cherry \"special\" devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-chicony.c b/drivers/hid/hid-chicony.c index f04d2aa23efe..5776ec2e7159 100644 --- a/drivers/hid/hid-chicony.c +++ b/drivers/hid/hid-chicony.c @@ -88,8 +88,8 @@ static int ch_input_mapping(struct hid_device *hdev, struct hid_input *hi, return 1; } -static __u8 *ch_switch12_report_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) +static const __u8 *ch_switch12_report_fixup(struct hid_device *hdev, + __u8 *rdesc, unsigned int *rsize) { struct usb_interface *intf = to_usb_interface(hdev->dev.parent); @@ -152,4 +152,5 @@ static struct hid_driver ch_driver = { }; module_hid_driver(ch_driver); +MODULE_DESCRIPTION("HID driver for some chicony \"special\" devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-cmedia.c b/drivers/hid/hid-cmedia.c index cab42047bc99..528d7f361215 100644 --- a/drivers/hid/hid-cmedia.c +++ b/drivers/hid/hid-cmedia.c @@ -26,7 +26,7 @@ MODULE_LICENSE("GPL"); /* Fixed report descriptor of HS-100B audio chip * Bit 4 is an abolute Microphone mute usage instead of being unassigned. */ -static __u8 hs100b_rdesc_fixed[] = { +static const __u8 hs100b_rdesc_fixed[] = { 0x05, 0x0C, /* Usage Page (Consumer), */ 0x09, 0x01, /* Usage (Consumer Control), */ 0xA1, 0x01, /* Collection (Application), */ @@ -199,13 +199,13 @@ static struct hid_driver cmhid_driver = { .input_mapping = cmhid_input_mapping, }; -static __u8 *cmhid_hs100b_report_fixup(struct hid_device *hid, __u8 *rdesc, +static const __u8 *cmhid_hs100b_report_fixup(struct hid_device *hid, __u8 *rdesc, unsigned int *rsize) { if (*rsize == HS100B_RDESC_ORIG_SIZE) { hid_info(hid, "Fixing CMedia HS-100B report descriptor\n"); - rdesc = hs100b_rdesc_fixed; *rsize = sizeof(hs100b_rdesc_fixed); + return hs100b_rdesc_fixed; } return rdesc; } diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index de7a477d6665..4497b50799db 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -20,7 +20,7 @@ #include <linux/list.h> #include <linux/mm.h> #include <linux/spinlock.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <asm/byteorder.h> #include <linux/input.h> #include <linux/wait.h> @@ -46,6 +46,34 @@ module_param_named(ignore_special_drivers, hid_ignore_special_drivers, int, 0600 MODULE_PARM_DESC(ignore_special_drivers, "Ignore any special drivers and handle all devices by generic driver"); /* + * Convert a signed n-bit integer to signed 32-bit integer. + */ + +static s32 snto32(__u32 value, unsigned int n) +{ + if (!value || !n) + return 0; + + if (n > 32) + n = 32; + + return sign_extend32(value, n - 1); +} + +/* + * Convert a signed 32-bit integer to a signed n-bit integer. + */ + +static u32 s32ton(__s32 value, unsigned int n) +{ + s32 a = value >> (n - 1); + + if (a && a != -1) + return value < 0 ? 1 << (n - 1) : (1 << (n - 1)) - 1; + return value & ((1 << n) - 1); +} + +/* * Register a new report for a device. */ @@ -95,9 +123,9 @@ static struct hid_field *hid_register_field(struct hid_report *report, unsigned return NULL; } - field = kzalloc((sizeof(struct hid_field) + - usages * sizeof(struct hid_usage) + - 3 * usages * sizeof(unsigned int)), GFP_KERNEL); + field = kvzalloc((sizeof(struct hid_field) + + usages * sizeof(struct hid_usage) + + 3 * usages * sizeof(unsigned int)), GFP_KERNEL); if (!field) return NULL; @@ -425,7 +453,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) * both this and the standard encoding. */ raw_value = item_sdata(item); if (!(raw_value & 0xfffffff0)) - parser->global.unit_exponent = hid_snto32(raw_value, 4); + parser->global.unit_exponent = snto32(raw_value, 4); else parser->global.unit_exponent = raw_value; return 0; @@ -661,7 +689,7 @@ static void hid_free_report(struct hid_report *report) kfree(report->field_entries); for (n = 0; n < report->maxfield; n++) - kfree(report->field[n]); + kvfree(report->field[n]); kfree(report); } @@ -685,7 +713,14 @@ static void hid_close_report(struct hid_device *device) INIT_LIST_HEAD(&report_enum->report_list); } - kfree(device->rdesc); + /* + * If the HID driver had a rdesc_fixup() callback, dev->rdesc + * will be allocated by hid-core and needs to be freed. + * Otherwise, it is either equal to dev_rdesc or bpf_rdesc, in + * which cases it'll be freed later on device removal or destroy. + */ + if (device->rdesc != device->dev_rdesc && device->rdesc != device->bpf_rdesc) + kfree(device->rdesc); device->rdesc = NULL; device->rsize = 0; @@ -698,6 +733,14 @@ static void hid_close_report(struct hid_device *device) device->status &= ~HID_STAT_PARSED; } +static inline void hid_free_bpf_rdesc(struct hid_device *hdev) +{ + /* bpf_rdesc is either equal to dev_rdesc or allocated by call_hid_bpf_rdesc_fixup() */ + if (hdev->bpf_rdesc != hdev->dev_rdesc) + kfree(hdev->bpf_rdesc); + hdev->bpf_rdesc = NULL; +} + /* * Free a device structure, all reports, and all fields. */ @@ -707,6 +750,7 @@ void hiddev_free(struct kref *ref) struct hid_device *hid = container_of(ref, struct hid_device, ref); hid_close_report(hid); + hid_free_bpf_rdesc(hid); kfree(hid->dev_rdesc); kfree(hid); } @@ -723,7 +767,7 @@ static void hid_device_release(struct device *dev) * items, though they are not used yet. */ -static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item) +static const u8 *fetch_item(const __u8 *start, const __u8 *end, struct hid_item *item) { u8 b; @@ -754,35 +798,29 @@ static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item) } item->format = HID_ITEM_FORMAT_SHORT; - item->size = b & 3; + item->size = BIT(b & 3) >> 1; /* 0, 1, 2, 3 -> 0, 1, 2, 4 */ + + if (end - start < item->size) + return NULL; switch (item->size) { case 0: - return start; + break; case 1: - if ((end - start) < 1) - return NULL; - item->data.u8 = *start++; - return start; + item->data.u8 = *start; + break; case 2: - if ((end - start) < 2) - return NULL; item->data.u16 = get_unaligned_le16(start); - start = (__u8 *)((__le16 *)start + 1); - return start; + break; - case 3: - item->size++; - if ((end - start) < 4) - return NULL; + case 4: item->data.u32 = get_unaligned_le32(start); - start = (__u8 *)((__le32 *)start + 1); - return start; + break; } - return NULL; + return start + item->size; } static void hid_scan_input_usage(struct hid_parser *parser, u32 usage) @@ -880,8 +918,8 @@ static int hid_scan_report(struct hid_device *hid) { struct hid_parser *parser; struct hid_item item; - __u8 *start = hid->dev_rdesc; - __u8 *end = start + hid->dev_rsize; + const __u8 *start = hid->dev_rdesc; + const __u8 *end = start + hid->dev_rsize; static int (*dispatch_type[])(struct hid_parser *parser, struct hid_item *item) = { hid_scan_main, @@ -946,7 +984,7 @@ static int hid_scan_report(struct hid_device *hid) * Allocate the device report as read by the bus driver. This function should * only be called from parse() in ll drivers. */ -int hid_parse_report(struct hid_device *hid, __u8 *start, unsigned size) +int hid_parse_report(struct hid_device *hid, const __u8 *start, unsigned size) { hid->dev_rdesc = kmemdup(start, size, GFP_KERNEL); if (!hid->dev_rdesc) @@ -1125,6 +1163,8 @@ static void hid_apply_multiplier(struct hid_device *hid, while (multiplier_collection->parent_idx != -1 && multiplier_collection->type != HID_COLLECTION_LOGICAL) multiplier_collection = &hid->collection[multiplier_collection->parent_idx]; + if (multiplier_collection->type != HID_COLLECTION_LOGICAL) + multiplier_collection = NULL; effective_multiplier = hid_calculate_multiplier(hid, multiplier); @@ -1204,10 +1244,9 @@ int hid_open_report(struct hid_device *device) struct hid_parser *parser; struct hid_item item; unsigned int size; - __u8 *start; - __u8 *buf; - __u8 *end; - __u8 *next; + const __u8 *start; + const __u8 *end; + const __u8 *next; int ret; int i; static int (*dispatch_type[])(struct hid_parser *parser, @@ -1221,25 +1260,34 @@ int hid_open_report(struct hid_device *device) if (WARN_ON(device->status & HID_STAT_PARSED)) return -EBUSY; - start = device->dev_rdesc; + start = device->bpf_rdesc; if (WARN_ON(!start)) return -ENODEV; - size = device->dev_rsize; + size = device->bpf_rsize; - /* call_hid_bpf_rdesc_fixup() ensures we work on a copy of rdesc */ - buf = call_hid_bpf_rdesc_fixup(device, start, &size); - if (buf == NULL) - return -ENOMEM; + if (device->driver->report_fixup) { + /* + * device->driver->report_fixup() needs to work + * on a copy of our report descriptor so it can + * change it. + */ + __u8 *buf = kmemdup(start, size, GFP_KERNEL); + + if (buf == NULL) + return -ENOMEM; - if (device->driver->report_fixup) start = device->driver->report_fixup(device, buf, &size); - else - start = buf; - start = kmemdup(start, size, GFP_KERNEL); - kfree(buf); - if (start == NULL) - return -ENOMEM; + /* + * The second kmemdup is required in case report_fixup() returns + * a static read-only memory, but we have no idea if that memory + * needs to be cleaned up or not at the end. + */ + start = kmemdup(start, size, GFP_KERNEL); + kfree(buf); + if (start == NULL) + return -ENOMEM; + } device->rdesc = start; device->rsize = size; @@ -1316,46 +1364,6 @@ alloc_err: EXPORT_SYMBOL_GPL(hid_open_report); /* - * Convert a signed n-bit integer to signed 32-bit integer. Common - * cases are done through the compiler, the screwed things has to be - * done by hand. - */ - -static s32 snto32(__u32 value, unsigned n) -{ - if (!value || !n) - return 0; - - if (n > 32) - n = 32; - - switch (n) { - case 8: return ((__s8)value); - case 16: return ((__s16)value); - case 32: return ((__s32)value); - } - return value & (1 << (n - 1)) ? value | (~0U << n) : value; -} - -s32 hid_snto32(__u32 value, unsigned n) -{ - return snto32(value, n); -} -EXPORT_SYMBOL_GPL(hid_snto32); - -/* - * Convert a signed 32-bit integer to a signed n-bit integer. - */ - -static u32 s32ton(__s32 value, unsigned n) -{ - s32 a = value >> (n - 1); - if (a && a != -1) - return value < 0 ? 1 << (n - 1) : (1 << (n - 1)) - 1; - return value & ((1 << n) - 1); -} - -/* * Extract/implement a data field from/to a little endian report (bit array). * * Code sort-of follows HID spec: @@ -1448,7 +1456,6 @@ static void implement(const struct hid_device *hid, u8 *report, hid_warn(hid, "%s() called with too large value %d (n: %d)! (%s)\n", __func__, value, n, current->comm); - WARN_ON(1); value &= m; } } @@ -1876,7 +1883,7 @@ u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags) u32 len = hid_report_len(report) + 7; - return kmalloc(len, flags); + return kzalloc(len, flags); } EXPORT_SYMBOL_GPL(hid_alloc_report_buf); @@ -1913,6 +1920,31 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value) } EXPORT_SYMBOL_GPL(hid_set_field); +struct hid_field *hid_find_field(struct hid_device *hdev, unsigned int report_type, + unsigned int application, unsigned int usage) +{ + struct list_head *report_list = &hdev->report_enum[report_type].report_list; + struct hid_report *report; + int i, j; + + list_for_each_entry(report, report_list, list) { + if (report->application != application) + continue; + + for (i = 0; i < report->maxfield; i++) { + struct hid_field *field = report->field[i]; + + for (j = 0; j < field->maxusage; j++) { + if (field->usage[j].hid == usage) + return field; + } + } + } + + return NULL; +} +EXPORT_SYMBOL_GPL(hid_find_field); + static struct hid_report *hid_get_report(struct hid_report_enum *report_enum, const u8 *data) { @@ -2026,19 +2058,10 @@ out: } EXPORT_SYMBOL_GPL(hid_report_raw_event); -/** - * hid_input_report - report data from lower layer (usb, bt...) - * - * @hid: hid device - * @type: HID report type (HID_*_REPORT) - * @data: report contents - * @size: size of data parameter - * @interrupt: distinguish between interrupt and control transfers - * - * This is data entry for lower layers. - */ -int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, - int interrupt) + +static int __hid_input_report(struct hid_device *hid, enum hid_report_type type, + u8 *data, u32 size, int interrupt, u64 source, bool from_bpf, + bool lock_already_taken) { struct hid_report_enum *report_enum; struct hid_driver *hdrv; @@ -2048,8 +2071,13 @@ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data if (!hid) return -ENODEV; - if (down_trylock(&hid->driver_input_lock)) + ret = down_trylock(&hid->driver_input_lock); + if (lock_already_taken && !ret) { + up(&hid->driver_input_lock); + return -EINVAL; + } else if (!lock_already_taken && ret) { return -EBUSY; + } if (!hid->driver) { ret = -ENODEV; @@ -2058,7 +2086,7 @@ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data report_enum = hid->report_enum + type; hdrv = hid->driver; - data = dispatch_hid_bpf_device_event(hid, type, data, &size, interrupt); + data = dispatch_hid_bpf_device_event(hid, type, data, &size, interrupt, source, from_bpf); if (IS_ERR(data)) { ret = PTR_ERR(data); goto unlock; @@ -2090,9 +2118,29 @@ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data ret = hid_report_raw_event(hid, type, data, size, interrupt); unlock: - up(&hid->driver_input_lock); + if (!lock_already_taken) + up(&hid->driver_input_lock); return ret; } + +/** + * hid_input_report - report data from lower layer (usb, bt...) + * + * @hid: hid device + * @type: HID report type (HID_*_REPORT) + * @data: report contents + * @size: size of data parameter + * @interrupt: distinguish between interrupt and control transfers + * + * This is data entry for lower layers. + */ +int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, + int interrupt) +{ + return __hid_input_report(hid, type, data, size, interrupt, 0, + false, /* from_bpf */ + false /* lock_already_taken */); +} EXPORT_SYMBOL_GPL(hid_input_report); bool hid_match_one_id(const struct hid_device *hdev, @@ -2128,9 +2176,9 @@ static bool hid_hiddev(struct hid_device *hdev) static ssize_t -read_report_descriptor(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, - char *buf, loff_t off, size_t count) +report_descriptor_read(struct file *filp, struct kobject *kobj, + const struct bin_attribute *attr, + char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); struct hid_device *hdev = to_hid_device(dev); @@ -2147,24 +2195,17 @@ read_report_descriptor(struct file *filp, struct kobject *kobj, } static ssize_t -show_country(struct device *dev, struct device_attribute *attr, - char *buf) +country_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct hid_device *hdev = to_hid_device(dev); return sprintf(buf, "%02x\n", hdev->country & 0xff); } -static struct bin_attribute dev_bin_attr_report_desc = { - .attr = { .name = "report_descriptor", .mode = 0444 }, - .read = read_report_descriptor, - .size = HID_MAX_DESCRIPTOR_SIZE, -}; +static const BIN_ATTR_RO(report_descriptor, HID_MAX_DESCRIPTOR_SIZE); -static const struct device_attribute dev_attr_country = { - .attr = { .name = "country", .mode = 0444 }, - .show = show_country, -}; +static const DEVICE_ATTR_RO(country); int hid_connect(struct hid_device *hdev, unsigned int connect_mask) { @@ -2393,6 +2434,30 @@ void hid_hw_request(struct hid_device *hdev, } EXPORT_SYMBOL_GPL(hid_hw_request); +int __hid_hw_raw_request(struct hid_device *hdev, + unsigned char reportnum, __u8 *buf, + size_t len, enum hid_report_type rtype, + enum hid_class_request reqtype, + u64 source, bool from_bpf) +{ + unsigned int max_buffer_size = HID_MAX_BUFFER_SIZE; + int ret; + + if (hdev->ll_driver->max_buffer_size) + max_buffer_size = hdev->ll_driver->max_buffer_size; + + if (len < 1 || len > max_buffer_size || !buf) + return -EINVAL; + + ret = dispatch_hid_bpf_raw_requests(hdev, reportnum, buf, len, rtype, + reqtype, source, from_bpf); + if (ret) + return ret; + + return hdev->ll_driver->raw_request(hdev, reportnum, buf, len, + rtype, reqtype); +} + /** * hid_hw_raw_request - send report request to device * @@ -2411,7 +2476,15 @@ int hid_hw_raw_request(struct hid_device *hdev, unsigned char reportnum, __u8 *buf, size_t len, enum hid_report_type rtype, enum hid_class_request reqtype) { + return __hid_hw_raw_request(hdev, reportnum, buf, len, rtype, reqtype, 0, false); +} +EXPORT_SYMBOL_GPL(hid_hw_raw_request); + +int __hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len, u64 source, + bool from_bpf) +{ unsigned int max_buffer_size = HID_MAX_BUFFER_SIZE; + int ret; if (hdev->ll_driver->max_buffer_size) max_buffer_size = hdev->ll_driver->max_buffer_size; @@ -2419,10 +2492,15 @@ int hid_hw_raw_request(struct hid_device *hdev, if (len < 1 || len > max_buffer_size || !buf) return -EINVAL; - return hdev->ll_driver->raw_request(hdev, reportnum, buf, len, - rtype, reqtype); + ret = dispatch_hid_bpf_output_report(hdev, buf, len, source, from_bpf); + if (ret) + return ret; + + if (hdev->ll_driver->output_report) + return hdev->ll_driver->output_report(hdev, buf, len); + + return -ENOSYS; } -EXPORT_SYMBOL_GPL(hid_hw_raw_request); /** * hid_hw_output_report - send output report to device @@ -2435,18 +2513,7 @@ EXPORT_SYMBOL_GPL(hid_hw_raw_request); */ int hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len) { - unsigned int max_buffer_size = HID_MAX_BUFFER_SIZE; - - if (hdev->ll_driver->max_buffer_size) - max_buffer_size = hdev->ll_driver->max_buffer_size; - - if (len < 1 || len > max_buffer_size || !buf) - return -EINVAL; - - if (hdev->ll_driver->output_report) - return hdev->ll_driver->output_report(hdev, buf, len); - - return -ENOSYS; + return __hid_hw_output_report(hdev, buf, len, 0, false); } EXPORT_SYMBOL_GPL(hid_hw_output_report); @@ -2563,7 +2630,7 @@ const struct hid_device_id *hid_match_device(struct hid_device *hdev, } EXPORT_SYMBOL_GPL(hid_match_device); -static int hid_bus_match(struct device *dev, struct device_driver *drv) +static int hid_bus_match(struct device *dev, const struct device_driver *drv) { struct hid_driver *hdrv = to_hid_driver(drv); struct hid_device *hdev = to_hid_device(dev); @@ -2608,9 +2675,10 @@ static bool hid_check_device_match(struct hid_device *hdev, /* * 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. + * hid_ignore_special_drivers or HID_QUIRK_IGNORE_SPECIAL_DRIVER + * are set or not. */ - return !hid_ignore_special_drivers; + return !hid_ignore_special_drivers && !(hdev->quirks & HID_QUIRK_IGNORE_SPECIAL_DRIVER); } static int __hid_device_probe(struct hid_device *hdev, struct hid_driver *hdrv) @@ -2618,6 +2686,18 @@ static int __hid_device_probe(struct hid_device *hdev, struct hid_driver *hdrv) const struct hid_device_id *id; int ret; + if (!hdev->bpf_rsize) { + /* in case a bpf program gets detached, we need to free the old one */ + hid_free_bpf_rdesc(hdev); + + /* keep this around so we know we called it once */ + hdev->bpf_rsize = hdev->dev_rsize; + + /* call_hid_bpf_rdesc_fixup will always return a valid pointer */ + hdev->bpf_rdesc = call_hid_bpf_rdesc_fixup(hdev, hdev->dev_rdesc, + &hdev->bpf_rsize); + } + if (!hid_check_device_match(hdev, hdrv, &id)) return -ENODEV; @@ -2715,13 +2795,13 @@ static struct attribute *hid_dev_attrs[] = { &dev_attr_modalias.attr, NULL, }; -static struct bin_attribute *hid_dev_bin_attrs[] = { - &dev_bin_attr_report_desc, +static const struct bin_attribute *hid_dev_bin_attrs[] = { + &bin_attr_report_descriptor, NULL }; static const struct attribute_group hid_dev_group = { .attrs = hid_dev_attrs, - .bin_attrs = hid_dev_bin_attrs, + .bin_attrs_new = hid_dev_bin_attrs, }; __ATTRIBUTE_GROUPS(hid_dev); @@ -2855,9 +2935,15 @@ struct hid_device *hid_allocate_device(void) mutex_init(&hdev->ll_open_lock); kref_init(&hdev->ref); - hid_bpf_device_init(hdev); + ret = hid_bpf_device_init(hdev); + if (ret) + goto out_err; return hdev; + +out_err: + hid_destroy_device(hdev); + return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(hid_allocate_device); @@ -2868,9 +2954,11 @@ static void hid_remove_device(struct hid_device *hdev) hid_debug_unregister(hdev); hdev->status &= ~HID_STAT_ADDED; } + hid_free_bpf_rdesc(hdev); kfree(hdev->dev_rdesc); hdev->dev_rdesc = NULL; hdev->dev_rsize = 0; + hdev->bpf_rsize = 0; } /** @@ -2971,9 +3059,11 @@ int hid_check_keys_pressed(struct hid_device *hid) EXPORT_SYMBOL_GPL(hid_check_keys_pressed); #ifdef CONFIG_HID_BPF -static struct hid_bpf_ops hid_ops = { +static const struct hid_ops __hid_ops = { .hid_get_report = hid_get_report, - .hid_hw_raw_request = hid_hw_raw_request, + .hid_hw_raw_request = __hid_hw_raw_request, + .hid_hw_output_report = __hid_hw_output_report, + .hid_input_report = __hid_input_report, .owner = THIS_MODULE, .bus_type = &hid_bus_type, }; @@ -2990,7 +3080,7 @@ static int __init hid_init(void) } #ifdef CONFIG_HID_BPF - hid_bpf_ops = &hid_ops; + hid_ops = &__hid_ops; #endif ret = hidraw_init(); @@ -3009,7 +3099,7 @@ err: static void __exit hid_exit(void) { #ifdef CONFIG_HID_BPF - hid_bpf_ops = NULL; + hid_ops = NULL; #endif hid_debug_exit(); hidraw_exit(); @@ -3023,4 +3113,5 @@ module_exit(hid_exit); MODULE_AUTHOR("Andreas Gal"); MODULE_AUTHOR("Vojtech Pavlik"); MODULE_AUTHOR("Jiri Kosina"); +MODULE_DESCRIPTION("HID support for Linux"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-corsair-void.c b/drivers/hid/hid-corsair-void.c new file mode 100644 index 000000000000..56e858066c3c --- /dev/null +++ b/drivers/hid/hid-corsair-void.c @@ -0,0 +1,830 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HID driver for Corsair Void headsets + * + * Copyright (C) 2023-2024 Stuart Hayhurst + */ + +/* -------------------------------------------------------------------------- */ +/* Receiver report information: (ID 100) */ +/* -------------------------------------------------------------------------- */ +/* + * When queried, the receiver reponds with 5 bytes to describe the battery + * The power button, mute button and moving the mic also trigger this report + * This includes power button + mic + connection + battery status and capacity + * The information below may not be perfect, it's been gathered through guesses + * + * 0: REPORT ID + * 100 for the battery packet + * + * 1: POWER BUTTON + (?) + * Largest bit is 1 when power button pressed + * + * 2: BATTERY CAPACITY + MIC STATUS + * Battery capacity: + * Seems to report ~54 higher than reality when charging + * Capped at 100, charging or not + * Microphone status: + * Largest bit is set to 1 when the mic is physically up + * No bits change when the mic is muted, only when physically moved + * This report is sent every time the mic is moved, no polling required + * + * 3: CONNECTION STATUS + * 16: Wired headset + * 38: Initialising + * 49: Lost connection + * 51: Disconnected, searching + * 52: Disconnected, not searching + * 177: Normal + * + * 4: BATTERY STATUS + * 0: Disconnected + * 1: Normal + * 2: Low + * 3: Critical - sent during shutdown + * 4: Fully charged + * 5: Charging + */ +/* -------------------------------------------------------------------------- */ + +/* -------------------------------------------------------------------------- */ +/* Receiver report information: (ID 102) */ +/* -------------------------------------------------------------------------- */ +/* + * When queried, the recevier responds with 4 bytes to describe the firmware + * The first 2 bytes are for the receiver, the second 2 are the headset + * The headset firmware version will be 0 if no headset is connected + * + * 0: Recevier firmware major version + * Major version of the receiver's firmware + * + * 1: Recevier firmware minor version + * Minor version of the receiver's firmware + * + * 2: Headset firmware major version + * Major version of the headset's firmware + * + * 3: Headset firmware minor version + * Minor version of the headset's firmware + */ +/* -------------------------------------------------------------------------- */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/cleanup.h> +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/power_supply.h> +#include <linux/usb.h> +#include <linux/workqueue.h> +#include <asm/byteorder.h> + +#include "hid-ids.h" + +#define CORSAIR_VOID_DEVICE(id, type) { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, (id)), \ + .driver_data = (type) } +#define CORSAIR_VOID_WIRELESS_DEVICE(id) CORSAIR_VOID_DEVICE((id), CORSAIR_VOID_WIRELESS) +#define CORSAIR_VOID_WIRED_DEVICE(id) CORSAIR_VOID_DEVICE((id), CORSAIR_VOID_WIRED) + +#define CORSAIR_VOID_STATUS_REQUEST_ID 0xC9 +#define CORSAIR_VOID_NOTIF_REQUEST_ID 0xCA +#define CORSAIR_VOID_SIDETONE_REQUEST_ID 0xFF +#define CORSAIR_VOID_STATUS_REPORT_ID 0x64 +#define CORSAIR_VOID_FIRMWARE_REPORT_ID 0x66 + +#define CORSAIR_VOID_USB_SIDETONE_REQUEST 0x1 +#define CORSAIR_VOID_USB_SIDETONE_REQUEST_TYPE 0x21 +#define CORSAIR_VOID_USB_SIDETONE_VALUE 0x200 +#define CORSAIR_VOID_USB_SIDETONE_INDEX 0xB00 + +#define CORSAIR_VOID_MIC_MASK GENMASK(7, 7) +#define CORSAIR_VOID_CAPACITY_MASK GENMASK(6, 0) + +#define CORSAIR_VOID_WIRELESS_CONNECTED 177 + +#define CORSAIR_VOID_SIDETONE_MAX_WIRELESS 55 +#define CORSAIR_VOID_SIDETONE_MAX_WIRED 4096 + +enum { + CORSAIR_VOID_WIRELESS, + CORSAIR_VOID_WIRED, +}; + +enum { + CORSAIR_VOID_BATTERY_NORMAL = 1, + CORSAIR_VOID_BATTERY_LOW = 2, + CORSAIR_VOID_BATTERY_CRITICAL = 3, + CORSAIR_VOID_BATTERY_CHARGED = 4, + CORSAIR_VOID_BATTERY_CHARGING = 5, +}; + +static enum power_supply_property corsair_void_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, + POWER_SUPPLY_PROP_SCOPE, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, +}; + +struct corsair_void_battery_data { + int status; + bool present; + int capacity; + int capacity_level; +}; + +struct corsair_void_drvdata { + struct hid_device *hid_dev; + struct device *dev; + + char *name; + bool is_wired; + unsigned int sidetone_max; + + struct corsair_void_battery_data battery_data; + bool mic_up; + bool connected; + int fw_receiver_major; + int fw_receiver_minor; + int fw_headset_major; + int fw_headset_minor; + + struct power_supply *battery; + struct power_supply_desc battery_desc; + struct mutex battery_mutex; + + struct delayed_work delayed_status_work; + struct delayed_work delayed_firmware_work; + struct work_struct battery_remove_work; + struct work_struct battery_add_work; +}; + +/* + * Functions to process receiver data +*/ + +static void corsair_void_set_wireless_status(struct corsair_void_drvdata *drvdata) +{ + struct usb_interface *usb_if = to_usb_interface(drvdata->dev->parent); + + if (drvdata->is_wired) + return; + + usb_set_wireless_status(usb_if, drvdata->connected ? + USB_WIRELESS_STATUS_CONNECTED : + USB_WIRELESS_STATUS_DISCONNECTED); +} + +static void corsair_void_set_unknown_batt(struct corsair_void_drvdata *drvdata) +{ + struct corsair_void_battery_data *battery_data = &drvdata->battery_data; + + battery_data->status = POWER_SUPPLY_STATUS_UNKNOWN; + battery_data->present = false; + battery_data->capacity = 0; + battery_data->capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; +} + +/* Reset data that may change between wireless connections */ +static void corsair_void_set_unknown_wireless_data(struct corsair_void_drvdata *drvdata) +{ + /* Only 0 out headset, receiver is always known if relevant */ + drvdata->fw_headset_major = 0; + drvdata->fw_headset_minor = 0; + + drvdata->connected = false; + drvdata->mic_up = false; + + corsair_void_set_wireless_status(drvdata); +} + +static void corsair_void_process_receiver(struct corsair_void_drvdata *drvdata, + int raw_battery_capacity, + int raw_connection_status, + int raw_battery_status) +{ + struct corsair_void_battery_data *battery_data = &drvdata->battery_data; + struct corsair_void_battery_data orig_battery_data; + + /* Save initial battery data, to compare later */ + orig_battery_data = *battery_data; + + /* Headset not connected, or it's wired */ + if (raw_connection_status != CORSAIR_VOID_WIRELESS_CONNECTED) + goto unknown_battery; + + /* Battery information unavailable */ + if (raw_battery_status == 0) + goto unknown_battery; + + /* Battery must be connected then */ + battery_data->present = true; + battery_data->capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; + + /* Set battery status */ + switch (raw_battery_status) { + case CORSAIR_VOID_BATTERY_NORMAL: + case CORSAIR_VOID_BATTERY_LOW: + case CORSAIR_VOID_BATTERY_CRITICAL: + battery_data->status = POWER_SUPPLY_STATUS_DISCHARGING; + if (raw_battery_status == CORSAIR_VOID_BATTERY_LOW) + battery_data->capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_LOW; + else if (raw_battery_status == CORSAIR_VOID_BATTERY_CRITICAL) + battery_data->capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + + break; + case CORSAIR_VOID_BATTERY_CHARGED: + battery_data->status = POWER_SUPPLY_STATUS_FULL; + break; + case CORSAIR_VOID_BATTERY_CHARGING: + battery_data->status = POWER_SUPPLY_STATUS_CHARGING; + break; + default: + hid_warn(drvdata->hid_dev, "unknown battery status '%d'", + raw_battery_status); + goto unknown_battery; + break; + } + + battery_data->capacity = raw_battery_capacity; + corsair_void_set_wireless_status(drvdata); + + goto success; +unknown_battery: + corsair_void_set_unknown_batt(drvdata); +success: + + /* Inform power supply if battery values changed */ + if (memcmp(&orig_battery_data, battery_data, sizeof(*battery_data))) { + scoped_guard(mutex, &drvdata->battery_mutex) { + if (drvdata->battery) { + power_supply_changed(drvdata->battery); + } + } + } +} + +/* + * Functions to report stored data +*/ + +static int corsair_void_battery_get_property(struct power_supply *psy, + enum power_supply_property prop, + union power_supply_propval *val) +{ + struct corsair_void_drvdata *drvdata = power_supply_get_drvdata(psy); + + switch (prop) { + case POWER_SUPPLY_PROP_SCOPE: + val->intval = POWER_SUPPLY_SCOPE_DEVICE; + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + if (!strncmp(drvdata->hid_dev->name, "Corsair ", 8)) + val->strval = drvdata->hid_dev->name + 8; + else + val->strval = drvdata->hid_dev->name; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = "Corsair"; + break; + case POWER_SUPPLY_PROP_STATUS: + val->intval = drvdata->battery_data.status; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = drvdata->battery_data.present; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = drvdata->battery_data.capacity; + break; + case POWER_SUPPLY_PROP_CAPACITY_LEVEL: + val->intval = drvdata->battery_data.capacity_level; + break; + default: + return -EINVAL; + } + + return 0; +} + +static ssize_t microphone_up_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct corsair_void_drvdata *drvdata = dev_get_drvdata(dev); + + if (!drvdata->connected) + return -ENODEV; + + return sysfs_emit(buf, "%d\n", drvdata->mic_up); +} + +static ssize_t fw_version_receiver_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct corsair_void_drvdata *drvdata = dev_get_drvdata(dev); + + if (drvdata->fw_receiver_major == 0 && drvdata->fw_receiver_minor == 0) + return -ENODATA; + + return sysfs_emit(buf, "%d.%02d\n", drvdata->fw_receiver_major, + drvdata->fw_receiver_minor); +} + + +static ssize_t fw_version_headset_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct corsair_void_drvdata *drvdata = dev_get_drvdata(dev); + + if (drvdata->fw_headset_major == 0 && drvdata->fw_headset_minor == 0) + return -ENODATA; + + return sysfs_emit(buf, "%d.%02d\n", drvdata->fw_headset_major, + drvdata->fw_headset_minor); +} + +static ssize_t sidetone_max_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct corsair_void_drvdata *drvdata = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%d\n", drvdata->sidetone_max); +} + +/* + * Functions to send data to headset +*/ + +static ssize_t send_alert_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct corsair_void_drvdata *drvdata = dev_get_drvdata(dev); + struct hid_device *hid_dev = drvdata->hid_dev; + unsigned char alert_id; + unsigned char *send_buf __free(kfree) = NULL; + int ret; + + if (!drvdata->connected || drvdata->is_wired) + return -ENODEV; + + /* Only accept 0 or 1 for alert ID */ + if (kstrtou8(buf, 10, &alert_id) || alert_id >= 2) + return -EINVAL; + + send_buf = kmalloc(3, GFP_KERNEL); + if (!send_buf) + return -ENOMEM; + + /* Packet format to send alert with ID alert_id */ + send_buf[0] = CORSAIR_VOID_NOTIF_REQUEST_ID; + send_buf[1] = 0x02; + send_buf[2] = alert_id; + + ret = hid_hw_raw_request(hid_dev, CORSAIR_VOID_NOTIF_REQUEST_ID, + send_buf, 3, HID_OUTPUT_REPORT, + HID_REQ_SET_REPORT); + if (ret < 0) + hid_warn(hid_dev, "failed to send alert request (reason: %d)", + ret); + else + ret = count; + + return ret; +} + +static int corsair_void_set_sidetone_wired(struct device *dev, const char *buf, + unsigned int sidetone) +{ + struct usb_interface *usb_if = to_usb_interface(dev->parent); + struct usb_device *usb_dev = interface_to_usbdev(usb_if); + + /* Packet format to set sidetone for wired headsets */ + __le16 sidetone_le = cpu_to_le16(sidetone); + + return usb_control_msg_send(usb_dev, 0, + CORSAIR_VOID_USB_SIDETONE_REQUEST, + CORSAIR_VOID_USB_SIDETONE_REQUEST_TYPE, + CORSAIR_VOID_USB_SIDETONE_VALUE, + CORSAIR_VOID_USB_SIDETONE_INDEX, + &sidetone_le, 2, USB_CTRL_SET_TIMEOUT, + GFP_KERNEL); +} + +static int corsair_void_set_sidetone_wireless(struct device *dev, + const char *buf, + unsigned char sidetone) +{ + struct corsair_void_drvdata *drvdata = dev_get_drvdata(dev); + struct hid_device *hid_dev = drvdata->hid_dev; + unsigned char *send_buf __free(kfree) = NULL; + + send_buf = kmalloc(12, GFP_KERNEL); + if (!send_buf) + return -ENOMEM; + + /* Packet format to set sidetone for wireless headsets */ + send_buf[0] = CORSAIR_VOID_SIDETONE_REQUEST_ID; + send_buf[1] = 0x0B; + send_buf[2] = 0x00; + send_buf[3] = 0xFF; + send_buf[4] = 0x04; + send_buf[5] = 0x0E; + send_buf[6] = 0xFF; + send_buf[7] = 0x05; + send_buf[8] = 0x01; + send_buf[9] = 0x04; + send_buf[10] = 0x00; + send_buf[11] = sidetone + 200; + + return hid_hw_raw_request(hid_dev, CORSAIR_VOID_SIDETONE_REQUEST_ID, + send_buf, 12, HID_FEATURE_REPORT, + HID_REQ_SET_REPORT); +} + +static ssize_t set_sidetone_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct corsair_void_drvdata *drvdata = dev_get_drvdata(dev); + struct hid_device *hid_dev = drvdata->hid_dev; + unsigned int sidetone; + int ret; + + if (!drvdata->connected) + return -ENODEV; + + /* sidetone must be between 0 and drvdata->sidetone_max inclusive */ + if (kstrtouint(buf, 10, &sidetone) || sidetone > drvdata->sidetone_max) + return -EINVAL; + + if (drvdata->is_wired) + ret = corsair_void_set_sidetone_wired(dev, buf, sidetone); + else + ret = corsair_void_set_sidetone_wireless(dev, buf, sidetone); + + if (ret < 0) + hid_warn(hid_dev, "failed to send sidetone (reason: %d)", ret); + else + ret = count; + + return ret; +} + +static int corsair_void_request_status(struct hid_device *hid_dev, int id) +{ + unsigned char *send_buf __free(kfree) = NULL; + + send_buf = kmalloc(2, GFP_KERNEL); + if (!send_buf) + return -ENOMEM; + + /* Packet format to request data item (status / firmware) refresh */ + send_buf[0] = CORSAIR_VOID_STATUS_REQUEST_ID; + send_buf[1] = id; + + /* Send request for data refresh */ + return hid_hw_raw_request(hid_dev, CORSAIR_VOID_STATUS_REQUEST_ID, + send_buf, 2, HID_OUTPUT_REPORT, + HID_REQ_SET_REPORT); +} + +/* + * Headset connect / disconnect handlers and work handlers +*/ + +static void corsair_void_status_work_handler(struct work_struct *work) +{ + struct corsair_void_drvdata *drvdata; + struct delayed_work *delayed_work; + int battery_ret; + + delayed_work = container_of(work, struct delayed_work, work); + drvdata = container_of(delayed_work, struct corsair_void_drvdata, + delayed_status_work); + + battery_ret = corsair_void_request_status(drvdata->hid_dev, + CORSAIR_VOID_STATUS_REPORT_ID); + if (battery_ret < 0) { + hid_warn(drvdata->hid_dev, + "failed to request battery (reason: %d)", battery_ret); + } +} + +static void corsair_void_firmware_work_handler(struct work_struct *work) +{ + struct corsair_void_drvdata *drvdata; + struct delayed_work *delayed_work; + int firmware_ret; + + delayed_work = container_of(work, struct delayed_work, work); + drvdata = container_of(delayed_work, struct corsair_void_drvdata, + delayed_firmware_work); + + firmware_ret = corsair_void_request_status(drvdata->hid_dev, + CORSAIR_VOID_FIRMWARE_REPORT_ID); + if (firmware_ret < 0) { + hid_warn(drvdata->hid_dev, + "failed to request firmware (reason: %d)", firmware_ret); + } + +} + +static void corsair_void_battery_remove_work_handler(struct work_struct *work) +{ + struct corsair_void_drvdata *drvdata; + + drvdata = container_of(work, struct corsair_void_drvdata, + battery_remove_work); + scoped_guard(mutex, &drvdata->battery_mutex) { + if (drvdata->battery) { + power_supply_unregister(drvdata->battery); + drvdata->battery = NULL; + } + } +} + +static void corsair_void_battery_add_work_handler(struct work_struct *work) +{ + struct corsair_void_drvdata *drvdata; + struct power_supply_config psy_cfg = {}; + struct power_supply *new_supply; + + drvdata = container_of(work, struct corsair_void_drvdata, + battery_add_work); + guard(mutex)(&drvdata->battery_mutex); + if (drvdata->battery) + return; + + psy_cfg.drv_data = drvdata; + new_supply = power_supply_register(drvdata->dev, + &drvdata->battery_desc, + &psy_cfg); + + if (IS_ERR(new_supply)) { + hid_err(drvdata->hid_dev, + "failed to register battery '%s' (reason: %ld)\n", + drvdata->battery_desc.name, + PTR_ERR(new_supply)); + return; + } + + if (power_supply_powers(new_supply, drvdata->dev)) { + power_supply_unregister(new_supply); + return; + } + + drvdata->battery = new_supply; +} + +static void corsair_void_headset_connected(struct corsair_void_drvdata *drvdata) +{ + schedule_work(&drvdata->battery_add_work); + schedule_delayed_work(&drvdata->delayed_firmware_work, + msecs_to_jiffies(100)); +} + +static void corsair_void_headset_disconnected(struct corsair_void_drvdata *drvdata) +{ + schedule_work(&drvdata->battery_remove_work); + + corsair_void_set_unknown_wireless_data(drvdata); + corsair_void_set_unknown_batt(drvdata); +} + +/* + * Driver setup, probing and HID event handling +*/ + +static DEVICE_ATTR_RO(fw_version_receiver); +static DEVICE_ATTR_RO(fw_version_headset); +static DEVICE_ATTR_RO(microphone_up); +static DEVICE_ATTR_RO(sidetone_max); + +static DEVICE_ATTR_WO(send_alert); +static DEVICE_ATTR_WO(set_sidetone); + +static struct attribute *corsair_void_attrs[] = { + &dev_attr_fw_version_receiver.attr, + &dev_attr_fw_version_headset.attr, + &dev_attr_microphone_up.attr, + &dev_attr_send_alert.attr, + &dev_attr_set_sidetone.attr, + &dev_attr_sidetone_max.attr, + NULL, +}; + +static const struct attribute_group corsair_void_attr_group = { + .attrs = corsair_void_attrs, +}; + +static int corsair_void_probe(struct hid_device *hid_dev, + const struct hid_device_id *hid_id) +{ + int ret; + struct corsair_void_drvdata *drvdata; + char *name; + + if (!hid_is_usb(hid_dev)) + return -EINVAL; + + drvdata = devm_kzalloc(&hid_dev->dev, sizeof(*drvdata), + GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + hid_set_drvdata(hid_dev, drvdata); + dev_set_drvdata(&hid_dev->dev, drvdata); + + drvdata->dev = &hid_dev->dev; + drvdata->hid_dev = hid_dev; + drvdata->is_wired = hid_id->driver_data == CORSAIR_VOID_WIRED; + + drvdata->sidetone_max = CORSAIR_VOID_SIDETONE_MAX_WIRELESS; + if (drvdata->is_wired) + drvdata->sidetone_max = CORSAIR_VOID_SIDETONE_MAX_WIRED; + + /* Set initial values for no wireless headset attached */ + /* If a headset is attached, it'll be prompted later */ + corsair_void_set_unknown_wireless_data(drvdata); + corsair_void_set_unknown_batt(drvdata); + + /* Receiver version won't be reset after init */ + /* Headset version already set via set_unknown_wireless_data */ + drvdata->fw_receiver_major = 0; + drvdata->fw_receiver_minor = 0; + + ret = hid_parse(hid_dev); + if (ret) { + hid_err(hid_dev, "parse failed (reason: %d)\n", ret); + return ret; + } + + name = devm_kasprintf(drvdata->dev, GFP_KERNEL, + "corsair-void-%d-battery", hid_dev->id); + if (!name) + return -ENOMEM; + + drvdata->battery_desc.name = name; + drvdata->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY; + drvdata->battery_desc.properties = corsair_void_battery_props; + drvdata->battery_desc.num_properties = ARRAY_SIZE(corsair_void_battery_props); + drvdata->battery_desc.get_property = corsair_void_battery_get_property; + + drvdata->battery = NULL; + INIT_WORK(&drvdata->battery_remove_work, + corsair_void_battery_remove_work_handler); + INIT_WORK(&drvdata->battery_add_work, + corsair_void_battery_add_work_handler); + ret = devm_mutex_init(drvdata->dev, &drvdata->battery_mutex); + if (ret) + return ret; + + ret = sysfs_create_group(&hid_dev->dev.kobj, &corsair_void_attr_group); + if (ret) + return ret; + + /* Any failures after here will need to call hid_hw_stop */ + ret = hid_hw_start(hid_dev, HID_CONNECT_DEFAULT); + if (ret) { + hid_err(hid_dev, "hid_hw_start failed (reason: %d)\n", ret); + goto failed_after_sysfs; + } + + /* Refresh battery data, in case wireless headset is already connected */ + INIT_DELAYED_WORK(&drvdata->delayed_status_work, + corsair_void_status_work_handler); + schedule_delayed_work(&drvdata->delayed_status_work, + msecs_to_jiffies(100)); + + /* Refresh firmware versions */ + INIT_DELAYED_WORK(&drvdata->delayed_firmware_work, + corsair_void_firmware_work_handler); + schedule_delayed_work(&drvdata->delayed_firmware_work, + msecs_to_jiffies(100)); + + return 0; + +failed_after_sysfs: + sysfs_remove_group(&hid_dev->dev.kobj, &corsair_void_attr_group); + return ret; +} + +static void corsair_void_remove(struct hid_device *hid_dev) +{ + struct corsair_void_drvdata *drvdata = hid_get_drvdata(hid_dev); + + hid_hw_stop(hid_dev); + cancel_work_sync(&drvdata->battery_remove_work); + cancel_work_sync(&drvdata->battery_add_work); + if (drvdata->battery) + power_supply_unregister(drvdata->battery); + + cancel_delayed_work_sync(&drvdata->delayed_status_work); + cancel_delayed_work_sync(&drvdata->delayed_firmware_work); + sysfs_remove_group(&hid_dev->dev.kobj, &corsair_void_attr_group); +} + +static int corsair_void_raw_event(struct hid_device *hid_dev, + struct hid_report *hid_report, + u8 *data, int size) +{ + struct corsair_void_drvdata *drvdata = hid_get_drvdata(hid_dev); + bool was_connected = drvdata->connected; + + /* Description of packets are documented at the top of this file */ + if (hid_report->id == CORSAIR_VOID_STATUS_REPORT_ID) { + drvdata->mic_up = FIELD_GET(CORSAIR_VOID_MIC_MASK, data[2]); + drvdata->connected = (data[3] == CORSAIR_VOID_WIRELESS_CONNECTED) || + drvdata->is_wired; + + corsair_void_process_receiver(drvdata, + FIELD_GET(CORSAIR_VOID_CAPACITY_MASK, data[2]), + data[3], data[4]); + } else if (hid_report->id == CORSAIR_VOID_FIRMWARE_REPORT_ID) { + drvdata->fw_receiver_major = data[1]; + drvdata->fw_receiver_minor = data[2]; + drvdata->fw_headset_major = data[3]; + drvdata->fw_headset_minor = data[4]; + } + + /* Handle wireless headset connect / disconnect */ + if ((was_connected != drvdata->connected) && !drvdata->is_wired) { + if (drvdata->connected) + corsair_void_headset_connected(drvdata); + else + corsair_void_headset_disconnected(drvdata); + } + + return 0; +} + +static const struct hid_device_id corsair_void_devices[] = { + /* Corsair Void Wireless */ + CORSAIR_VOID_WIRELESS_DEVICE(0x0a0c), + CORSAIR_VOID_WIRELESS_DEVICE(0x0a2b), + CORSAIR_VOID_WIRELESS_DEVICE(0x1b23), + CORSAIR_VOID_WIRELESS_DEVICE(0x1b25), + CORSAIR_VOID_WIRELESS_DEVICE(0x1b27), + + /* Corsair Void USB */ + CORSAIR_VOID_WIRED_DEVICE(0x0a0f), + CORSAIR_VOID_WIRED_DEVICE(0x1b1c), + CORSAIR_VOID_WIRED_DEVICE(0x1b29), + CORSAIR_VOID_WIRED_DEVICE(0x1b2a), + + /* Corsair Void Surround */ + CORSAIR_VOID_WIRED_DEVICE(0x0a30), + CORSAIR_VOID_WIRED_DEVICE(0x0a31), + + /* Corsair Void Pro Wireless */ + CORSAIR_VOID_WIRELESS_DEVICE(0x0a14), + CORSAIR_VOID_WIRELESS_DEVICE(0x0a16), + CORSAIR_VOID_WIRELESS_DEVICE(0x0a1a), + + /* Corsair Void Pro USB */ + CORSAIR_VOID_WIRED_DEVICE(0x0a17), + CORSAIR_VOID_WIRED_DEVICE(0x0a1d), + + /* Corsair Void Pro Surround */ + CORSAIR_VOID_WIRED_DEVICE(0x0a18), + CORSAIR_VOID_WIRED_DEVICE(0x0a1e), + CORSAIR_VOID_WIRED_DEVICE(0x0a1f), + + /* Corsair Void Elite Wireless */ + CORSAIR_VOID_WIRELESS_DEVICE(0x0a51), + CORSAIR_VOID_WIRELESS_DEVICE(0x0a55), + CORSAIR_VOID_WIRELESS_DEVICE(0x0a75), + + /* Corsair Void Elite USB */ + CORSAIR_VOID_WIRED_DEVICE(0x0a52), + CORSAIR_VOID_WIRED_DEVICE(0x0a56), + + /* Corsair Void Elite Surround */ + CORSAIR_VOID_WIRED_DEVICE(0x0a53), + CORSAIR_VOID_WIRED_DEVICE(0x0a57), + + {} +}; + +MODULE_DEVICE_TABLE(hid, corsair_void_devices); + +static struct hid_driver corsair_void_driver = { + .name = "hid-corsair-void", + .id_table = corsair_void_devices, + .probe = corsair_void_probe, + .remove = corsair_void_remove, + .raw_event = corsair_void_raw_event, +}; + +module_hid_driver(corsair_void_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Stuart Hayhurst <stuart.a.hayhurst@gmail.com>"); +MODULE_DESCRIPTION("HID driver for Corsair Void headsets"); diff --git a/drivers/hid/hid-corsair.c b/drivers/hid/hid-corsair.c index 8c895c820b67..62b99f5c3cf8 100644 --- a/drivers/hid/hid-corsair.c +++ b/drivers/hid/hid-corsair.c @@ -298,7 +298,7 @@ static ssize_t k90_show_macro_mode(struct device *dev, goto out; } - ret = snprintf(buf, PAGE_SIZE, "%s\n", macro_mode); + ret = sysfs_emit(buf, "%s\n", macro_mode); out: kfree(data); @@ -367,7 +367,7 @@ static ssize_t k90_show_current_profile(struct device *dev, goto out; } - ret = snprintf(buf, PAGE_SIZE, "%d\n", current_profile); + ret = sysfs_emit(buf, "%d\n", current_profile); out: kfree(data); @@ -690,8 +690,8 @@ static int corsair_input_mapping(struct hid_device *dev, * - USB ID 1b1c:1b3e, sold as Scimitar RGB Pro Gaming mouse */ -static __u8 *corsair_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) +static const __u8 *corsair_mouse_report_fixup(struct hid_device *hdev, + __u8 *rdesc, unsigned int *rsize) { struct usb_interface *intf = to_usb_interface(hdev->dev.parent); diff --git a/drivers/hid/hid-cougar.c b/drivers/hid/hid-cougar.c index cb8bd8aae15b..5596dd940322 100644 --- a/drivers/hid/hid-cougar.c +++ b/drivers/hid/hid-cougar.c @@ -103,10 +103,10 @@ static void cougar_fix_g6_mapping(void) /* * Constant-friendly rdesc fixup for mouse interface */ -static __u8 *cougar_report_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) +static const __u8 *cougar_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) { - if (rdesc[2] == 0x09 && rdesc[3] == 0x02 && + if (*rsize >= 117 && rdesc[2] == 0x09 && rdesc[3] == 0x02 && (rdesc[115] | rdesc[116] << 8) >= HID_MAX_USAGES) { hid_info(hdev, "usage count exceeds max: fixing up report descriptor\n"); diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c index 20a0d1315d90..f4c8d981aa0a 100644 --- a/drivers/hid/hid-cp2112.c +++ b/drivers/hid/hid-cp2112.c @@ -852,7 +852,8 @@ static int cp2112_set_usb_config(struct hid_device *hdev, { int ret; - BUG_ON(cfg->report != CP2112_USB_CONFIG); + if (WARN_ON(cfg->report != CP2112_USB_CONFIG)) + return -EINVAL; ret = cp2112_hid_output(hdev, (u8 *)cfg, sizeof(*cfg), HID_FEATURE_REPORT); @@ -1094,7 +1095,6 @@ static void cp2112_gpio_poll_callback(struct work_struct *work) { struct cp2112_device *dev = container_of(work, struct cp2112_device, gpio_poll_worker.work); - struct irq_data *d; u8 gpio_mask; u32 irq_type; int irq, virq, ret; @@ -1111,12 +1111,10 @@ static void cp2112_gpio_poll_callback(struct work_struct *work) if (!irq) continue; - d = irq_get_irq_data(irq); - if (!d) + irq_type = irq_get_trigger_type(irq); + if (!irq_type) continue; - irq_type = irqd_get_trigger_type(d); - if (gpio_mask & BIT(virq)) { /* Level High */ diff --git a/drivers/hid/hid-cypress.c b/drivers/hid/hid-cypress.c index b88f889b3932..98548201feec 100644 --- a/drivers/hid/hid-cypress.c +++ b/drivers/hid/hid-cypress.c @@ -67,7 +67,7 @@ static __u8 *va_logical_boundary_fixup(struct hid_device *hdev, __u8 *rdesc, return rdesc; } -static __u8 *cp_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *cp_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); @@ -176,4 +176,5 @@ static struct hid_driver cp_driver = { }; module_hid_driver(cp_driver); +MODULE_DESCRIPTION("HID driver for some cypress \"special\" devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 7dd83ec74f8a..541d682af15a 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -37,437 +37,2808 @@ struct hid_usage_entry { }; static const struct hid_usage_entry hid_usage_table[] = { - { 0, 0, "Undefined" }, - { 1, 0, "GenericDesktop" }, - {0, 0x01, "Pointer"}, - {0, 0x02, "Mouse"}, - {0, 0x04, "Joystick"}, - {0, 0x05, "GamePad"}, - {0, 0x06, "Keyboard"}, - {0, 0x07, "Keypad"}, - {0, 0x08, "MultiAxis"}, - {0, 0x30, "X"}, - {0, 0x31, "Y"}, - {0, 0x32, "Z"}, - {0, 0x33, "Rx"}, - {0, 0x34, "Ry"}, - {0, 0x35, "Rz"}, - {0, 0x36, "Slider"}, - {0, 0x37, "Dial"}, - {0, 0x38, "Wheel"}, - {0, 0x39, "HatSwitch"}, - {0, 0x3a, "CountedBuffer"}, - {0, 0x3b, "ByteCount"}, - {0, 0x3c, "MotionWakeup"}, - {0, 0x3d, "Start"}, - {0, 0x3e, "Select"}, - {0, 0x40, "Vx"}, - {0, 0x41, "Vy"}, - {0, 0x42, "Vz"}, - {0, 0x43, "Vbrx"}, - {0, 0x44, "Vbry"}, - {0, 0x45, "Vbrz"}, - {0, 0x46, "Vno"}, - {0, 0x80, "SystemControl"}, - {0, 0x81, "SystemPowerDown"}, - {0, 0x82, "SystemSleep"}, - {0, 0x83, "SystemWakeUp"}, - {0, 0x84, "SystemContextMenu"}, - {0, 0x85, "SystemMainMenu"}, - {0, 0x86, "SystemAppMenu"}, - {0, 0x87, "SystemMenuHelp"}, - {0, 0x88, "SystemMenuExit"}, - {0, 0x89, "SystemMenuSelect"}, - {0, 0x8a, "SystemMenuRight"}, - {0, 0x8b, "SystemMenuLeft"}, - {0, 0x8c, "SystemMenuUp"}, - {0, 0x8d, "SystemMenuDown"}, - {0, 0x90, "D-PadUp"}, - {0, 0x91, "D-PadDown"}, - {0, 0x92, "D-PadRight"}, - {0, 0x93, "D-PadLeft"}, - { 2, 0, "Simulation" }, - {0, 0xb0, "Aileron"}, - {0, 0xb1, "AileronTrim"}, - {0, 0xb2, "Anti-Torque"}, - {0, 0xb3, "Autopilot"}, - {0, 0xb4, "Chaff"}, - {0, 0xb5, "Collective"}, - {0, 0xb6, "DiveBrake"}, - {0, 0xb7, "ElectronicCountermeasures"}, - {0, 0xb8, "Elevator"}, - {0, 0xb9, "ElevatorTrim"}, - {0, 0xba, "Rudder"}, - {0, 0xbb, "Throttle"}, - {0, 0xbc, "FlightCommunications"}, - {0, 0xbd, "FlareRelease"}, - {0, 0xbe, "LandingGear"}, - {0, 0xbf, "ToeBrake"}, - { 6, 0, "GenericDeviceControls" }, - {0, 0x20, "BatteryStrength" }, - {0, 0x21, "WirelessChannel" }, - {0, 0x22, "WirelessID" }, - {0, 0x23, "DiscoverWirelessControl" }, - {0, 0x24, "SecurityCodeCharacterEntered" }, - {0, 0x25, "SecurityCodeCharactedErased" }, - {0, 0x26, "SecurityCodeCleared" }, - { 7, 0, "Keyboard" }, - { 8, 0, "LED" }, - {0, 0x01, "NumLock"}, - {0, 0x02, "CapsLock"}, - {0, 0x03, "ScrollLock"}, - {0, 0x04, "Compose"}, - {0, 0x05, "Kana"}, - {0, 0x4b, "GenericIndicator"}, - { 9, 0, "Button" }, - { 10, 0, "Ordinal" }, - { 12, 0, "Consumer" }, - {0, 0x003, "ProgrammableButtons"}, - {0, 0x238, "HorizontalWheel"}, - { 13, 0, "Digitizers" }, - {0, 0x01, "Digitizer"}, - {0, 0x02, "Pen"}, - {0, 0x03, "LightPen"}, - {0, 0x04, "TouchScreen"}, - {0, 0x05, "TouchPad"}, - {0, 0x0e, "DeviceConfiguration"}, - {0, 0x20, "Stylus"}, - {0, 0x21, "Puck"}, - {0, 0x22, "Finger"}, - {0, 0x23, "DeviceSettings"}, - {0, 0x30, "TipPressure"}, - {0, 0x31, "BarrelPressure"}, - {0, 0x32, "InRange"}, - {0, 0x33, "Touch"}, - {0, 0x34, "UnTouch"}, - {0, 0x35, "Tap"}, - {0, 0x38, "Transducer Index"}, - {0, 0x39, "TabletFunctionKey"}, - {0, 0x3a, "ProgramChangeKey"}, - {0, 0x3B, "Battery Strength"}, - {0, 0x3c, "Invert"}, - {0, 0x42, "TipSwitch"}, - {0, 0x43, "SecondaryTipSwitch"}, - {0, 0x44, "BarrelSwitch"}, - {0, 0x45, "Eraser"}, - {0, 0x46, "TabletPick"}, - {0, 0x47, "Confidence"}, - {0, 0x48, "Width"}, - {0, 0x49, "Height"}, - {0, 0x51, "ContactID"}, - {0, 0x52, "InputMode"}, - {0, 0x53, "DeviceIndex"}, - {0, 0x54, "ContactCount"}, - {0, 0x55, "ContactMaximumNumber"}, - {0, 0x59, "ButtonType"}, - {0, 0x5A, "SecondaryBarrelSwitch"}, - {0, 0x5B, "TransducerSerialNumber"}, - {0, 0x5C, "Preferred Color"}, - {0, 0x5D, "Preferred Color is Locked"}, - {0, 0x5E, "Preferred Line Width"}, - {0, 0x5F, "Preferred Line Width is Locked"}, - {0, 0x6e, "TransducerSerialNumber2"}, - {0, 0x70, "Preferred Line Style"}, - {0, 0x71, "Preferred Line Style is Locked"}, - {0, 0x72, "Ink"}, - {0, 0x73, "Pencil"}, - {0, 0x74, "Highlighter"}, - {0, 0x75, "Chisel Marker"}, - {0, 0x76, "Brush"}, - {0, 0x77, "No Preference"}, - {0, 0x80, "Digitizer Diagnostic"}, - {0, 0x81, "Digitizer Error"}, - {0, 0x82, "Err Normal Status"}, - {0, 0x83, "Err Transducers Exceeded"}, - {0, 0x84, "Err Full Trans Features Unavailable"}, - {0, 0x85, "Err Charge Low"}, - {0, 0x90, "Transducer Software Info"}, - {0, 0x91, "Transducer Vendor Id"}, - {0, 0x92, "Transducer Product Id"}, - {0, 0x93, "Device Supported Protocols"}, - {0, 0x94, "Transducer Supported Protocols"}, - {0, 0x95, "No Protocol"}, - {0, 0x96, "Wacom AES Protocol"}, - {0, 0x97, "USI Protocol"}, - {0, 0x98, "Microsoft Pen Protocol"}, - {0, 0xA0, "Supported Report Rates"}, - {0, 0xA1, "Report Rate"}, - {0, 0xA2, "Transducer Connected"}, - {0, 0xA3, "Switch Disabled"}, - {0, 0xA4, "Switch Unimplemented"}, - {0, 0xA5, "Transducer Switches"}, - { 15, 0, "PhysicalInterfaceDevice" }, - {0, 0x00, "Undefined"}, - {0, 0x01, "Physical_Interface_Device"}, - {0, 0x20, "Normal"}, - {0, 0x21, "Set_Effect_Report"}, - {0, 0x22, "Effect_Block_Index"}, - {0, 0x23, "Parameter_Block_Offset"}, - {0, 0x24, "ROM_Flag"}, - {0, 0x25, "Effect_Type"}, - {0, 0x26, "ET_Constant_Force"}, - {0, 0x27, "ET_Ramp"}, - {0, 0x28, "ET_Custom_Force_Data"}, - {0, 0x30, "ET_Square"}, - {0, 0x31, "ET_Sine"}, - {0, 0x32, "ET_Triangle"}, - {0, 0x33, "ET_Sawtooth_Up"}, - {0, 0x34, "ET_Sawtooth_Down"}, - {0, 0x40, "ET_Spring"}, - {0, 0x41, "ET_Damper"}, - {0, 0x42, "ET_Inertia"}, - {0, 0x43, "ET_Friction"}, - {0, 0x50, "Duration"}, - {0, 0x51, "Sample_Period"}, - {0, 0x52, "Gain"}, - {0, 0x53, "Trigger_Button"}, - {0, 0x54, "Trigger_Repeat_Interval"}, - {0, 0x55, "Axes_Enable"}, - {0, 0x56, "Direction_Enable"}, - {0, 0x57, "Direction"}, - {0, 0x58, "Type_Specific_Block_Offset"}, - {0, 0x59, "Block_Type"}, - {0, 0x5A, "Set_Envelope_Report"}, - {0, 0x5B, "Attack_Level"}, - {0, 0x5C, "Attack_Time"}, - {0, 0x5D, "Fade_Level"}, - {0, 0x5E, "Fade_Time"}, - {0, 0x5F, "Set_Condition_Report"}, - {0, 0x60, "CP_Offset"}, - {0, 0x61, "Positive_Coefficient"}, - {0, 0x62, "Negative_Coefficient"}, - {0, 0x63, "Positive_Saturation"}, - {0, 0x64, "Negative_Saturation"}, - {0, 0x65, "Dead_Band"}, - {0, 0x66, "Download_Force_Sample"}, - {0, 0x67, "Isoch_Custom_Force_Enable"}, - {0, 0x68, "Custom_Force_Data_Report"}, - {0, 0x69, "Custom_Force_Data"}, - {0, 0x6A, "Custom_Force_Vendor_Defined_Data"}, - {0, 0x6B, "Set_Custom_Force_Report"}, - {0, 0x6C, "Custom_Force_Data_Offset"}, - {0, 0x6D, "Sample_Count"}, - {0, 0x6E, "Set_Periodic_Report"}, - {0, 0x6F, "Offset"}, - {0, 0x70, "Magnitude"}, - {0, 0x71, "Phase"}, - {0, 0x72, "Period"}, - {0, 0x73, "Set_Constant_Force_Report"}, - {0, 0x74, "Set_Ramp_Force_Report"}, - {0, 0x75, "Ramp_Start"}, - {0, 0x76, "Ramp_End"}, - {0, 0x77, "Effect_Operation_Report"}, - {0, 0x78, "Effect_Operation"}, - {0, 0x79, "Op_Effect_Start"}, - {0, 0x7A, "Op_Effect_Start_Solo"}, - {0, 0x7B, "Op_Effect_Stop"}, - {0, 0x7C, "Loop_Count"}, - {0, 0x7D, "Device_Gain_Report"}, - {0, 0x7E, "Device_Gain"}, - {0, 0x7F, "PID_Pool_Report"}, - {0, 0x80, "RAM_Pool_Size"}, - {0, 0x81, "ROM_Pool_Size"}, - {0, 0x82, "ROM_Effect_Block_Count"}, - {0, 0x83, "Simultaneous_Effects_Max"}, - {0, 0x84, "Pool_Alignment"}, - {0, 0x85, "PID_Pool_Move_Report"}, - {0, 0x86, "Move_Source"}, - {0, 0x87, "Move_Destination"}, - {0, 0x88, "Move_Length"}, - {0, 0x89, "PID_Block_Load_Report"}, - {0, 0x8B, "Block_Load_Status"}, - {0, 0x8C, "Block_Load_Success"}, - {0, 0x8D, "Block_Load_Full"}, - {0, 0x8E, "Block_Load_Error"}, - {0, 0x8F, "Block_Handle"}, - {0, 0x90, "PID_Block_Free_Report"}, - {0, 0x91, "Type_Specific_Block_Handle"}, - {0, 0x92, "PID_State_Report"}, - {0, 0x94, "Effect_Playing"}, - {0, 0x95, "PID_Device_Control_Report"}, - {0, 0x96, "PID_Device_Control"}, - {0, 0x97, "DC_Enable_Actuators"}, - {0, 0x98, "DC_Disable_Actuators"}, - {0, 0x99, "DC_Stop_All_Effects"}, - {0, 0x9A, "DC_Device_Reset"}, - {0, 0x9B, "DC_Device_Pause"}, - {0, 0x9C, "DC_Device_Continue"}, - {0, 0x9F, "Device_Paused"}, - {0, 0xA0, "Actuators_Enabled"}, - {0, 0xA4, "Safety_Switch"}, - {0, 0xA5, "Actuator_Override_Switch"}, - {0, 0xA6, "Actuator_Power"}, - {0, 0xA7, "Start_Delay"}, - {0, 0xA8, "Parameter_Block_Size"}, - {0, 0xA9, "Device_Managed_Pool"}, - {0, 0xAA, "Shared_Parameter_Blocks"}, - {0, 0xAB, "Create_New_Effect_Report"}, - {0, 0xAC, "RAM_Pool_Available"}, - { 0x20, 0, "Sensor" }, - { 0x20, 0x01, "Sensor" }, - { 0x20, 0x10, "Biometric" }, - { 0x20, 0x11, "BiometricHumanPresence" }, - { 0x20, 0x12, "BiometricHumanProximity" }, - { 0x20, 0x13, "BiometricHumanTouch" }, - { 0x20, 0x20, "Electrical" }, - { 0x20, 0x21, "ElectricalCapacitance" }, - { 0x20, 0x22, "ElectricalCurrent" }, - { 0x20, 0x23, "ElectricalPower" }, - { 0x20, 0x24, "ElectricalInductance" }, - { 0x20, 0x25, "ElectricalResistance" }, - { 0x20, 0x26, "ElectricalVoltage" }, - { 0x20, 0x27, "ElectricalPoteniometer" }, - { 0x20, 0x28, "ElectricalFrequency" }, - { 0x20, 0x29, "ElectricalPeriod" }, - { 0x20, 0x30, "Environmental" }, - { 0x20, 0x31, "EnvironmentalAtmosphericPressure" }, - { 0x20, 0x32, "EnvironmentalHumidity" }, - { 0x20, 0x33, "EnvironmentalTemperature" }, - { 0x20, 0x34, "EnvironmentalWindDirection" }, - { 0x20, 0x35, "EnvironmentalWindSpeed" }, - { 0x20, 0x40, "Light" }, - { 0x20, 0x41, "LightAmbientLight" }, - { 0x20, 0x42, "LightConsumerInfrared" }, - { 0x20, 0x50, "Location" }, - { 0x20, 0x51, "LocationBroadcast" }, - { 0x20, 0x52, "LocationDeadReckoning" }, - { 0x20, 0x53, "LocationGPS" }, - { 0x20, 0x54, "LocationLookup" }, - { 0x20, 0x55, "LocationOther" }, - { 0x20, 0x56, "LocationStatic" }, - { 0x20, 0x57, "LocationTriangulation" }, - { 0x20, 0x60, "Mechanical" }, - { 0x20, 0x61, "MechanicalBooleanSwitch" }, - { 0x20, 0x62, "MechanicalBooleanSwitchArray" }, - { 0x20, 0x63, "MechanicalMultivalueSwitch" }, - { 0x20, 0x64, "MechanicalForce" }, - { 0x20, 0x65, "MechanicalPressure" }, - { 0x20, 0x66, "MechanicalStrain" }, - { 0x20, 0x67, "MechanicalWeight" }, - { 0x20, 0x68, "MechanicalHapticVibrator" }, - { 0x20, 0x69, "MechanicalHallEffectSwitch" }, - { 0x20, 0x70, "Motion" }, - { 0x20, 0x71, "MotionAccelerometer1D" }, - { 0x20, 0x72, "MotionAccelerometer2D" }, - { 0x20, 0x73, "MotionAccelerometer3D" }, - { 0x20, 0x74, "MotionGyrometer1D" }, - { 0x20, 0x75, "MotionGyrometer2D" }, - { 0x20, 0x76, "MotionGyrometer3D" }, - { 0x20, 0x77, "MotionMotionDetector" }, - { 0x20, 0x78, "MotionSpeedometer" }, - { 0x20, 0x79, "MotionAccelerometer" }, - { 0x20, 0x7A, "MotionGyrometer" }, - { 0x20, 0x80, "Orientation" }, - { 0x20, 0x81, "OrientationCompass1D" }, - { 0x20, 0x82, "OrientationCompass2D" }, - { 0x20, 0x83, "OrientationCompass3D" }, - { 0x20, 0x84, "OrientationInclinometer1D" }, - { 0x20, 0x85, "OrientationInclinometer2D" }, - { 0x20, 0x86, "OrientationInclinometer3D" }, - { 0x20, 0x87, "OrientationDistance1D" }, - { 0x20, 0x88, "OrientationDistance2D" }, - { 0x20, 0x89, "OrientationDistance3D" }, - { 0x20, 0x8A, "OrientationDeviceOrientation" }, - { 0x20, 0x8B, "OrientationCompass" }, - { 0x20, 0x8C, "OrientationInclinometer" }, - { 0x20, 0x8D, "OrientationDistance" }, - { 0x20, 0x90, "Scanner" }, - { 0x20, 0x91, "ScannerBarcode" }, - { 0x20, 0x91, "ScannerRFID" }, - { 0x20, 0x91, "ScannerNFC" }, - { 0x20, 0xA0, "Time" }, - { 0x20, 0xA1, "TimeAlarmTimer" }, - { 0x20, 0xA2, "TimeRealTimeClock" }, - { 0x20, 0xE0, "Other" }, - { 0x20, 0xE1, "OtherCustom" }, - { 0x20, 0xE2, "OtherGeneric" }, - { 0x20, 0xE3, "OtherGenericEnumerator" }, - { 0x84, 0, "Power Device" }, - { 0x84, 0x02, "PresentStatus" }, - { 0x84, 0x03, "ChangeStatus" }, - { 0x84, 0x04, "UPS" }, - { 0x84, 0x05, "PowerSupply" }, - { 0x84, 0x10, "BatterySystem" }, - { 0x84, 0x11, "BatterySystemID" }, - { 0x84, 0x12, "Battery" }, - { 0x84, 0x13, "BatteryID" }, - { 0x84, 0x14, "Charger" }, - { 0x84, 0x15, "ChargerID" }, - { 0x84, 0x16, "PowerConverter" }, - { 0x84, 0x17, "PowerConverterID" }, - { 0x84, 0x18, "OutletSystem" }, - { 0x84, 0x19, "OutletSystemID" }, - { 0x84, 0x1a, "Input" }, - { 0x84, 0x1b, "InputID" }, - { 0x84, 0x1c, "Output" }, - { 0x84, 0x1d, "OutputID" }, - { 0x84, 0x1e, "Flow" }, - { 0x84, 0x1f, "FlowID" }, - { 0x84, 0x20, "Outlet" }, - { 0x84, 0x21, "OutletID" }, - { 0x84, 0x22, "Gang" }, - { 0x84, 0x24, "PowerSummary" }, - { 0x84, 0x25, "PowerSummaryID" }, - { 0x84, 0x30, "Voltage" }, - { 0x84, 0x31, "Current" }, - { 0x84, 0x32, "Frequency" }, - { 0x84, 0x33, "ApparentPower" }, - { 0x84, 0x35, "PercentLoad" }, - { 0x84, 0x40, "ConfigVoltage" }, - { 0x84, 0x41, "ConfigCurrent" }, - { 0x84, 0x43, "ConfigApparentPower" }, - { 0x84, 0x53, "LowVoltageTransfer" }, - { 0x84, 0x54, "HighVoltageTransfer" }, - { 0x84, 0x56, "DelayBeforeStartup" }, - { 0x84, 0x57, "DelayBeforeShutdown" }, - { 0x84, 0x58, "Test" }, - { 0x84, 0x5a, "AudibleAlarmControl" }, - { 0x84, 0x60, "Present" }, - { 0x84, 0x61, "Good" }, - { 0x84, 0x62, "InternalFailure" }, - { 0x84, 0x65, "Overload" }, - { 0x84, 0x66, "OverCharged" }, - { 0x84, 0x67, "OverTemperature" }, - { 0x84, 0x68, "ShutdownRequested" }, - { 0x84, 0x69, "ShutdownImminent" }, - { 0x84, 0x6b, "SwitchOn/Off" }, - { 0x84, 0x6c, "Switchable" }, - { 0x84, 0x6d, "Used" }, - { 0x84, 0x6e, "Boost" }, - { 0x84, 0x73, "CommunicationLost" }, - { 0x84, 0xfd, "iManufacturer" }, - { 0x84, 0xfe, "iProduct" }, - { 0x84, 0xff, "iSerialNumber" }, - { 0x85, 0, "Battery System" }, - { 0x85, 0x01, "SMBBatteryMode" }, - { 0x85, 0x02, "SMBBatteryStatus" }, - { 0x85, 0x03, "SMBAlarmWarning" }, - { 0x85, 0x04, "SMBChargerMode" }, - { 0x85, 0x05, "SMBChargerStatus" }, - { 0x85, 0x06, "SMBChargerSpecInfo" }, - { 0x85, 0x07, "SMBSelectorState" }, - { 0x85, 0x08, "SMBSelectorPresets" }, - { 0x85, 0x09, "SMBSelectorInfo" }, - { 0x85, 0x29, "RemainingCapacityLimit" }, - { 0x85, 0x2c, "CapacityMode" }, - { 0x85, 0x42, "BelowRemainingCapacityLimit" }, - { 0x85, 0x44, "Charging" }, - { 0x85, 0x45, "Discharging" }, - { 0x85, 0x4b, "NeedReplacement" }, - { 0x85, 0x65, "AbsoluteStateOfCharge" }, - { 0x85, 0x66, "RemainingCapacity" }, - { 0x85, 0x68, "RunTimeToEmpty" }, - { 0x85, 0x6a, "AverageTimeToFull" }, - { 0x85, 0x83, "DesignCapacity" }, - { 0x85, 0x85, "ManufacturerDate" }, - { 0x85, 0x89, "iDeviceChemistry" }, - { 0x85, 0x8b, "Rechargeable" }, - { 0x85, 0x8f, "iOEMInformation" }, - { 0x85, 0x8d, "CapacityGranularity1" }, - { 0x85, 0xd0, "ACPresent" }, - /* pages 0xff00 to 0xffff are vendor-specific */ - { 0xffff, 0, "Vendor-specific-FF" }, - { 0, 0, NULL } + { 0x00, 0, "Undefined" }, + { 0x01, 0, "GenericDesktop" }, + { 0x01, 0x0001, "Pointer" }, + { 0x01, 0x0002, "Mouse" }, + { 0x01, 0x0004, "Joystick" }, + { 0x01, 0x0005, "Gamepad" }, + { 0x01, 0x0006, "Keyboard" }, + { 0x01, 0x0007, "Keypad" }, + { 0x01, 0x0008, "MultiaxisController" }, + { 0x01, 0x0009, "TabletPCSystemControls" }, + { 0x01, 0x000a, "WaterCoolingDevice" }, + { 0x01, 0x000b, "ComputerChassisDevice" }, + { 0x01, 0x000c, "WirelessRadioControls" }, + { 0x01, 0x000d, "PortableDeviceControl" }, + { 0x01, 0x000e, "SystemMultiAxisController" }, + { 0x01, 0x000f, "SpatialController" }, + { 0x01, 0x0010, "AssistiveControl" }, + { 0x01, 0x0011, "DeviceDock" }, + { 0x01, 0x0012, "DockableDevice" }, + { 0x01, 0x0013, "CallStateManagementControl" }, + { 0x01, 0x0030, "X" }, + { 0x01, 0x0031, "Y" }, + { 0x01, 0x0032, "Z" }, + { 0x01, 0x0033, "Rx" }, + { 0x01, 0x0034, "Ry" }, + { 0x01, 0x0035, "Rz" }, + { 0x01, 0x0036, "Slider" }, + { 0x01, 0x0037, "Dial" }, + { 0x01, 0x0038, "Wheel" }, + { 0x01, 0x0039, "HatSwitch" }, + { 0x01, 0x003a, "CountedBuffer" }, + { 0x01, 0x003b, "ByteCount" }, + { 0x01, 0x003c, "MotionWakeup" }, + { 0x01, 0x003d, "Start" }, + { 0x01, 0x003e, "Select" }, + { 0x01, 0x0040, "Vx" }, + { 0x01, 0x0041, "Vy" }, + { 0x01, 0x0042, "Vz" }, + { 0x01, 0x0043, "Vbrx" }, + { 0x01, 0x0044, "Vbry" }, + { 0x01, 0x0045, "Vbrz" }, + { 0x01, 0x0046, "Vno" }, + { 0x01, 0x0047, "FeatureNotification" }, + { 0x01, 0x0048, "ResolutionMultiplier" }, + { 0x01, 0x0049, "Qx" }, + { 0x01, 0x004a, "Qy" }, + { 0x01, 0x004b, "Qz" }, + { 0x01, 0x004c, "Qw" }, + { 0x01, 0x0080, "SystemControl" }, + { 0x01, 0x0081, "SystemPowerDown" }, + { 0x01, 0x0082, "SystemSleep" }, + { 0x01, 0x0083, "SystemWakeUp" }, + { 0x01, 0x0084, "SystemContextMenu" }, + { 0x01, 0x0085, "SystemMainMenu" }, + { 0x01, 0x0086, "SystemAppMenu" }, + { 0x01, 0x0087, "SystemMenuHelp" }, + { 0x01, 0x0088, "SystemMenuExit" }, + { 0x01, 0x0089, "SystemMenuSelect" }, + { 0x01, 0x008a, "SystemMenuRight" }, + { 0x01, 0x008b, "SystemMenuLeft" }, + { 0x01, 0x008c, "SystemMenuUp" }, + { 0x01, 0x008d, "SystemMenuDown" }, + { 0x01, 0x008e, "SystemColdRestart" }, + { 0x01, 0x008f, "SystemWarmRestart" }, + { 0x01, 0x0090, "DpadUp" }, + { 0x01, 0x0091, "DpadDown" }, + { 0x01, 0x0092, "DpadRight" }, + { 0x01, 0x0093, "DpadLeft" }, + { 0x01, 0x0094, "IndexTrigger" }, + { 0x01, 0x0095, "PalmTrigger" }, + { 0x01, 0x0096, "Thumbstick" }, + { 0x01, 0x0097, "SystemFunctionShift" }, + { 0x01, 0x0098, "SystemFunctionShiftLock" }, + { 0x01, 0x0099, "SystemFunctionShiftLockIndicator" }, + { 0x01, 0x009a, "SystemDismissNotification" }, + { 0x01, 0x009b, "SystemDoNotDisturb" }, + { 0x01, 0x00a0, "SystemDock" }, + { 0x01, 0x00a1, "SystemUndock" }, + { 0x01, 0x00a2, "SystemSetup" }, + { 0x01, 0x00a3, "SystemBreak" }, + { 0x01, 0x00a4, "SystemDebuggerBreak" }, + { 0x01, 0x00a5, "ApplicationBreak" }, + { 0x01, 0x00a6, "ApplicationDebuggerBreak" }, + { 0x01, 0x00a7, "SystemSpeakerMute" }, + { 0x01, 0x00a8, "SystemHibernate" }, + { 0x01, 0x00a9, "SystemMicrophoneMute" }, + { 0x01, 0x00b0, "SystemDisplayInvert" }, + { 0x01, 0x00b1, "SystemDisplayInternal" }, + { 0x01, 0x00b2, "SystemDisplayExternal" }, + { 0x01, 0x00b3, "SystemDisplayBoth" }, + { 0x01, 0x00b4, "SystemDisplayDual" }, + { 0x01, 0x00b5, "SystemDisplayToggleIntExtMode" }, + { 0x01, 0x00b6, "SystemDisplaySwapPrimarySecondary" }, + { 0x01, 0x00b7, "SystemDisplayToggleLCDAutoscale" }, + { 0x01, 0x00c0, "SensorZone" }, + { 0x01, 0x00c1, "RPM" }, + { 0x01, 0x00c2, "CoolantLevel" }, + { 0x01, 0x00c3, "CoolantCriticalLevel" }, + { 0x01, 0x00c4, "CoolantPump" }, + { 0x01, 0x00c5, "ChassisEnclosure" }, + { 0x01, 0x00c6, "WirelessRadioButton" }, + { 0x01, 0x00c7, "WirelessRadioLED" }, + { 0x01, 0x00c8, "WirelessRadioSliderSwitch" }, + { 0x01, 0x00c9, "SystemDisplayRotationLockButton" }, + { 0x01, 0x00ca, "SystemDisplayRotationLockSliderSwitch" }, + { 0x01, 0x00cb, "ControlEnable" }, + { 0x01, 0x00d0, "DockableDeviceUniqueID" }, + { 0x01, 0x00d1, "DockableDeviceVendorID" }, + { 0x01, 0x00d2, "DockableDevicePrimaryUsagePage" }, + { 0x01, 0x00d3, "DockableDevicePrimaryUsageID" }, + { 0x01, 0x00d4, "DockableDeviceDockingState" }, + { 0x01, 0x00d5, "DockableDeviceDisplayOcclusion" }, + { 0x01, 0x00d6, "DockableDeviceObjectType" }, + { 0x01, 0x00e0, "CallActiveLED" }, + { 0x01, 0x00e1, "CallMuteToggle" }, + { 0x01, 0x00e2, "CallMuteLED" }, + { 0x02, 0, "SimulationControls" }, + { 0x02, 0x0001, "FlightSimulationDevice" }, + { 0x02, 0x0002, "AutomobileSimulationDevice" }, + { 0x02, 0x0003, "TankSimulationDevice" }, + { 0x02, 0x0004, "SpaceshipSimulationDevice" }, + { 0x02, 0x0005, "SubmarineSimulationDevice" }, + { 0x02, 0x0006, "SailingSimulationDevice" }, + { 0x02, 0x0007, "MotorcycleSimulationDevice" }, + { 0x02, 0x0008, "SportsSimulationDevice" }, + { 0x02, 0x0009, "AirplaneSimulationDevice" }, + { 0x02, 0x000a, "HelicopterSimulationDevice" }, + { 0x02, 0x000b, "MagicCarpetSimulationDevice" }, + { 0x02, 0x000c, "BicycleSimulationDevice" }, + { 0x02, 0x0020, "FlightControlStick" }, + { 0x02, 0x0021, "FlightStick" }, + { 0x02, 0x0022, "CyclicControl" }, + { 0x02, 0x0023, "CyclicTrim" }, + { 0x02, 0x0024, "FlightYoke" }, + { 0x02, 0x0025, "TrackControl" }, + { 0x02, 0x00b0, "Aileron" }, + { 0x02, 0x00b1, "AileronTrim" }, + { 0x02, 0x00b2, "AntiTorqueControl" }, + { 0x02, 0x00b3, "AutopilotEnable" }, + { 0x02, 0x00b4, "ChaffRelease" }, + { 0x02, 0x00b5, "CollectiveControl" }, + { 0x02, 0x00b6, "DiveBrake" }, + { 0x02, 0x00b7, "ElectronicCountermeasures" }, + { 0x02, 0x00b8, "Elevator" }, + { 0x02, 0x00b9, "ElevatorTrim" }, + { 0x02, 0x00ba, "Rudder" }, + { 0x02, 0x00bb, "Throttle" }, + { 0x02, 0x00bc, "FlightCommunications" }, + { 0x02, 0x00bd, "FlareRelease" }, + { 0x02, 0x00be, "LandingGear" }, + { 0x02, 0x00bf, "ToeBrake" }, + { 0x02, 0x00c0, "Trigger" }, + { 0x02, 0x00c1, "WeaponsArm" }, + { 0x02, 0x00c2, "WeaponsSelect" }, + { 0x02, 0x00c3, "WingFlaps" }, + { 0x02, 0x00c4, "Accelerator" }, + { 0x02, 0x00c5, "Brake" }, + { 0x02, 0x00c6, "Clutch" }, + { 0x02, 0x00c7, "Shifter" }, + { 0x02, 0x00c8, "Steering" }, + { 0x02, 0x00c9, "TurretDirection" }, + { 0x02, 0x00ca, "BarrelElevation" }, + { 0x02, 0x00cb, "DivePlane" }, + { 0x02, 0x00cc, "Ballast" }, + { 0x02, 0x00cd, "BicycleCrank" }, + { 0x02, 0x00ce, "HandleBars" }, + { 0x02, 0x00cf, "FrontBrake" }, + { 0x02, 0x00d0, "RearBrake" }, + { 0x03, 0, "VRControls" }, + { 0x03, 0x0001, "Belt" }, + { 0x03, 0x0002, "BodySuit" }, + { 0x03, 0x0003, "Flexor" }, + { 0x03, 0x0004, "Glove" }, + { 0x03, 0x0005, "HeadTracker" }, + { 0x03, 0x0006, "HeadMountedDisplay" }, + { 0x03, 0x0007, "HandTracker" }, + { 0x03, 0x0008, "Oculometer" }, + { 0x03, 0x0009, "Vest" }, + { 0x03, 0x000a, "AnimatronicDevice" }, + { 0x03, 0x0020, "StereoEnable" }, + { 0x03, 0x0021, "DisplayEnable" }, + { 0x04, 0, "SportControls" }, + { 0x04, 0x0001, "BaseballBat" }, + { 0x04, 0x0002, "GolfClub" }, + { 0x04, 0x0003, "RowingMachine" }, + { 0x04, 0x0004, "Treadmill" }, + { 0x04, 0x0030, "Oar" }, + { 0x04, 0x0031, "Slope" }, + { 0x04, 0x0032, "Rate" }, + { 0x04, 0x0033, "StickSpeed" }, + { 0x04, 0x0034, "StickFaceAngle" }, + { 0x04, 0x0035, "StickHeelToe" }, + { 0x04, 0x0036, "StickFollowThrough" }, + { 0x04, 0x0037, "StickTempo" }, + { 0x04, 0x0038, "StickType" }, + { 0x04, 0x0039, "StickHeight" }, + { 0x04, 0x0050, "Putter" }, + { 0x04, 0x0051, "1Iron" }, + { 0x04, 0x0052, "2Iron" }, + { 0x04, 0x0053, "3Iron" }, + { 0x04, 0x0054, "4Iron" }, + { 0x04, 0x0055, "5Iron" }, + { 0x04, 0x0056, "6Iron" }, + { 0x04, 0x0057, "7Iron" }, + { 0x04, 0x0058, "8Iron" }, + { 0x04, 0x0059, "9Iron" }, + { 0x04, 0x005a, "10Iron" }, + { 0x04, 0x005b, "11Iron" }, + { 0x04, 0x005c, "SandWedge" }, + { 0x04, 0x005d, "LoftWedge" }, + { 0x04, 0x005e, "PowerWedge" }, + { 0x04, 0x005f, "1Wood" }, + { 0x04, 0x0060, "3Wood" }, + { 0x04, 0x0061, "5Wood" }, + { 0x04, 0x0062, "7Wood" }, + { 0x04, 0x0063, "9Wood" }, + { 0x05, 0, "GameControls" }, + { 0x05, 0x0001, "3DGameController" }, + { 0x05, 0x0002, "PinballDevice" }, + { 0x05, 0x0003, "GunDevice" }, + { 0x05, 0x0020, "PointofView" }, + { 0x05, 0x0021, "TurnRightLeft" }, + { 0x05, 0x0022, "PitchForwardBackward" }, + { 0x05, 0x0023, "RollRightLeft" }, + { 0x05, 0x0024, "MoveRightLeft" }, + { 0x05, 0x0025, "MoveForwardBackward" }, + { 0x05, 0x0026, "MoveUpDown" }, + { 0x05, 0x0027, "LeanRightLeft" }, + { 0x05, 0x0028, "LeanForwardBackward" }, + { 0x05, 0x0029, "HeightofPOV" }, + { 0x05, 0x002a, "Flipper" }, + { 0x05, 0x002b, "SecondaryFlipper" }, + { 0x05, 0x002c, "Bump" }, + { 0x05, 0x002d, "NewGame" }, + { 0x05, 0x002e, "ShootBall" }, + { 0x05, 0x002f, "Player" }, + { 0x05, 0x0030, "GunBolt" }, + { 0x05, 0x0031, "GunClip" }, + { 0x05, 0x0032, "GunSelector" }, + { 0x05, 0x0033, "GunSingleShot" }, + { 0x05, 0x0034, "GunBurst" }, + { 0x05, 0x0035, "GunAutomatic" }, + { 0x05, 0x0036, "GunSafety" }, + { 0x05, 0x0037, "GamepadFireJump" }, + { 0x05, 0x0039, "GamepadTrigger" }, + { 0x05, 0x003a, "FormfittingGamepad" }, + { 0x06, 0, "GenericDeviceControls" }, + { 0x06, 0x0001, "BackgroundNonuserControls" }, + { 0x06, 0x0020, "BatteryStrength" }, + { 0x06, 0x0021, "WirelessChannel" }, + { 0x06, 0x0022, "WirelessID" }, + { 0x06, 0x0023, "DiscoverWirelessControl" }, + { 0x06, 0x0024, "SecurityCodeCharacterEntered" }, + { 0x06, 0x0025, "SecurityCodeCharacterErased" }, + { 0x06, 0x0026, "SecurityCodeCleared" }, + { 0x06, 0x0027, "SequenceID" }, + { 0x06, 0x0028, "SequenceIDReset" }, + { 0x06, 0x0029, "RFSignalStrength" }, + { 0x06, 0x002a, "SoftwareVersion" }, + { 0x06, 0x002b, "ProtocolVersion" }, + { 0x06, 0x002c, "HardwareVersion" }, + { 0x06, 0x002d, "Major" }, + { 0x06, 0x002e, "Minor" }, + { 0x06, 0x002f, "Revision" }, + { 0x06, 0x0030, "Handedness" }, + { 0x06, 0x0031, "EitherHand" }, + { 0x06, 0x0032, "LeftHand" }, + { 0x06, 0x0033, "RightHand" }, + { 0x06, 0x0034, "BothHands" }, + { 0x06, 0x0040, "GripPoseOffset" }, + { 0x06, 0x0041, "PointerPoseOffset" }, + { 0x07, 0, "KeyboardKeypad" }, + { 0x07, 0x0001, "ErrorRollOver" }, + { 0x07, 0x0002, "POSTFail" }, + { 0x07, 0x0003, "ErrorUndefined" }, + { 0x07, 0x0004, "KeyboardA" }, + { 0x07, 0x0005, "KeyboardB" }, + { 0x07, 0x0006, "KeyboardC" }, + { 0x07, 0x0007, "KeyboardD" }, + { 0x07, 0x0008, "KeyboardE" }, + { 0x07, 0x0009, "KeyboardF" }, + { 0x07, 0x000a, "KeyboardG" }, + { 0x07, 0x000b, "KeyboardH" }, + { 0x07, 0x000c, "KeyboardI" }, + { 0x07, 0x000d, "KeyboardJ" }, + { 0x07, 0x000e, "KeyboardK" }, + { 0x07, 0x000f, "KeyboardL" }, + { 0x07, 0x0010, "KeyboardM" }, + { 0x07, 0x0011, "KeyboardN" }, + { 0x07, 0x0012, "KeyboardO" }, + { 0x07, 0x0013, "KeyboardP" }, + { 0x07, 0x0014, "KeyboardQ" }, + { 0x07, 0x0015, "KeyboardR" }, + { 0x07, 0x0016, "KeyboardS" }, + { 0x07, 0x0017, "KeyboardT" }, + { 0x07, 0x0018, "KeyboardU" }, + { 0x07, 0x0019, "KeyboardV" }, + { 0x07, 0x001a, "KeyboardW" }, + { 0x07, 0x001b, "KeyboardX" }, + { 0x07, 0x001c, "KeyboardY" }, + { 0x07, 0x001d, "KeyboardZ" }, + { 0x07, 0x001e, "Keyboard1andBang" }, + { 0x07, 0x001f, "Keyboard2andAt" }, + { 0x07, 0x0020, "Keyboard3andHash" }, + { 0x07, 0x0021, "Keyboard4andDollar" }, + { 0x07, 0x0022, "Keyboard5andPercent" }, + { 0x07, 0x0023, "Keyboard6andCaret" }, + { 0x07, 0x0024, "Keyboard7andAmpersand" }, + { 0x07, 0x0025, "Keyboard8andStar" }, + { 0x07, 0x0026, "Keyboard9andLeftBracket" }, + { 0x07, 0x0027, "Keyboard0andRightBracket" }, + { 0x07, 0x0028, "KeyboardReturnEnter" }, + { 0x07, 0x0029, "KeyboardEscape" }, + { 0x07, 0x002a, "KeyboardDelete" }, + { 0x07, 0x002b, "KeyboardTab" }, + { 0x07, 0x002c, "KeyboardSpacebar" }, + { 0x07, 0x002d, "KeyboardDashandUnderscore" }, + { 0x07, 0x002e, "KeyboardEqualsandPlus" }, + { 0x07, 0x002f, "KeyboardLeftBrace" }, + { 0x07, 0x0030, "KeyboardRightBrace" }, + { 0x07, 0x0031, "KeyboardBackslashandPipe" }, + { 0x07, 0x0032, "KeyboardNonUSHashandTilde" }, + { 0x07, 0x0033, "KeyboardSemiColonandColon" }, + { 0x07, 0x0034, "KeyboardLeftAposandDouble" }, + { 0x07, 0x0035, "KeyboardGraveAccentandTilde" }, + { 0x07, 0x0036, "KeyboardCommaandLessThan" }, + { 0x07, 0x0037, "KeyboardPeriodandGreaterThan" }, + { 0x07, 0x0038, "KeyboardForwardSlashandQuestionMark" }, + { 0x07, 0x0039, "KeyboardCapsLock" }, + { 0x07, 0x003a, "KeyboardF1" }, + { 0x07, 0x003b, "KeyboardF2" }, + { 0x07, 0x003c, "KeyboardF3" }, + { 0x07, 0x003d, "KeyboardF4" }, + { 0x07, 0x003e, "KeyboardF5" }, + { 0x07, 0x003f, "KeyboardF6" }, + { 0x07, 0x0040, "KeyboardF7" }, + { 0x07, 0x0041, "KeyboardF8" }, + { 0x07, 0x0042, "KeyboardF9" }, + { 0x07, 0x0043, "KeyboardF10" }, + { 0x07, 0x0044, "KeyboardF11" }, + { 0x07, 0x0045, "KeyboardF12" }, + { 0x07, 0x0046, "KeyboardPrintScreen" }, + { 0x07, 0x0047, "KeyboardScrollLock" }, + { 0x07, 0x0048, "KeyboardPause" }, + { 0x07, 0x0049, "KeyboardInsert" }, + { 0x07, 0x004a, "KeyboardHome" }, + { 0x07, 0x004b, "KeyboardPageUp" }, + { 0x07, 0x004c, "KeyboardDeleteForward" }, + { 0x07, 0x004d, "KeyboardEnd" }, + { 0x07, 0x004e, "KeyboardPageDown" }, + { 0x07, 0x004f, "KeyboardRightArrow" }, + { 0x07, 0x0050, "KeyboardLeftArrow" }, + { 0x07, 0x0051, "KeyboardDownArrow" }, + { 0x07, 0x0052, "KeyboardUpArrow" }, + { 0x07, 0x0053, "KeypadNumLockandClear" }, + { 0x07, 0x0054, "KeypadForwardSlash" }, + { 0x07, 0x0055, "KeypadStar" }, + { 0x07, 0x0056, "KeypadDash" }, + { 0x07, 0x0057, "KeypadPlus" }, + { 0x07, 0x0058, "KeypadENTER" }, + { 0x07, 0x0059, "Keypad1andEnd" }, + { 0x07, 0x005a, "Keypad2andDownArrow" }, + { 0x07, 0x005b, "Keypad3andPageDn" }, + { 0x07, 0x005c, "Keypad4andLeftArrow" }, + { 0x07, 0x005d, "Keypad5" }, + { 0x07, 0x005e, "Keypad6andRightArrow" }, + { 0x07, 0x005f, "Keypad7andHome" }, + { 0x07, 0x0060, "Keypad8andUpArrow" }, + { 0x07, 0x0061, "Keypad9andPageUp" }, + { 0x07, 0x0062, "Keypad0andInsert" }, + { 0x07, 0x0063, "KeypadPeriodandDelete" }, + { 0x07, 0x0064, "KeyboardNonUSBackslashandPipe" }, + { 0x07, 0x0065, "KeyboardApplication" }, + { 0x07, 0x0066, "KeyboardPower" }, + { 0x07, 0x0067, "KeypadEquals" }, + { 0x07, 0x0068, "KeyboardF13" }, + { 0x07, 0x0069, "KeyboardF14" }, + { 0x07, 0x006a, "KeyboardF15" }, + { 0x07, 0x006b, "KeyboardF16" }, + { 0x07, 0x006c, "KeyboardF17" }, + { 0x07, 0x006d, "KeyboardF18" }, + { 0x07, 0x006e, "KeyboardF19" }, + { 0x07, 0x006f, "KeyboardF20" }, + { 0x07, 0x0070, "KeyboardF21" }, + { 0x07, 0x0071, "KeyboardF22" }, + { 0x07, 0x0072, "KeyboardF23" }, + { 0x07, 0x0073, "KeyboardF24" }, + { 0x07, 0x0074, "KeyboardExecute" }, + { 0x07, 0x0075, "KeyboardHelp" }, + { 0x07, 0x0076, "KeyboardMenu" }, + { 0x07, 0x0077, "KeyboardSelect" }, + { 0x07, 0x0078, "KeyboardStop" }, + { 0x07, 0x0079, "KeyboardAgain" }, + { 0x07, 0x007a, "KeyboardUndo" }, + { 0x07, 0x007b, "KeyboardCut" }, + { 0x07, 0x007c, "KeyboardCopy" }, + { 0x07, 0x007d, "KeyboardPaste" }, + { 0x07, 0x007e, "KeyboardFind" }, + { 0x07, 0x007f, "KeyboardMute" }, + { 0x07, 0x0080, "KeyboardVolumeUp" }, + { 0x07, 0x0081, "KeyboardVolumeDown" }, + { 0x07, 0x0082, "KeyboardLockingCapsLock" }, + { 0x07, 0x0083, "KeyboardLockingNumLock" }, + { 0x07, 0x0084, "KeyboardLockingScrollLock" }, + { 0x07, 0x0085, "KeypadComma" }, + { 0x07, 0x0086, "KeypadEqualSign" }, + { 0x07, 0x0087, "KeyboardInternational1" }, + { 0x07, 0x0088, "KeyboardInternational2" }, + { 0x07, 0x0089, "KeyboardInternational3" }, + { 0x07, 0x008a, "KeyboardInternational4" }, + { 0x07, 0x008b, "KeyboardInternational5" }, + { 0x07, 0x008c, "KeyboardInternational6" }, + { 0x07, 0x008d, "KeyboardInternational7" }, + { 0x07, 0x008e, "KeyboardInternational8" }, + { 0x07, 0x008f, "KeyboardInternational9" }, + { 0x07, 0x0090, "KeyboardLANG1" }, + { 0x07, 0x0091, "KeyboardLANG2" }, + { 0x07, 0x0092, "KeyboardLANG3" }, + { 0x07, 0x0093, "KeyboardLANG4" }, + { 0x07, 0x0094, "KeyboardLANG5" }, + { 0x07, 0x0095, "KeyboardLANG6" }, + { 0x07, 0x0096, "KeyboardLANG7" }, + { 0x07, 0x0097, "KeyboardLANG8" }, + { 0x07, 0x0098, "KeyboardLANG9" }, + { 0x07, 0x0099, "KeyboardAlternateErase" }, + { 0x07, 0x009a, "KeyboardSysReqAttention" }, + { 0x07, 0x009b, "KeyboardCancel" }, + { 0x07, 0x009c, "KeyboardClear" }, + { 0x07, 0x009d, "KeyboardPrior" }, + { 0x07, 0x009e, "KeyboardReturn" }, + { 0x07, 0x009f, "KeyboardSeparator" }, + { 0x07, 0x00a0, "KeyboardOut" }, + { 0x07, 0x00a1, "KeyboardOper" }, + { 0x07, 0x00a2, "KeyboardClearAgain" }, + { 0x07, 0x00a3, "KeyboardCrSelProps" }, + { 0x07, 0x00a4, "KeyboardExSel" }, + { 0x07, 0x00b0, "KeypadDouble0" }, + { 0x07, 0x00b1, "KeypadTriple0" }, + { 0x07, 0x00b2, "ThousandsSeparator" }, + { 0x07, 0x00b3, "DecimalSeparator" }, + { 0x07, 0x00b4, "CurrencyUnit" }, + { 0x07, 0x00b5, "CurrencySubunit" }, + { 0x07, 0x00b6, "KeypadLeftBracket" }, + { 0x07, 0x00b7, "KeypadRightBracket" }, + { 0x07, 0x00b8, "KeypadLeftBrace" }, + { 0x07, 0x00b9, "KeypadRightBrace" }, + { 0x07, 0x00ba, "KeypadTab" }, + { 0x07, 0x00bb, "KeypadBackspace" }, + { 0x07, 0x00bc, "KeypadA" }, + { 0x07, 0x00bd, "KeypadB" }, + { 0x07, 0x00be, "KeypadC" }, + { 0x07, 0x00bf, "KeypadD" }, + { 0x07, 0x00c0, "KeypadE" }, + { 0x07, 0x00c1, "KeypadF" }, + { 0x07, 0x00c2, "KeypadXOR" }, + { 0x07, 0x00c3, "KeypadCaret" }, + { 0x07, 0x00c4, "KeypadPercentage" }, + { 0x07, 0x00c5, "KeypadLess" }, + { 0x07, 0x00c6, "KeypadGreater" }, + { 0x07, 0x00c7, "KeypadAmpersand" }, + { 0x07, 0x00c8, "KeypadDoubleAmpersand" }, + { 0x07, 0x00c9, "KeypadBar" }, + { 0x07, 0x00ca, "KeypadDoubleBar" }, + { 0x07, 0x00cb, "KeypadColon" }, + { 0x07, 0x00cc, "KeypadHash" }, + { 0x07, 0x00cd, "KeypadSpace" }, + { 0x07, 0x00ce, "KeypadAt" }, + { 0x07, 0x00cf, "KeypadBang" }, + { 0x07, 0x00d0, "KeypadMemoryStore" }, + { 0x07, 0x00d1, "KeypadMemoryRecall" }, + { 0x07, 0x00d2, "KeypadMemoryClear" }, + { 0x07, 0x00d3, "KeypadMemoryAdd" }, + { 0x07, 0x00d4, "KeypadMemorySubtract" }, + { 0x07, 0x00d5, "KeypadMemoryMultiply" }, + { 0x07, 0x00d6, "KeypadMemoryDivide" }, + { 0x07, 0x00d7, "KeypadPlusMinus" }, + { 0x07, 0x00d8, "KeypadClear" }, + { 0x07, 0x00d9, "KeypadClearEntry" }, + { 0x07, 0x00da, "KeypadBinary" }, + { 0x07, 0x00db, "KeypadOctal" }, + { 0x07, 0x00dc, "KeypadDecimal" }, + { 0x07, 0x00dd, "KeypadHexadecimal" }, + { 0x07, 0x00e0, "KeyboardLeftControl" }, + { 0x07, 0x00e1, "KeyboardLeftShift" }, + { 0x07, 0x00e2, "KeyboardLeftAlt" }, + { 0x07, 0x00e3, "KeyboardLeftGUI" }, + { 0x07, 0x00e4, "KeyboardRightControl" }, + { 0x07, 0x00e5, "KeyboardRightShift" }, + { 0x07, 0x00e6, "KeyboardRightAlt" }, + { 0x07, 0x00e7, "KeyboardRightGUI" }, + { 0x08, 0, "LED" }, + { 0x08, 0x0001, "NumLock" }, + { 0x08, 0x0002, "CapsLock" }, + { 0x08, 0x0003, "ScrollLock" }, + { 0x08, 0x0004, "Compose" }, + { 0x08, 0x0005, "Kana" }, + { 0x08, 0x0006, "Power" }, + { 0x08, 0x0007, "Shift" }, + { 0x08, 0x0008, "DoNotDisturb" }, + { 0x08, 0x0009, "Mute" }, + { 0x08, 0x000a, "ToneEnable" }, + { 0x08, 0x000b, "HighCutFilter" }, + { 0x08, 0x000c, "LowCutFilter" }, + { 0x08, 0x000d, "EqualizerEnable" }, + { 0x08, 0x000e, "SoundFieldOn" }, + { 0x08, 0x000f, "SurroundOn" }, + { 0x08, 0x0010, "Repeat" }, + { 0x08, 0x0011, "Stereo" }, + { 0x08, 0x0012, "SamplingRateDetect" }, + { 0x08, 0x0013, "Spinning" }, + { 0x08, 0x0014, "CAV" }, + { 0x08, 0x0015, "CLV" }, + { 0x08, 0x0016, "RecordingFormatDetect" }, + { 0x08, 0x0017, "OffHook" }, + { 0x08, 0x0018, "Ring" }, + { 0x08, 0x0019, "MessageWaiting" }, + { 0x08, 0x001a, "DataMode" }, + { 0x08, 0x001b, "BatteryOperation" }, + { 0x08, 0x001c, "BatteryOK" }, + { 0x08, 0x001d, "BatteryLow" }, + { 0x08, 0x001e, "Speaker" }, + { 0x08, 0x001f, "Headset" }, + { 0x08, 0x0020, "Hold" }, + { 0x08, 0x0021, "Microphone" }, + { 0x08, 0x0022, "Coverage" }, + { 0x08, 0x0023, "NightMode" }, + { 0x08, 0x0024, "SendCalls" }, + { 0x08, 0x0025, "CallPickup" }, + { 0x08, 0x0026, "Conference" }, + { 0x08, 0x0027, "Standby" }, + { 0x08, 0x0028, "CameraOn" }, + { 0x08, 0x0029, "CameraOff" }, + { 0x08, 0x002a, "OnLine" }, + { 0x08, 0x002b, "OffLine" }, + { 0x08, 0x002c, "Busy" }, + { 0x08, 0x002d, "Ready" }, + { 0x08, 0x002e, "PaperOut" }, + { 0x08, 0x002f, "PaperJam" }, + { 0x08, 0x0030, "Remote" }, + { 0x08, 0x0031, "Forward" }, + { 0x08, 0x0032, "Reverse" }, + { 0x08, 0x0033, "Stop" }, + { 0x08, 0x0034, "Rewind" }, + { 0x08, 0x0035, "FastForward" }, + { 0x08, 0x0036, "Play" }, + { 0x08, 0x0037, "Pause" }, + { 0x08, 0x0038, "Record" }, + { 0x08, 0x0039, "Error" }, + { 0x08, 0x003a, "UsageSelectedIndicator" }, + { 0x08, 0x003b, "UsageInUseIndicator" }, + { 0x08, 0x003c, "UsageMultiModeIndicator" }, + { 0x08, 0x003d, "IndicatorOn" }, + { 0x08, 0x003e, "IndicatorFlash" }, + { 0x08, 0x003f, "IndicatorSlowBlink" }, + { 0x08, 0x0040, "IndicatorFastBlink" }, + { 0x08, 0x0041, "IndicatorOff" }, + { 0x08, 0x0042, "FlashOnTime" }, + { 0x08, 0x0043, "SlowBlinkOnTime" }, + { 0x08, 0x0044, "SlowBlinkOffTime" }, + { 0x08, 0x0045, "FastBlinkOnTime" }, + { 0x08, 0x0046, "FastBlinkOffTime" }, + { 0x08, 0x0047, "UsageIndicatorColor" }, + { 0x08, 0x0048, "IndicatorRed" }, + { 0x08, 0x0049, "IndicatorGreen" }, + { 0x08, 0x004a, "IndicatorAmber" }, + { 0x08, 0x004b, "GenericIndicator" }, + { 0x08, 0x004c, "SystemSuspend" }, + { 0x08, 0x004d, "ExternalPowerConnected" }, + { 0x08, 0x004e, "IndicatorBlue" }, + { 0x08, 0x004f, "IndicatorOrange" }, + { 0x08, 0x0050, "GoodStatus" }, + { 0x08, 0x0051, "WarningStatus" }, + { 0x08, 0x0052, "RGBLED" }, + { 0x08, 0x0053, "RedLEDChannel" }, + { 0x08, 0x0054, "BlueLEDChannel" }, + { 0x08, 0x0055, "GreenLEDChannel" }, + { 0x08, 0x0056, "LEDIntensity" }, + { 0x08, 0x0057, "SystemMicrophoneMute" }, + { 0x08, 0x0060, "PlayerIndicator" }, + { 0x08, 0x0061, "Player1" }, + { 0x08, 0x0062, "Player2" }, + { 0x08, 0x0063, "Player3" }, + { 0x08, 0x0064, "Player4" }, + { 0x08, 0x0065, "Player5" }, + { 0x08, 0x0066, "Player6" }, + { 0x08, 0x0067, "Player7" }, + { 0x08, 0x0068, "Player8" }, + { 0x09, 0, "Button" }, + { 0x0a, 0, "Ordinal" }, + { 0x0b, 0, "TelephonyDevice" }, + { 0x0b, 0x0001, "Phone" }, + { 0x0b, 0x0002, "AnsweringMachine" }, + { 0x0b, 0x0003, "MessageControls" }, + { 0x0b, 0x0004, "Handset" }, + { 0x0b, 0x0005, "Headset" }, + { 0x0b, 0x0006, "TelephonyKeyPad" }, + { 0x0b, 0x0007, "ProgrammableButton" }, + { 0x0b, 0x0020, "HookSwitch" }, + { 0x0b, 0x0021, "Flash" }, + { 0x0b, 0x0022, "Feature" }, + { 0x0b, 0x0023, "Hold" }, + { 0x0b, 0x0024, "Redial" }, + { 0x0b, 0x0025, "Transfer" }, + { 0x0b, 0x0026, "Drop" }, + { 0x0b, 0x0027, "Park" }, + { 0x0b, 0x0028, "ForwardCalls" }, + { 0x0b, 0x0029, "AlternateFunction" }, + { 0x0b, 0x002a, "Line" }, + { 0x0b, 0x002b, "SpeakerPhone" }, + { 0x0b, 0x002c, "Conference" }, + { 0x0b, 0x002d, "RingEnable" }, + { 0x0b, 0x002e, "RingSelect" }, + { 0x0b, 0x002f, "PhoneMute" }, + { 0x0b, 0x0030, "CallerID" }, + { 0x0b, 0x0031, "Send" }, + { 0x0b, 0x0050, "SpeedDial" }, + { 0x0b, 0x0051, "StoreNumber" }, + { 0x0b, 0x0052, "RecallNumber" }, + { 0x0b, 0x0053, "PhoneDirectory" }, + { 0x0b, 0x0070, "VoiceMail" }, + { 0x0b, 0x0071, "ScreenCalls" }, + { 0x0b, 0x0072, "DoNotDisturb" }, + { 0x0b, 0x0073, "Message" }, + { 0x0b, 0x0074, "AnswerOnOff" }, + { 0x0b, 0x0090, "InsideDialTone" }, + { 0x0b, 0x0091, "OutsideDialTone" }, + { 0x0b, 0x0092, "InsideRingTone" }, + { 0x0b, 0x0093, "OutsideRingTone" }, + { 0x0b, 0x0094, "PriorityRingTone" }, + { 0x0b, 0x0095, "InsideRingback" }, + { 0x0b, 0x0096, "PriorityRingback" }, + { 0x0b, 0x0097, "LineBusyTone" }, + { 0x0b, 0x0098, "ReorderTone" }, + { 0x0b, 0x0099, "CallWaitingTone" }, + { 0x0b, 0x009a, "ConfirmationTone1" }, + { 0x0b, 0x009b, "ConfirmationTone2" }, + { 0x0b, 0x009c, "TonesOff" }, + { 0x0b, 0x009d, "OutsideRingback" }, + { 0x0b, 0x009e, "Ringer" }, + { 0x0b, 0x00b0, "PhoneKey0" }, + { 0x0b, 0x00b1, "PhoneKey1" }, + { 0x0b, 0x00b2, "PhoneKey2" }, + { 0x0b, 0x00b3, "PhoneKey3" }, + { 0x0b, 0x00b4, "PhoneKey4" }, + { 0x0b, 0x00b5, "PhoneKey5" }, + { 0x0b, 0x00b6, "PhoneKey6" }, + { 0x0b, 0x00b7, "PhoneKey7" }, + { 0x0b, 0x00b8, "PhoneKey8" }, + { 0x0b, 0x00b9, "PhoneKey9" }, + { 0x0b, 0x00ba, "PhoneKeyStar" }, + { 0x0b, 0x00bb, "PhoneKeyPound" }, + { 0x0b, 0x00bc, "PhoneKeyA" }, + { 0x0b, 0x00bd, "PhoneKeyB" }, + { 0x0b, 0x00be, "PhoneKeyC" }, + { 0x0b, 0x00bf, "PhoneKeyD" }, + { 0x0b, 0x00c0, "PhoneCallHistoryKey" }, + { 0x0b, 0x00c1, "PhoneCallerIDKey" }, + { 0x0b, 0x00c2, "PhoneSettingsKey" }, + { 0x0b, 0x00f0, "HostControl" }, + { 0x0b, 0x00f1, "HostAvailable" }, + { 0x0b, 0x00f2, "HostCallActive" }, + { 0x0b, 0x00f3, "ActivateHandsetAudio" }, + { 0x0b, 0x00f4, "RingType" }, + { 0x0b, 0x00f5, "RedialablePhoneNumber" }, + { 0x0b, 0x00f8, "StopRingTone" }, + { 0x0b, 0x00f9, "PSTNRingTone" }, + { 0x0b, 0x00fa, "HostRingTone" }, + { 0x0b, 0x00fb, "AlertSoundError" }, + { 0x0b, 0x00fc, "AlertSoundConfirm" }, + { 0x0b, 0x00fd, "AlertSoundNotification" }, + { 0x0b, 0x00fe, "SilentRing" }, + { 0x0b, 0x0108, "EmailMessageWaiting" }, + { 0x0b, 0x0109, "VoicemailMessageWaiting" }, + { 0x0b, 0x010a, "HostHold" }, + { 0x0b, 0x0110, "IncomingCallHistoryCount" }, + { 0x0b, 0x0111, "OutgoingCallHistoryCount" }, + { 0x0b, 0x0112, "IncomingCallHistory" }, + { 0x0b, 0x0113, "OutgoingCallHistory" }, + { 0x0b, 0x0114, "PhoneLocale" }, + { 0x0b, 0x0140, "PhoneTimeSecond" }, + { 0x0b, 0x0141, "PhoneTimeMinute" }, + { 0x0b, 0x0142, "PhoneTimeHour" }, + { 0x0b, 0x0143, "PhoneDateDay" }, + { 0x0b, 0x0144, "PhoneDateMonth" }, + { 0x0b, 0x0145, "PhoneDateYear" }, + { 0x0b, 0x0146, "HandsetNickname" }, + { 0x0b, 0x0147, "AddressBookID" }, + { 0x0b, 0x014a, "CallDuration" }, + { 0x0b, 0x014b, "DualModePhone" }, + { 0x0c, 0, "Consumer" }, + { 0x0c, 0x0001, "ConsumerControl" }, + { 0x0c, 0x0002, "NumericKeyPad" }, + { 0x0c, 0x0003, "ProgrammableButtons" }, + { 0x0c, 0x0004, "Microphone" }, + { 0x0c, 0x0005, "Headphone" }, + { 0x0c, 0x0006, "GraphicEqualizer" }, + { 0x0c, 0x0020, "10" }, + { 0x0c, 0x0021, "100" }, + { 0x0c, 0x0022, "AMPM" }, + { 0x0c, 0x0030, "Power" }, + { 0x0c, 0x0031, "Reset" }, + { 0x0c, 0x0032, "Sleep" }, + { 0x0c, 0x0033, "SleepAfter" }, + { 0x0c, 0x0034, "SleepMode" }, + { 0x0c, 0x0035, "Illumination" }, + { 0x0c, 0x0036, "FunctionButtons" }, + { 0x0c, 0x0040, "Menu" }, + { 0x0c, 0x0041, "MenuPick" }, + { 0x0c, 0x0042, "MenuUp" }, + { 0x0c, 0x0043, "MenuDown" }, + { 0x0c, 0x0044, "MenuLeft" }, + { 0x0c, 0x0045, "MenuRight" }, + { 0x0c, 0x0046, "MenuEscape" }, + { 0x0c, 0x0047, "MenuValueIncrease" }, + { 0x0c, 0x0048, "MenuValueDecrease" }, + { 0x0c, 0x0060, "DataOnScreen" }, + { 0x0c, 0x0061, "ClosedCaption" }, + { 0x0c, 0x0062, "ClosedCaptionSelect" }, + { 0x0c, 0x0063, "VCRTV" }, + { 0x0c, 0x0064, "BroadcastMode" }, + { 0x0c, 0x0065, "Snapshot" }, + { 0x0c, 0x0066, "Still" }, + { 0x0c, 0x0067, "PictureinPictureToggle" }, + { 0x0c, 0x0068, "PictureinPictureSwap" }, + { 0x0c, 0x0069, "RedMenuButton" }, + { 0x0c, 0x006a, "GreenMenuButton" }, + { 0x0c, 0x006b, "BlueMenuButton" }, + { 0x0c, 0x006c, "YellowMenuButton" }, + { 0x0c, 0x006d, "Aspect" }, + { 0x0c, 0x006e, "3DModeSelect" }, + { 0x0c, 0x006f, "DisplayBrightnessIncrement" }, + { 0x0c, 0x0070, "DisplayBrightnessDecrement" }, + { 0x0c, 0x0071, "DisplayBrightness" }, + { 0x0c, 0x0072, "DisplayBacklightToggle" }, + { 0x0c, 0x0073, "DisplaySetBrightnesstoMinimum" }, + { 0x0c, 0x0074, "DisplaySetBrightnesstoMaximum" }, + { 0x0c, 0x0075, "DisplaySetAutoBrightness" }, + { 0x0c, 0x0076, "CameraAccessEnabled" }, + { 0x0c, 0x0077, "CameraAccessDisabled" }, + { 0x0c, 0x0078, "CameraAccessToggle" }, + { 0x0c, 0x0079, "KeyboardBrightnessIncrement" }, + { 0x0c, 0x007a, "KeyboardBrightnessDecrement" }, + { 0x0c, 0x007b, "KeyboardBacklightSetLevel" }, + { 0x0c, 0x007c, "KeyboardBacklightOOC" }, + { 0x0c, 0x007d, "KeyboardBacklightSetMinimum" }, + { 0x0c, 0x007e, "KeyboardBacklightSetMaximum" }, + { 0x0c, 0x007f, "KeyboardBacklightAuto" }, + { 0x0c, 0x0080, "Selection" }, + { 0x0c, 0x0081, "AssignSelection" }, + { 0x0c, 0x0082, "ModeStep" }, + { 0x0c, 0x0083, "RecallLast" }, + { 0x0c, 0x0084, "EnterChannel" }, + { 0x0c, 0x0085, "OrderMovie" }, + { 0x0c, 0x0086, "Channel" }, + { 0x0c, 0x0087, "MediaSelection" }, + { 0x0c, 0x0088, "MediaSelectComputer" }, + { 0x0c, 0x0089, "MediaSelectTV" }, + { 0x0c, 0x008a, "MediaSelectWWW" }, + { 0x0c, 0x008b, "MediaSelectDVD" }, + { 0x0c, 0x008c, "MediaSelectTelephone" }, + { 0x0c, 0x008d, "MediaSelectProgramGuide" }, + { 0x0c, 0x008e, "MediaSelectVideoPhone" }, + { 0x0c, 0x008f, "MediaSelectGames" }, + { 0x0c, 0x0090, "MediaSelectMessages" }, + { 0x0c, 0x0091, "MediaSelectCD" }, + { 0x0c, 0x0092, "MediaSelectVCR" }, + { 0x0c, 0x0093, "MediaSelectTuner" }, + { 0x0c, 0x0094, "Quit" }, + { 0x0c, 0x0095, "Help" }, + { 0x0c, 0x0096, "MediaSelectTape" }, + { 0x0c, 0x0097, "MediaSelectCable" }, + { 0x0c, 0x0098, "MediaSelectSatellite" }, + { 0x0c, 0x0099, "MediaSelectSecurity" }, + { 0x0c, 0x009a, "MediaSelectHome" }, + { 0x0c, 0x009b, "MediaSelectCall" }, + { 0x0c, 0x009c, "ChannelIncrement" }, + { 0x0c, 0x009d, "ChannelDecrement" }, + { 0x0c, 0x009e, "MediaSelectSAP" }, + { 0x0c, 0x00a0, "VCRPlus" }, + { 0x0c, 0x00a1, "Once" }, + { 0x0c, 0x00a2, "Daily" }, + { 0x0c, 0x00a3, "Weekly" }, + { 0x0c, 0x00a4, "Monthly" }, + { 0x0c, 0x00b0, "Play" }, + { 0x0c, 0x00b1, "Pause" }, + { 0x0c, 0x00b2, "Record" }, + { 0x0c, 0x00b3, "FastForward" }, + { 0x0c, 0x00b4, "Rewind" }, + { 0x0c, 0x00b5, "ScanNextTrack" }, + { 0x0c, 0x00b6, "ScanPreviousTrack" }, + { 0x0c, 0x00b7, "Stop" }, + { 0x0c, 0x00b8, "Eject" }, + { 0x0c, 0x00b9, "RandomPlay" }, + { 0x0c, 0x00ba, "SelectDisc" }, + { 0x0c, 0x00bb, "EnterDisc" }, + { 0x0c, 0x00bc, "Repeat" }, + { 0x0c, 0x00bd, "Tracking" }, + { 0x0c, 0x00be, "TrackNormal" }, + { 0x0c, 0x00bf, "SlowTracking" }, + { 0x0c, 0x00c0, "FrameForward" }, + { 0x0c, 0x00c1, "FrameBack" }, + { 0x0c, 0x00c2, "Mark" }, + { 0x0c, 0x00c3, "ClearMark" }, + { 0x0c, 0x00c4, "RepeatFromMark" }, + { 0x0c, 0x00c5, "ReturnToMark" }, + { 0x0c, 0x00c6, "SearchMarkForward" }, + { 0x0c, 0x00c7, "SearchMarkBackwards" }, + { 0x0c, 0x00c8, "CounterReset" }, + { 0x0c, 0x00c9, "ShowCounter" }, + { 0x0c, 0x00ca, "TrackingIncrement" }, + { 0x0c, 0x00cb, "TrackingDecrement" }, + { 0x0c, 0x00cc, "StopEject" }, + { 0x0c, 0x00cd, "PlayPause" }, + { 0x0c, 0x00ce, "PlaySkip" }, + { 0x0c, 0x00cf, "VoiceCommand" }, + { 0x0c, 0x00d0, "InvokeCaptureInterface" }, + { 0x0c, 0x00d1, "StartorStopGameRecording" }, + { 0x0c, 0x00d2, "HistoricalGameCapture" }, + { 0x0c, 0x00d3, "CaptureGameScreenshot" }, + { 0x0c, 0x00d4, "ShoworHideRecordingIndicator" }, + { 0x0c, 0x00d5, "StartorStopMicrophoneCapture" }, + { 0x0c, 0x00d6, "StartorStopCameraCapture" }, + { 0x0c, 0x00d7, "StartorStopGameBroadcast" }, + { 0x0c, 0x00d8, "StartorStopVoiceDictationSession" }, + { 0x0c, 0x00d9, "InvokeDismissEmojiPicker" }, + { 0x0c, 0x00e0, "Volume" }, + { 0x0c, 0x00e1, "Balance" }, + { 0x0c, 0x00e2, "Mute" }, + { 0x0c, 0x00e3, "Bass" }, + { 0x0c, 0x00e4, "Treble" }, + { 0x0c, 0x00e5, "BassBoost" }, + { 0x0c, 0x00e6, "SurroundMode" }, + { 0x0c, 0x00e7, "Loudness" }, + { 0x0c, 0x00e8, "MPX" }, + { 0x0c, 0x00e9, "VolumeIncrement" }, + { 0x0c, 0x00ea, "VolumeDecrement" }, + { 0x0c, 0x00f0, "SpeedSelect" }, + { 0x0c, 0x00f1, "PlaybackSpeed" }, + { 0x0c, 0x00f2, "StandardPlay" }, + { 0x0c, 0x00f3, "LongPlay" }, + { 0x0c, 0x00f4, "ExtendedPlay" }, + { 0x0c, 0x00f5, "Slow" }, + { 0x0c, 0x0100, "FanEnable" }, + { 0x0c, 0x0101, "FanSpeed" }, + { 0x0c, 0x0102, "LightEnable" }, + { 0x0c, 0x0103, "LightIlluminationLevel" }, + { 0x0c, 0x0104, "ClimateControlEnable" }, + { 0x0c, 0x0105, "RoomTemperature" }, + { 0x0c, 0x0106, "SecurityEnable" }, + { 0x0c, 0x0107, "FireAlarm" }, + { 0x0c, 0x0108, "PoliceAlarm" }, + { 0x0c, 0x0109, "Proximity" }, + { 0x0c, 0x010a, "Motion" }, + { 0x0c, 0x010b, "DuressAlarm" }, + { 0x0c, 0x010c, "HoldupAlarm" }, + { 0x0c, 0x010d, "MedicalAlarm" }, + { 0x0c, 0x0150, "BalanceRight" }, + { 0x0c, 0x0151, "BalanceLeft" }, + { 0x0c, 0x0152, "BassIncrement" }, + { 0x0c, 0x0153, "BassDecrement" }, + { 0x0c, 0x0154, "TrebleIncrement" }, + { 0x0c, 0x0155, "TrebleDecrement" }, + { 0x0c, 0x0160, "SpeakerSystem" }, + { 0x0c, 0x0161, "ChannelLeft" }, + { 0x0c, 0x0162, "ChannelRight" }, + { 0x0c, 0x0163, "ChannelCenter" }, + { 0x0c, 0x0164, "ChannelFront" }, + { 0x0c, 0x0165, "ChannelCenterFront" }, + { 0x0c, 0x0166, "ChannelSide" }, + { 0x0c, 0x0167, "ChannelSurround" }, + { 0x0c, 0x0168, "ChannelLowFrequencyEnhancement" }, + { 0x0c, 0x0169, "ChannelTop" }, + { 0x0c, 0x016a, "ChannelUnknown" }, + { 0x0c, 0x0170, "Subchannel" }, + { 0x0c, 0x0171, "SubchannelIncrement" }, + { 0x0c, 0x0172, "SubchannelDecrement" }, + { 0x0c, 0x0173, "AlternateAudioIncrement" }, + { 0x0c, 0x0174, "AlternateAudioDecrement" }, + { 0x0c, 0x0180, "ApplicationLaunchButtons" }, + { 0x0c, 0x0181, "ALLaunchButtonConfigurationTool" }, + { 0x0c, 0x0182, "ALProgrammableButtonConfiguration" }, + { 0x0c, 0x0183, "ALConsumerControlConfiguration" }, + { 0x0c, 0x0184, "ALWordProcessor" }, + { 0x0c, 0x0185, "ALTextEditor" }, + { 0x0c, 0x0186, "ALSpreadsheet" }, + { 0x0c, 0x0187, "ALGraphicsEditor" }, + { 0x0c, 0x0188, "ALPresentationApp" }, + { 0x0c, 0x0189, "ALDatabaseApp" }, + { 0x0c, 0x018a, "ALEmailReader" }, + { 0x0c, 0x018b, "ALNewsreader" }, + { 0x0c, 0x018c, "ALVoicemail" }, + { 0x0c, 0x018d, "ALContactsAddressBook" }, + { 0x0c, 0x018e, "ALCalendarSchedule" }, + { 0x0c, 0x018f, "ALTaskProjectManager" }, + { 0x0c, 0x0190, "ALLogJournalTimecard" }, + { 0x0c, 0x0191, "ALCheckbookFinance" }, + { 0x0c, 0x0192, "ALCalculator" }, + { 0x0c, 0x0193, "ALAVCapturePlayback" }, + { 0x0c, 0x0194, "ALLocalMachineBrowser" }, + { 0x0c, 0x0195, "ALLANWANBrowser" }, + { 0x0c, 0x0196, "ALInternetBrowser" }, + { 0x0c, 0x0197, "ALRemoteNetworkingISPConnect" }, + { 0x0c, 0x0198, "ALNetworkConference" }, + { 0x0c, 0x0199, "ALNetworkChat" }, + { 0x0c, 0x019a, "ALTelephonyDialer" }, + { 0x0c, 0x019b, "ALLogon" }, + { 0x0c, 0x019c, "ALLogoff" }, + { 0x0c, 0x019d, "ALLogonLogoff" }, + { 0x0c, 0x019e, "ALTerminalLockScreensaver" }, + { 0x0c, 0x019f, "ALControlPanel" }, + { 0x0c, 0x01a0, "ALCommandLineProcessorRun" }, + { 0x0c, 0x01a1, "ALProcessTaskManager" }, + { 0x0c, 0x01a2, "ALSelectTaskApplication" }, + { 0x0c, 0x01a3, "ALNextTaskApplication" }, + { 0x0c, 0x01a4, "ALPreviousTaskApplication" }, + { 0x0c, 0x01a5, "ALPreemptiveHaltTaskApplication" }, + { 0x0c, 0x01a6, "ALIntegratedHelpCenter" }, + { 0x0c, 0x01a7, "ALDocuments" }, + { 0x0c, 0x01a8, "ALThesaurus" }, + { 0x0c, 0x01a9, "ALDictionary" }, + { 0x0c, 0x01aa, "ALDesktop" }, + { 0x0c, 0x01ab, "ALSpellCheck" }, + { 0x0c, 0x01ac, "ALGrammarCheck" }, + { 0x0c, 0x01ad, "ALWirelessStatus" }, + { 0x0c, 0x01ae, "ALKeyboardLayout" }, + { 0x0c, 0x01af, "ALVirusProtection" }, + { 0x0c, 0x01b0, "ALEncryption" }, + { 0x0c, 0x01b1, "ALScreenSaver" }, + { 0x0c, 0x01b2, "ALAlarms" }, + { 0x0c, 0x01b3, "ALClock" }, + { 0x0c, 0x01b4, "ALFileBrowser" }, + { 0x0c, 0x01b5, "ALPowerStatus" }, + { 0x0c, 0x01b6, "ALImageBrowser" }, + { 0x0c, 0x01b7, "ALAudioBrowser" }, + { 0x0c, 0x01b8, "ALMovieBrowser" }, + { 0x0c, 0x01b9, "ALDigitalRightsManager" }, + { 0x0c, 0x01ba, "ALDigitalWallet" }, + { 0x0c, 0x01bc, "ALInstantMessaging" }, + { 0x0c, 0x01bd, "ALOEMFeaturesTipsTutorialBrowser" }, + { 0x0c, 0x01be, "ALOEMHelp" }, + { 0x0c, 0x01bf, "ALOnlineCommunity" }, + { 0x0c, 0x01c0, "ALEntertainmentContentBrowser" }, + { 0x0c, 0x01c1, "ALOnlineShoppingBrowser" }, + { 0x0c, 0x01c2, "ALSmartCardInformationHelp" }, + { 0x0c, 0x01c3, "ALMarketMonitorFinanceBrowser" }, + { 0x0c, 0x01c4, "ALCustomizedCorporateNewsBrowser" }, + { 0x0c, 0x01c5, "ALOnlineActivityBrowser" }, + { 0x0c, 0x01c6, "ALResearchSearchBrowser" }, + { 0x0c, 0x01c7, "ALAudioPlayer" }, + { 0x0c, 0x01c8, "ALMessageStatus" }, + { 0x0c, 0x01c9, "ALContactSync" }, + { 0x0c, 0x01ca, "ALNavigation" }, + { 0x0c, 0x01cb, "ALContextawareDesktopAssistant" }, + { 0x0c, 0x0200, "GenericGUIApplicationControls" }, + { 0x0c, 0x0201, "ACNew" }, + { 0x0c, 0x0202, "ACOpen" }, + { 0x0c, 0x0203, "ACClose" }, + { 0x0c, 0x0204, "ACExit" }, + { 0x0c, 0x0205, "ACMaximize" }, + { 0x0c, 0x0206, "ACMinimize" }, + { 0x0c, 0x0207, "ACSave" }, + { 0x0c, 0x0208, "ACPrint" }, + { 0x0c, 0x0209, "ACProperties" }, + { 0x0c, 0x021a, "ACUndo" }, + { 0x0c, 0x021b, "ACCopy" }, + { 0x0c, 0x021c, "ACCut" }, + { 0x0c, 0x021d, "ACPaste" }, + { 0x0c, 0x021e, "ACSelectAll" }, + { 0x0c, 0x021f, "ACFind" }, + { 0x0c, 0x0220, "ACFindandReplace" }, + { 0x0c, 0x0221, "ACSearch" }, + { 0x0c, 0x0222, "ACGoTo" }, + { 0x0c, 0x0223, "ACHome" }, + { 0x0c, 0x0224, "ACBack" }, + { 0x0c, 0x0225, "ACForward" }, + { 0x0c, 0x0226, "ACStop" }, + { 0x0c, 0x0227, "ACRefresh" }, + { 0x0c, 0x0228, "ACPreviousLink" }, + { 0x0c, 0x0229, "ACNextLink" }, + { 0x0c, 0x022a, "ACBookmarks" }, + { 0x0c, 0x022b, "ACHistory" }, + { 0x0c, 0x022c, "ACSubscriptions" }, + { 0x0c, 0x022d, "ACZoomIn" }, + { 0x0c, 0x022e, "ACZoomOut" }, + { 0x0c, 0x022f, "ACZoom" }, + { 0x0c, 0x0230, "ACFullScreenView" }, + { 0x0c, 0x0231, "ACNormalView" }, + { 0x0c, 0x0232, "ACViewToggle" }, + { 0x0c, 0x0233, "ACScrollUp" }, + { 0x0c, 0x0234, "ACScrollDown" }, + { 0x0c, 0x0235, "ACScroll" }, + { 0x0c, 0x0236, "ACPanLeft" }, + { 0x0c, 0x0237, "ACPanRight" }, + { 0x0c, 0x0238, "ACPan" }, + { 0x0c, 0x0239, "ACNewWindow" }, + { 0x0c, 0x023a, "ACTileHorizontally" }, + { 0x0c, 0x023b, "ACTileVertically" }, + { 0x0c, 0x023c, "ACFormat" }, + { 0x0c, 0x023d, "ACEdit" }, + { 0x0c, 0x023e, "ACBold" }, + { 0x0c, 0x023f, "ACItalics" }, + { 0x0c, 0x0240, "ACUnderline" }, + { 0x0c, 0x0241, "ACStrikethrough" }, + { 0x0c, 0x0242, "ACSubscript" }, + { 0x0c, 0x0243, "ACSuperscript" }, + { 0x0c, 0x0244, "ACAllCaps" }, + { 0x0c, 0x0245, "ACRotate" }, + { 0x0c, 0x0246, "ACResize" }, + { 0x0c, 0x0247, "ACFlipHorizontal" }, + { 0x0c, 0x0248, "ACFlipVertical" }, + { 0x0c, 0x0249, "ACMirrorHorizontal" }, + { 0x0c, 0x024a, "ACMirrorVertical" }, + { 0x0c, 0x024b, "ACFontSelect" }, + { 0x0c, 0x024c, "ACFontColor" }, + { 0x0c, 0x024d, "ACFontSize" }, + { 0x0c, 0x024e, "ACJustifyLeft" }, + { 0x0c, 0x024f, "ACJustifyCenterH" }, + { 0x0c, 0x0250, "ACJustifyRight" }, + { 0x0c, 0x0251, "ACJustifyBlockH" }, + { 0x0c, 0x0252, "ACJustifyTop" }, + { 0x0c, 0x0253, "ACJustifyCenterV" }, + { 0x0c, 0x0254, "ACJustifyBottom" }, + { 0x0c, 0x0255, "ACJustifyBlockV" }, + { 0x0c, 0x0256, "ACIndentDecrease" }, + { 0x0c, 0x0257, "ACIndentIncrease" }, + { 0x0c, 0x0258, "ACNumberedList" }, + { 0x0c, 0x0259, "ACRestartNumbering" }, + { 0x0c, 0x025a, "ACBulletedList" }, + { 0x0c, 0x025b, "ACPromote" }, + { 0x0c, 0x025c, "ACDemote" }, + { 0x0c, 0x025d, "ACYes" }, + { 0x0c, 0x025e, "ACNo" }, + { 0x0c, 0x025f, "ACCancel" }, + { 0x0c, 0x0260, "ACCatalog" }, + { 0x0c, 0x0261, "ACBuyCheckout" }, + { 0x0c, 0x0262, "ACAddtoCart" }, + { 0x0c, 0x0263, "ACExpand" }, + { 0x0c, 0x0264, "ACExpandAll" }, + { 0x0c, 0x0265, "ACCollapse" }, + { 0x0c, 0x0266, "ACCollapseAll" }, + { 0x0c, 0x0267, "ACPrintPreview" }, + { 0x0c, 0x0268, "ACPasteSpecial" }, + { 0x0c, 0x0269, "ACInsertMode" }, + { 0x0c, 0x026a, "ACDelete" }, + { 0x0c, 0x026b, "ACLock" }, + { 0x0c, 0x026c, "ACUnlock" }, + { 0x0c, 0x026d, "ACProtect" }, + { 0x0c, 0x026e, "ACUnprotect" }, + { 0x0c, 0x026f, "ACAttachComment" }, + { 0x0c, 0x0270, "ACDeleteComment" }, + { 0x0c, 0x0271, "ACViewComment" }, + { 0x0c, 0x0272, "ACSelectWord" }, + { 0x0c, 0x0273, "ACSelectSentence" }, + { 0x0c, 0x0274, "ACSelectParagraph" }, + { 0x0c, 0x0275, "ACSelectColumn" }, + { 0x0c, 0x0276, "ACSelectRow" }, + { 0x0c, 0x0277, "ACSelectTable" }, + { 0x0c, 0x0278, "ACSelectObject" }, + { 0x0c, 0x0279, "ACRedoRepeat" }, + { 0x0c, 0x027a, "ACSort" }, + { 0x0c, 0x027b, "ACSortAscending" }, + { 0x0c, 0x027c, "ACSortDescending" }, + { 0x0c, 0x027d, "ACFilter" }, + { 0x0c, 0x027e, "ACSetClock" }, + { 0x0c, 0x027f, "ACViewClock" }, + { 0x0c, 0x0280, "ACSelectTimeZone" }, + { 0x0c, 0x0281, "ACEditTimeZones" }, + { 0x0c, 0x0282, "ACSetAlarm" }, + { 0x0c, 0x0283, "ACClearAlarm" }, + { 0x0c, 0x0284, "ACSnoozeAlarm" }, + { 0x0c, 0x0285, "ACResetAlarm" }, + { 0x0c, 0x0286, "ACSynchronize" }, + { 0x0c, 0x0287, "ACSendReceive" }, + { 0x0c, 0x0288, "ACSendTo" }, + { 0x0c, 0x0289, "ACReply" }, + { 0x0c, 0x028a, "ACReplyAll" }, + { 0x0c, 0x028b, "ACForwardMsg" }, + { 0x0c, 0x028c, "ACSend" }, + { 0x0c, 0x028d, "ACAttachFile" }, + { 0x0c, 0x028e, "ACUpload" }, + { 0x0c, 0x028f, "ACDownloadSaveTargetAs" }, + { 0x0c, 0x0290, "ACSetBorders" }, + { 0x0c, 0x0291, "ACInsertRow" }, + { 0x0c, 0x0292, "ACInsertColumn" }, + { 0x0c, 0x0293, "ACInsertFile" }, + { 0x0c, 0x0294, "ACInsertPicture" }, + { 0x0c, 0x0295, "ACInsertObject" }, + { 0x0c, 0x0296, "ACInsertSymbol" }, + { 0x0c, 0x0297, "ACSaveandClose" }, + { 0x0c, 0x0298, "ACRename" }, + { 0x0c, 0x0299, "ACMerge" }, + { 0x0c, 0x029a, "ACSplit" }, + { 0x0c, 0x029b, "ACDisributeHorizontally" }, + { 0x0c, 0x029c, "ACDistributeVertically" }, + { 0x0c, 0x029d, "ACNextKeyboardLayoutSelect" }, + { 0x0c, 0x029e, "ACNavigationGuidance" }, + { 0x0c, 0x029f, "ACDesktopShowAllWindows" }, + { 0x0c, 0x02a0, "ACSoftKeyLeft" }, + { 0x0c, 0x02a1, "ACSoftKeyRight" }, + { 0x0c, 0x02a2, "ACDesktopShowAllApplications" }, + { 0x0c, 0x02b0, "ACIdleKeepAlive" }, + { 0x0c, 0x02c0, "ExtendedKeyboardAttributesCollection" }, + { 0x0c, 0x02c1, "KeyboardFormFactor" }, + { 0x0c, 0x02c2, "KeyboardKeyType" }, + { 0x0c, 0x02c3, "KeyboardPhysicalLayout" }, + { 0x0c, 0x02c4, "VendorSpecificKeyboardPhysicalLayout" }, + { 0x0c, 0x02c5, "KeyboardIETFLanguageTagIndex" }, + { 0x0c, 0x02c6, "ImplementedKeyboardInputAssistControls" }, + { 0x0c, 0x02c7, "KeyboardInputAssistPrevious" }, + { 0x0c, 0x02c8, "KeyboardInputAssistNext" }, + { 0x0c, 0x02c9, "KeyboardInputAssistPreviousGroup" }, + { 0x0c, 0x02ca, "KeyboardInputAssistNextGroup" }, + { 0x0c, 0x02cb, "KeyboardInputAssistAccept" }, + { 0x0c, 0x02cc, "KeyboardInputAssistCancel" }, + { 0x0c, 0x02d0, "PrivacyScreenToggle" }, + { 0x0c, 0x02d1, "PrivacyScreenLevelDecrement" }, + { 0x0c, 0x02d2, "PrivacyScreenLevelIncrement" }, + { 0x0c, 0x02d3, "PrivacyScreenLevelMinimum" }, + { 0x0c, 0x02d4, "PrivacyScreenLevelMaximum" }, + { 0x0c, 0x0500, "ContactEdited" }, + { 0x0c, 0x0501, "ContactAdded" }, + { 0x0c, 0x0502, "ContactRecordActive" }, + { 0x0c, 0x0503, "ContactIndex" }, + { 0x0c, 0x0504, "ContactNickname" }, + { 0x0c, 0x0505, "ContactFirstName" }, + { 0x0c, 0x0506, "ContactLastName" }, + { 0x0c, 0x0507, "ContactFullName" }, + { 0x0c, 0x0508, "ContactPhoneNumberPersonal" }, + { 0x0c, 0x0509, "ContactPhoneNumberBusiness" }, + { 0x0c, 0x050a, "ContactPhoneNumberMobile" }, + { 0x0c, 0x050b, "ContactPhoneNumberPager" }, + { 0x0c, 0x050c, "ContactPhoneNumberFax" }, + { 0x0c, 0x050d, "ContactPhoneNumberOther" }, + { 0x0c, 0x050e, "ContactEmailPersonal" }, + { 0x0c, 0x050f, "ContactEmailBusiness" }, + { 0x0c, 0x0510, "ContactEmailOther" }, + { 0x0c, 0x0511, "ContactEmailMain" }, + { 0x0c, 0x0512, "ContactSpeedDialNumber" }, + { 0x0c, 0x0513, "ContactStatusFlag" }, + { 0x0c, 0x0514, "ContactMisc" }, + { 0x0d, 0, "Digitizers" }, + { 0x0d, 0x0001, "Digitizer" }, + { 0x0d, 0x0002, "Pen" }, + { 0x0d, 0x0003, "LightPen" }, + { 0x0d, 0x0004, "TouchScreen" }, + { 0x0d, 0x0005, "TouchPad" }, + { 0x0d, 0x0006, "Whiteboard" }, + { 0x0d, 0x0007, "CoordinateMeasuringMachine" }, + { 0x0d, 0x0008, "3DDigitizer" }, + { 0x0d, 0x0009, "StereoPlotter" }, + { 0x0d, 0x000a, "ArticulatedArm" }, + { 0x0d, 0x000b, "Armature" }, + { 0x0d, 0x000c, "MultiplePointDigitizer" }, + { 0x0d, 0x000d, "FreeSpaceWand" }, + { 0x0d, 0x000e, "DeviceConfiguration" }, + { 0x0d, 0x000f, "CapacitiveHeatMapDigitizer" }, + { 0x0d, 0x0020, "Stylus" }, + { 0x0d, 0x0021, "Puck" }, + { 0x0d, 0x0022, "Finger" }, + { 0x0d, 0x0023, "Devicesettings" }, + { 0x0d, 0x0024, "CharacterGesture" }, + { 0x0d, 0x0030, "TipPressure" }, + { 0x0d, 0x0031, "BarrelPressure" }, + { 0x0d, 0x0032, "InRange" }, + { 0x0d, 0x0033, "Touch" }, + { 0x0d, 0x0034, "Untouch" }, + { 0x0d, 0x0035, "Tap" }, + { 0x0d, 0x0036, "Quality" }, + { 0x0d, 0x0037, "DataValid" }, + { 0x0d, 0x0038, "TransducerIndex" }, + { 0x0d, 0x0039, "TabletFunctionKeys" }, + { 0x0d, 0x003a, "ProgramChangeKeys" }, + { 0x0d, 0x003b, "BatteryStrength" }, + { 0x0d, 0x003c, "Invert" }, + { 0x0d, 0x003d, "XTilt" }, + { 0x0d, 0x003e, "YTilt" }, + { 0x0d, 0x003f, "Azimuth" }, + { 0x0d, 0x0040, "Altitude" }, + { 0x0d, 0x0041, "Twist" }, + { 0x0d, 0x0042, "TipSwitch" }, + { 0x0d, 0x0043, "SecondaryTipSwitch" }, + { 0x0d, 0x0044, "BarrelSwitch" }, + { 0x0d, 0x0045, "Eraser" }, + { 0x0d, 0x0046, "TabletPick" }, + { 0x0d, 0x0047, "TouchValid" }, + { 0x0d, 0x0048, "Width" }, + { 0x0d, 0x0049, "Height" }, + { 0x0d, 0x0051, "ContactIdentifier" }, + { 0x0d, 0x0052, "DeviceMode" }, + { 0x0d, 0x0053, "DeviceIdentifier" }, + { 0x0d, 0x0054, "ContactCount" }, + { 0x0d, 0x0055, "ContactCountMaximum" }, + { 0x0d, 0x0056, "ScanTime" }, + { 0x0d, 0x0057, "SurfaceSwitch" }, + { 0x0d, 0x0058, "ButtonSwitch" }, + { 0x0d, 0x0059, "PadType" }, + { 0x0d, 0x005a, "SecondaryBarrelSwitch" }, + { 0x0d, 0x005b, "TransducerSerialNumber" }, + { 0x0d, 0x005c, "PreferredColor" }, + { 0x0d, 0x005d, "PreferredColorisLocked" }, + { 0x0d, 0x005e, "PreferredLineWidth" }, + { 0x0d, 0x005f, "PreferredLineWidthisLocked" }, + { 0x0d, 0x0060, "LatencyMode" }, + { 0x0d, 0x0061, "GestureCharacterQuality" }, + { 0x0d, 0x0062, "CharacterGestureDataLength" }, + { 0x0d, 0x0063, "CharacterGestureData" }, + { 0x0d, 0x0064, "GestureCharacterEncoding" }, + { 0x0d, 0x0065, "UTF8CharacterGestureEncoding" }, + { 0x0d, 0x0066, "UTF16LittleEndianCharacterGestureEncoding" }, + { 0x0d, 0x0067, "UTF16BigEndianCharacterGestureEncoding" }, + { 0x0d, 0x0068, "UTF32LittleEndianCharacterGestureEncoding" }, + { 0x0d, 0x0069, "UTF32BigEndianCharacterGestureEncoding" }, + { 0x0d, 0x006a, "CapacitiveHeatMapProtocolVendorID" }, + { 0x0d, 0x006b, "CapacitiveHeatMapProtocolVersion" }, + { 0x0d, 0x006c, "CapacitiveHeatMapFrameData" }, + { 0x0d, 0x006d, "GestureCharacterEnable" }, + { 0x0d, 0x006e, "TransducerSerialNumberPart2" }, + { 0x0d, 0x006f, "NoPreferredColor" }, + { 0x0d, 0x0070, "PreferredLineStyle" }, + { 0x0d, 0x0071, "PreferredLineStyleisLocked" }, + { 0x0d, 0x0072, "Ink" }, + { 0x0d, 0x0073, "Pencil" }, + { 0x0d, 0x0074, "Highlighter" }, + { 0x0d, 0x0075, "ChiselMarker" }, + { 0x0d, 0x0076, "Brush" }, + { 0x0d, 0x0077, "NoPreference" }, + { 0x0d, 0x0080, "DigitizerDiagnostic" }, + { 0x0d, 0x0081, "DigitizerError" }, + { 0x0d, 0x0082, "ErrNormalStatus" }, + { 0x0d, 0x0083, "ErrTransducersExceeded" }, + { 0x0d, 0x0084, "ErrFullTransFeaturesUnavailable" }, + { 0x0d, 0x0085, "ErrChargeLow" }, + { 0x0d, 0x0090, "TransducerSoftwareInfo" }, + { 0x0d, 0x0091, "TransducerVendorId" }, + { 0x0d, 0x0092, "TransducerProductId" }, + { 0x0d, 0x0093, "DeviceSupportedProtocols" }, + { 0x0d, 0x0094, "TransducerSupportedProtocols" }, + { 0x0d, 0x0095, "NoProtocol" }, + { 0x0d, 0x0096, "WacomAESProtocol" }, + { 0x0d, 0x0097, "USIProtocol" }, + { 0x0d, 0x0098, "MicrosoftPenProtocol" }, + { 0x0d, 0x00a0, "SupportedReportRates" }, + { 0x0d, 0x00a1, "ReportRate" }, + { 0x0d, 0x00a2, "TransducerConnected" }, + { 0x0d, 0x00a3, "SwitchDisabled" }, + { 0x0d, 0x00a4, "SwitchUnimplemented" }, + { 0x0d, 0x00a5, "TransducerSwitches" }, + { 0x0d, 0x00a6, "TransducerIndexSelector" }, + { 0x0d, 0x00b0, "ButtonPressThreshold" }, + { 0x0e, 0, "Haptics" }, + { 0x0e, 0x0001, "SimpleHapticController" }, + { 0x0e, 0x0010, "WaveformList" }, + { 0x0e, 0x0011, "DurationList" }, + { 0x0e, 0x0020, "AutoTrigger" }, + { 0x0e, 0x0021, "ManualTrigger" }, + { 0x0e, 0x0022, "AutoTriggerAssociatedControl" }, + { 0x0e, 0x0023, "Intensity" }, + { 0x0e, 0x0024, "RepeatCount" }, + { 0x0e, 0x0025, "RetriggerPeriod" }, + { 0x0e, 0x0026, "WaveformVendorPage" }, + { 0x0e, 0x0027, "WaveformVendorID" }, + { 0x0e, 0x0028, "WaveformCutoffTime" }, + { 0x0e, 0x1001, "WaveformNone" }, + { 0x0e, 0x1002, "WaveformStop" }, + { 0x0e, 0x1003, "WaveformClick" }, + { 0x0e, 0x1004, "WaveformBuzzContinuous" }, + { 0x0e, 0x1005, "WaveformRumbleContinuous" }, + { 0x0e, 0x1006, "WaveformPress" }, + { 0x0e, 0x1007, "WaveformRelease" }, + { 0x0e, 0x1008, "WaveformHover" }, + { 0x0e, 0x1009, "WaveformSuccess" }, + { 0x0e, 0x100a, "WaveformError" }, + { 0x0e, 0x100b, "WaveformInkContinuous" }, + { 0x0e, 0x100c, "WaveformPencilContinuous" }, + { 0x0e, 0x100d, "WaveformMarkerContinuous" }, + { 0x0e, 0x100e, "WaveformChiselMarkerContinuous" }, + { 0x0e, 0x100f, "WaveformBrushContinuous" }, + { 0x0e, 0x1010, "WaveformEraserContinuous" }, + { 0x0e, 0x1011, "WaveformSparkleContinuous" }, + { 0x0f, 0, "PhysicalInputDevice" }, + { 0x0f, 0x0001, "PhysicalInputDevice" }, + { 0x0f, 0x0020, "Normal" }, + { 0x0f, 0x0021, "SetEffectReport" }, + { 0x0f, 0x0022, "EffectParameterBlockIndex" }, + { 0x0f, 0x0023, "ParameterBlockOffset" }, + { 0x0f, 0x0024, "ROMFlag" }, + { 0x0f, 0x0025, "EffectType" }, + { 0x0f, 0x0026, "ETConstantForce" }, + { 0x0f, 0x0027, "ETRamp" }, + { 0x0f, 0x0028, "ETCustomForce" }, + { 0x0f, 0x0030, "ETSquare" }, + { 0x0f, 0x0031, "ETSine" }, + { 0x0f, 0x0032, "ETTriangle" }, + { 0x0f, 0x0033, "ETSawtoothUp" }, + { 0x0f, 0x0034, "ETSawtoothDown" }, + { 0x0f, 0x0040, "ETSpring" }, + { 0x0f, 0x0041, "ETDamper" }, + { 0x0f, 0x0042, "ETInertia" }, + { 0x0f, 0x0043, "ETFriction" }, + { 0x0f, 0x0050, "Duration" }, + { 0x0f, 0x0051, "SamplePeriod" }, + { 0x0f, 0x0052, "Gain" }, + { 0x0f, 0x0053, "TriggerButton" }, + { 0x0f, 0x0054, "TriggerRepeatInterval" }, + { 0x0f, 0x0055, "AxesEnable" }, + { 0x0f, 0x0056, "DirectionEnable" }, + { 0x0f, 0x0057, "Direction" }, + { 0x0f, 0x0058, "TypeSpecificBlockOffset" }, + { 0x0f, 0x0059, "BlockType" }, + { 0x0f, 0x005a, "SetEnvelopeReport" }, + { 0x0f, 0x005b, "AttackLevel" }, + { 0x0f, 0x005c, "AttackTime" }, + { 0x0f, 0x005d, "FadeLevel" }, + { 0x0f, 0x005e, "FadeTime" }, + { 0x0f, 0x005f, "SetConditionReport" }, + { 0x0f, 0x0060, "CenterPointOffset" }, + { 0x0f, 0x0061, "PositiveCoefficient" }, + { 0x0f, 0x0062, "NegativeCoefficient" }, + { 0x0f, 0x0063, "PositiveSaturation" }, + { 0x0f, 0x0064, "NegativeSaturation" }, + { 0x0f, 0x0065, "DeadBand" }, + { 0x0f, 0x0066, "DownloadForceSample" }, + { 0x0f, 0x0067, "IsochCustomForceEnable" }, + { 0x0f, 0x0068, "CustomForceDataReport" }, + { 0x0f, 0x0069, "CustomForceData" }, + { 0x0f, 0x006a, "CustomForceVendorDefinedData" }, + { 0x0f, 0x006b, "SetCustomForceReport" }, + { 0x0f, 0x006c, "CustomForceDataOffset" }, + { 0x0f, 0x006d, "SampleCount" }, + { 0x0f, 0x006e, "SetPeriodicReport" }, + { 0x0f, 0x006f, "Offset" }, + { 0x0f, 0x0070, "Magnitude" }, + { 0x0f, 0x0071, "Phase" }, + { 0x0f, 0x0072, "Period" }, + { 0x0f, 0x0073, "SetConstantForceReport" }, + { 0x0f, 0x0074, "SetRampForceReport" }, + { 0x0f, 0x0075, "RampStart" }, + { 0x0f, 0x0076, "RampEnd" }, + { 0x0f, 0x0077, "EffectOperationReport" }, + { 0x0f, 0x0078, "EffectOperation" }, + { 0x0f, 0x0079, "OpEffectStart" }, + { 0x0f, 0x007a, "OpEffectStartSolo" }, + { 0x0f, 0x007b, "OpEffectStop" }, + { 0x0f, 0x007c, "LoopCount" }, + { 0x0f, 0x007d, "DeviceGainReport" }, + { 0x0f, 0x007e, "DeviceGain" }, + { 0x0f, 0x007f, "ParameterBlockPoolsReport" }, + { 0x0f, 0x0080, "RAMPoolSize" }, + { 0x0f, 0x0081, "ROMPoolSize" }, + { 0x0f, 0x0082, "ROMEffectBlockCount" }, + { 0x0f, 0x0083, "SimultaneousEffectsMax" }, + { 0x0f, 0x0084, "PoolAlignment" }, + { 0x0f, 0x0085, "ParameterBlockMoveReport" }, + { 0x0f, 0x0086, "MoveSource" }, + { 0x0f, 0x0087, "MoveDestination" }, + { 0x0f, 0x0088, "MoveLength" }, + { 0x0f, 0x0089, "EffectParameterBlockLoadReport" }, + { 0x0f, 0x008b, "EffectParameterBlockLoadStatus" }, + { 0x0f, 0x008c, "BlockLoadSuccess" }, + { 0x0f, 0x008d, "BlockLoadFull" }, + { 0x0f, 0x008e, "BlockLoadError" }, + { 0x0f, 0x008f, "BlockHandle" }, + { 0x0f, 0x0090, "EffectParameterBlockFreeReport" }, + { 0x0f, 0x0091, "TypeSpecificBlockHandle" }, + { 0x0f, 0x0092, "PIDStateReport" }, + { 0x0f, 0x0094, "EffectPlaying" }, + { 0x0f, 0x0095, "PIDDeviceControlReport" }, + { 0x0f, 0x0096, "PIDDeviceControl" }, + { 0x0f, 0x0097, "DCEnableActuators" }, + { 0x0f, 0x0098, "DCDisableActuators" }, + { 0x0f, 0x0099, "DCStopAllEffects" }, + { 0x0f, 0x009a, "DCReset" }, + { 0x0f, 0x009b, "DCPause" }, + { 0x0f, 0x009c, "DCContinue" }, + { 0x0f, 0x009f, "DevicePaused" }, + { 0x0f, 0x00a0, "ActuatorsEnabled" }, + { 0x0f, 0x00a4, "SafetySwitch" }, + { 0x0f, 0x00a5, "ActuatorOverrideSwitch" }, + { 0x0f, 0x00a6, "ActuatorPower" }, + { 0x0f, 0x00a7, "StartDelay" }, + { 0x0f, 0x00a8, "ParameterBlockSize" }, + { 0x0f, 0x00a9, "DeviceManagedPool" }, + { 0x0f, 0x00aa, "SharedParameterBlocks" }, + { 0x0f, 0x00ab, "CreateNewEffectParameterBlockReport" }, + { 0x0f, 0x00ac, "RAMPoolAvailable" }, + { 0x11, 0, "SoC" }, + { 0x11, 0x0001, "SocControl" }, + { 0x11, 0x0002, "FirmwareTransfer" }, + { 0x11, 0x0003, "FirmwareFileId" }, + { 0x11, 0x0004, "FileOffsetInBytes" }, + { 0x11, 0x0005, "FileTransferSizeMaxInBytes" }, + { 0x11, 0x0006, "FilePayload" }, + { 0x11, 0x0007, "FilePayloadSizeInBytes" }, + { 0x11, 0x0008, "FilePayloadContainsLastBytes" }, + { 0x11, 0x0009, "FileTransferStop" }, + { 0x11, 0x000a, "FileTransferTillEnd" }, + { 0x12, 0, "EyeandHeadTrackers" }, + { 0x12, 0x0001, "EyeTracker" }, + { 0x12, 0x0002, "HeadTracker" }, + { 0x12, 0x0010, "TrackingData" }, + { 0x12, 0x0011, "Capabilities" }, + { 0x12, 0x0012, "Configuration" }, + { 0x12, 0x0013, "Status" }, + { 0x12, 0x0014, "Control" }, + { 0x12, 0x0020, "SensorTimestamp" }, + { 0x12, 0x0021, "PositionX" }, + { 0x12, 0x0022, "PositionY" }, + { 0x12, 0x0023, "PositionZ" }, + { 0x12, 0x0024, "GazePoint" }, + { 0x12, 0x0025, "LeftEyePosition" }, + { 0x12, 0x0026, "RightEyePosition" }, + { 0x12, 0x0027, "HeadPosition" }, + { 0x12, 0x0028, "HeadDirectionPoint" }, + { 0x12, 0x0029, "RotationaboutXaxis" }, + { 0x12, 0x002a, "RotationaboutYaxis" }, + { 0x12, 0x002b, "RotationaboutZaxis" }, + { 0x12, 0x0100, "TrackerQuality" }, + { 0x12, 0x0101, "MinimumTrackingDistance" }, + { 0x12, 0x0102, "OptimumTrackingDistance" }, + { 0x12, 0x0103, "MaximumTrackingDistance" }, + { 0x12, 0x0104, "MaximumScreenPlaneWidth" }, + { 0x12, 0x0105, "MaximumScreenPlaneHeight" }, + { 0x12, 0x0200, "DisplayManufacturerID" }, + { 0x12, 0x0201, "DisplayProductID" }, + { 0x12, 0x0202, "DisplaySerialNumber" }, + { 0x12, 0x0203, "DisplayManufacturerDate" }, + { 0x12, 0x0204, "CalibratedScreenWidth" }, + { 0x12, 0x0205, "CalibratedScreenHeight" }, + { 0x12, 0x0300, "SamplingFrequency" }, + { 0x12, 0x0301, "ConfigurationStatus" }, + { 0x12, 0x0400, "DeviceModeRequest" }, + { 0x14, 0, "AuxiliaryDisplay" }, + { 0x14, 0x0001, "AlphanumericDisplay" }, + { 0x14, 0x0002, "AuxiliaryDisplay" }, + { 0x14, 0x0020, "DisplayAttributesReport" }, + { 0x14, 0x0021, "ASCIICharacterSet" }, + { 0x14, 0x0022, "DataReadBack" }, + { 0x14, 0x0023, "FontReadBack" }, + { 0x14, 0x0024, "DisplayControlReport" }, + { 0x14, 0x0025, "ClearDisplay" }, + { 0x14, 0x0026, "DisplayEnable" }, + { 0x14, 0x0027, "ScreenSaverDelay" }, + { 0x14, 0x0028, "ScreenSaverEnable" }, + { 0x14, 0x0029, "VerticalScroll" }, + { 0x14, 0x002a, "HorizontalScroll" }, + { 0x14, 0x002b, "CharacterReport" }, + { 0x14, 0x002c, "DisplayData" }, + { 0x14, 0x002d, "DisplayStatus" }, + { 0x14, 0x002e, "StatNotReady" }, + { 0x14, 0x002f, "StatReady" }, + { 0x14, 0x0030, "ErrNotaloadablecharacter" }, + { 0x14, 0x0031, "ErrFontdatacannotberead" }, + { 0x14, 0x0032, "CursorPositionReport" }, + { 0x14, 0x0033, "Row" }, + { 0x14, 0x0034, "Column" }, + { 0x14, 0x0035, "Rows" }, + { 0x14, 0x0036, "Columns" }, + { 0x14, 0x0037, "CursorPixelPositioning" }, + { 0x14, 0x0038, "CursorMode" }, + { 0x14, 0x0039, "CursorEnable" }, + { 0x14, 0x003a, "CursorBlink" }, + { 0x14, 0x003b, "FontReport" }, + { 0x14, 0x003c, "FontData" }, + { 0x14, 0x003d, "CharacterWidth" }, + { 0x14, 0x003e, "CharacterHeight" }, + { 0x14, 0x003f, "CharacterSpacingHorizontal" }, + { 0x14, 0x0040, "CharacterSpacingVertical" }, + { 0x14, 0x0041, "UnicodeCharacterSet" }, + { 0x14, 0x0042, "Font7Segment" }, + { 0x14, 0x0043, "7SegmentDirectMap" }, + { 0x14, 0x0044, "Font14Segment" }, + { 0x14, 0x0045, "14SegmentDirectMap" }, + { 0x14, 0x0046, "DisplayBrightness" }, + { 0x14, 0x0047, "DisplayContrast" }, + { 0x14, 0x0048, "CharacterAttribute" }, + { 0x14, 0x0049, "AttributeReadback" }, + { 0x14, 0x004a, "AttributeData" }, + { 0x14, 0x004b, "CharAttrEnhance" }, + { 0x14, 0x004c, "CharAttrUnderline" }, + { 0x14, 0x004d, "CharAttrBlink" }, + { 0x14, 0x0080, "BitmapSizeX" }, + { 0x14, 0x0081, "BitmapSizeY" }, + { 0x14, 0x0082, "MaxBlitSize" }, + { 0x14, 0x0083, "BitDepthFormat" }, + { 0x14, 0x0084, "DisplayOrientation" }, + { 0x14, 0x0085, "PaletteReport" }, + { 0x14, 0x0086, "PaletteDataSize" }, + { 0x14, 0x0087, "PaletteDataOffset" }, + { 0x14, 0x0088, "PaletteData" }, + { 0x14, 0x008a, "BlitReport" }, + { 0x14, 0x008b, "BlitRectangleX1" }, + { 0x14, 0x008c, "BlitRectangleY1" }, + { 0x14, 0x008d, "BlitRectangleX2" }, + { 0x14, 0x008e, "BlitRectangleY2" }, + { 0x14, 0x008f, "BlitData" }, + { 0x14, 0x0090, "SoftButton" }, + { 0x14, 0x0091, "SoftButtonID" }, + { 0x14, 0x0092, "SoftButtonSide" }, + { 0x14, 0x0093, "SoftButtonOffset1" }, + { 0x14, 0x0094, "SoftButtonOffset2" }, + { 0x14, 0x0095, "SoftButtonReport" }, + { 0x14, 0x00c2, "SoftKeys" }, + { 0x14, 0x00cc, "DisplayDataExtensions" }, + { 0x14, 0x00cf, "CharacterMapping" }, + { 0x14, 0x00dd, "UnicodeEquivalent" }, + { 0x14, 0x00df, "CharacterPageMapping" }, + { 0x14, 0x00ff, "RequestReport" }, + { 0x20, 0, "Sensors" }, + { 0x20, 0x0001, "Sensor" }, + { 0x20, 0x0010, "Biometric" }, + { 0x20, 0x0011, "BiometricHumanPresence" }, + { 0x20, 0x0012, "BiometricHumanProximity" }, + { 0x20, 0x0013, "BiometricHumanTouch" }, + { 0x20, 0x0014, "BiometricBloodPressure" }, + { 0x20, 0x0015, "BiometricBodyTemperature" }, + { 0x20, 0x0016, "BiometricHeartRate" }, + { 0x20, 0x0017, "BiometricHeartRateVariability" }, + { 0x20, 0x0018, "BiometricPeripheralOxygenSaturation" }, + { 0x20, 0x0019, "BiometricRespiratoryRate" }, + { 0x20, 0x0020, "Electrical" }, + { 0x20, 0x0021, "ElectricalCapacitance" }, + { 0x20, 0x0022, "ElectricalCurrent" }, + { 0x20, 0x0023, "ElectricalPower" }, + { 0x20, 0x0024, "ElectricalInductance" }, + { 0x20, 0x0025, "ElectricalResistance" }, + { 0x20, 0x0026, "ElectricalVoltage" }, + { 0x20, 0x0027, "ElectricalPotentiometer" }, + { 0x20, 0x0028, "ElectricalFrequency" }, + { 0x20, 0x0029, "ElectricalPeriod" }, + { 0x20, 0x0030, "Environmental" }, + { 0x20, 0x0031, "EnvironmentalAtmosphericPressure" }, + { 0x20, 0x0032, "EnvironmentalHumidity" }, + { 0x20, 0x0033, "EnvironmentalTemperature" }, + { 0x20, 0x0034, "EnvironmentalWindDirection" }, + { 0x20, 0x0035, "EnvironmentalWindSpeed" }, + { 0x20, 0x0036, "EnvironmentalAirQuality" }, + { 0x20, 0x0037, "EnvironmentalHeatIndex" }, + { 0x20, 0x0038, "EnvironmentalSurfaceTemperature" }, + { 0x20, 0x0039, "EnvironmentalVolatileOrganicCompounds" }, + { 0x20, 0x003a, "EnvironmentalObjectPresence" }, + { 0x20, 0x003b, "EnvironmentalObjectProximity" }, + { 0x20, 0x0040, "Light" }, + { 0x20, 0x0041, "LightAmbientLight" }, + { 0x20, 0x0042, "LightConsumerInfrared" }, + { 0x20, 0x0043, "LightInfraredLight" }, + { 0x20, 0x0044, "LightVisibleLight" }, + { 0x20, 0x0045, "LightUltravioletLight" }, + { 0x20, 0x0050, "Location" }, + { 0x20, 0x0051, "LocationBroadcast" }, + { 0x20, 0x0052, "LocationDeadReckoning" }, + { 0x20, 0x0053, "LocationGPSGlobalPositioningSystem" }, + { 0x20, 0x0054, "LocationLookup" }, + { 0x20, 0x0055, "LocationOther" }, + { 0x20, 0x0056, "LocationStatic" }, + { 0x20, 0x0057, "LocationTriangulation" }, + { 0x20, 0x0060, "Mechanical" }, + { 0x20, 0x0061, "MechanicalBooleanSwitch" }, + { 0x20, 0x0062, "MechanicalBooleanSwitchArray" }, + { 0x20, 0x0063, "MechanicalMultivalueSwitch" }, + { 0x20, 0x0064, "MechanicalForce" }, + { 0x20, 0x0065, "MechanicalPressure" }, + { 0x20, 0x0066, "MechanicalStrain" }, + { 0x20, 0x0067, "MechanicalWeight" }, + { 0x20, 0x0068, "MechanicalHapticVibrator" }, + { 0x20, 0x0069, "MechanicalHallEffectSwitch" }, + { 0x20, 0x0070, "Motion" }, + { 0x20, 0x0071, "MotionAccelerometer1D" }, + { 0x20, 0x0072, "MotionAccelerometer2D" }, + { 0x20, 0x0073, "MotionAccelerometer3D" }, + { 0x20, 0x0074, "MotionGyrometer1D" }, + { 0x20, 0x0075, "MotionGyrometer2D" }, + { 0x20, 0x0076, "MotionGyrometer3D" }, + { 0x20, 0x0077, "MotionMotionDetector" }, + { 0x20, 0x0078, "MotionSpeedometer" }, + { 0x20, 0x0079, "MotionAccelerometer" }, + { 0x20, 0x007a, "MotionGyrometer" }, + { 0x20, 0x007b, "MotionGravityVector" }, + { 0x20, 0x007c, "MotionLinearAccelerometer" }, + { 0x20, 0x0080, "Orientation" }, + { 0x20, 0x0081, "OrientationCompass1D" }, + { 0x20, 0x0082, "OrientationCompass2D" }, + { 0x20, 0x0083, "OrientationCompass3D" }, + { 0x20, 0x0084, "OrientationInclinometer1D" }, + { 0x20, 0x0085, "OrientationInclinometer2D" }, + { 0x20, 0x0086, "OrientationInclinometer3D" }, + { 0x20, 0x0087, "OrientationDistance1D" }, + { 0x20, 0x0088, "OrientationDistance2D" }, + { 0x20, 0x0089, "OrientationDistance3D" }, + { 0x20, 0x008a, "OrientationDeviceOrientation" }, + { 0x20, 0x008b, "OrientationCompass" }, + { 0x20, 0x008c, "OrientationInclinometer" }, + { 0x20, 0x008d, "OrientationDistance" }, + { 0x20, 0x008e, "OrientationRelativeOrientation" }, + { 0x20, 0x008f, "OrientationSimpleOrientation" }, + { 0x20, 0x0090, "Scanner" }, + { 0x20, 0x0091, "ScannerBarcode" }, + { 0x20, 0x0092, "ScannerRFID" }, + { 0x20, 0x0093, "ScannerNFC" }, + { 0x20, 0x00a0, "Time" }, + { 0x20, 0x00a1, "TimeAlarmTimer" }, + { 0x20, 0x00a2, "TimeRealTimeClock" }, + { 0x20, 0x00b0, "PersonalActivity" }, + { 0x20, 0x00b1, "PersonalActivityActivityDetection" }, + { 0x20, 0x00b2, "PersonalActivityDevicePosition" }, + { 0x20, 0x00b3, "PersonalActivityFloorTracker" }, + { 0x20, 0x00b4, "PersonalActivityPedometer" }, + { 0x20, 0x00b5, "PersonalActivityStepDetection" }, + { 0x20, 0x00c0, "OrientationExtended" }, + { 0x20, 0x00c1, "OrientationExtendedGeomagneticOrientation" }, + { 0x20, 0x00c2, "OrientationExtendedMagnetometer" }, + { 0x20, 0x00d0, "Gesture" }, + { 0x20, 0x00d1, "GestureChassisFlipGesture" }, + { 0x20, 0x00d2, "GestureHingeFoldGesture" }, + { 0x20, 0x00e0, "Other" }, + { 0x20, 0x00e1, "OtherCustom" }, + { 0x20, 0x00e2, "OtherGeneric" }, + { 0x20, 0x00e3, "OtherGenericEnumerator" }, + { 0x20, 0x00e4, "OtherHingeAngle" }, + { 0x20, 0x00f0, "VendorReserved1" }, + { 0x20, 0x00f1, "VendorReserved2" }, + { 0x20, 0x00f2, "VendorReserved3" }, + { 0x20, 0x00f3, "VendorReserved4" }, + { 0x20, 0x00f4, "VendorReserved5" }, + { 0x20, 0x00f5, "VendorReserved6" }, + { 0x20, 0x00f6, "VendorReserved7" }, + { 0x20, 0x00f7, "VendorReserved8" }, + { 0x20, 0x00f8, "VendorReserved9" }, + { 0x20, 0x00f9, "VendorReserved10" }, + { 0x20, 0x00fa, "VendorReserved11" }, + { 0x20, 0x00fb, "VendorReserved12" }, + { 0x20, 0x00fc, "VendorReserved13" }, + { 0x20, 0x00fd, "VendorReserved14" }, + { 0x20, 0x00fe, "VendorReserved15" }, + { 0x20, 0x00ff, "VendorReserved16" }, + { 0x20, 0x0200, "Event" }, + { 0x20, 0x0201, "EventSensorState" }, + { 0x20, 0x0202, "EventSensorEvent" }, + { 0x20, 0x0300, "Property" }, + { 0x20, 0x0301, "PropertyFriendlyName" }, + { 0x20, 0x0302, "PropertyPersistentUniqueID" }, + { 0x20, 0x0303, "PropertySensorStatus" }, + { 0x20, 0x0304, "PropertyMinimumReportInterval" }, + { 0x20, 0x0305, "PropertySensorManufacturer" }, + { 0x20, 0x0306, "PropertySensorModel" }, + { 0x20, 0x0307, "PropertySensorSerialNumber" }, + { 0x20, 0x0308, "PropertySensorDescription" }, + { 0x20, 0x0309, "PropertySensorConnectionType" }, + { 0x20, 0x030a, "PropertySensorDevicePath" }, + { 0x20, 0x030b, "PropertyHardwareRevision" }, + { 0x20, 0x030c, "PropertyFirmwareVersion" }, + { 0x20, 0x030d, "PropertyReleaseDate" }, + { 0x20, 0x030e, "PropertyReportInterval" }, + { 0x20, 0x030f, "PropertyChangeSensitivityAbsolute" }, + { 0x20, 0x0310, "PropertyChangeSensitivityPercentofRange" }, + { 0x20, 0x0311, "PropertyChangeSensitivityPercentRelative" }, + { 0x20, 0x0312, "PropertyAccuracy" }, + { 0x20, 0x0313, "PropertyResolution" }, + { 0x20, 0x0314, "PropertyMaximum" }, + { 0x20, 0x0315, "PropertyMinimum" }, + { 0x20, 0x0316, "PropertyReportingState" }, + { 0x20, 0x0317, "PropertySamplingRate" }, + { 0x20, 0x0318, "PropertyResponseCurve" }, + { 0x20, 0x0319, "PropertyPowerState" }, + { 0x20, 0x031a, "PropertyMaximumFIFOEvents" }, + { 0x20, 0x031b, "PropertyReportLatency" }, + { 0x20, 0x031c, "PropertyFlushFIFOEvents" }, + { 0x20, 0x031d, "PropertyMaximumPowerConsumption" }, + { 0x20, 0x031e, "PropertyIsPrimary" }, + { 0x20, 0x031f, "PropertyHumanPresenceDetectionType" }, + { 0x20, 0x0400, "DataFieldLocation" }, + { 0x20, 0x0402, "DataFieldAltitudeAntennaSeaLevel" }, + { 0x20, 0x0403, "DataFieldDifferentialReferenceStationID" }, + { 0x20, 0x0404, "DataFieldAltitudeEllipsoidError" }, + { 0x20, 0x0405, "DataFieldAltitudeEllipsoid" }, + { 0x20, 0x0406, "DataFieldAltitudeSeaLevelError" }, + { 0x20, 0x0407, "DataFieldAltitudeSeaLevel" }, + { 0x20, 0x0408, "DataFieldDifferentialGPSDataAge" }, + { 0x20, 0x0409, "DataFieldErrorRadius" }, + { 0x20, 0x040a, "DataFieldFixQuality" }, + { 0x20, 0x040b, "DataFieldFixType" }, + { 0x20, 0x040c, "DataFieldGeoidalSeparation" }, + { 0x20, 0x040d, "DataFieldGPSOperationMode" }, + { 0x20, 0x040e, "DataFieldGPSSelectionMode" }, + { 0x20, 0x040f, "DataFieldGPSStatus" }, + { 0x20, 0x0410, "DataFieldPositionDilutionofPrecision" }, + { 0x20, 0x0411, "DataFieldHorizontalDilutionofPrecision" }, + { 0x20, 0x0412, "DataFieldVerticalDilutionofPrecision" }, + { 0x20, 0x0413, "DataFieldLatitude" }, + { 0x20, 0x0414, "DataFieldLongitude" }, + { 0x20, 0x0415, "DataFieldTrueHeading" }, + { 0x20, 0x0416, "DataFieldMagneticHeading" }, + { 0x20, 0x0417, "DataFieldMagneticVariation" }, + { 0x20, 0x0418, "DataFieldSpeed" }, + { 0x20, 0x0419, "DataFieldSatellitesinView" }, + { 0x20, 0x041a, "DataFieldSatellitesinViewAzimuth" }, + { 0x20, 0x041b, "DataFieldSatellitesinViewElevation" }, + { 0x20, 0x041c, "DataFieldSatellitesinViewIDs" }, + { 0x20, 0x041d, "DataFieldSatellitesinViewPRNs" }, + { 0x20, 0x041e, "DataFieldSatellitesinViewSNRatios" }, + { 0x20, 0x041f, "DataFieldSatellitesUsedCount" }, + { 0x20, 0x0420, "DataFieldSatellitesUsedPRNs" }, + { 0x20, 0x0421, "DataFieldNMEASentence" }, + { 0x20, 0x0422, "DataFieldAddressLine1" }, + { 0x20, 0x0423, "DataFieldAddressLine2" }, + { 0x20, 0x0424, "DataFieldCity" }, + { 0x20, 0x0425, "DataFieldStateorProvince" }, + { 0x20, 0x0426, "DataFieldCountryorRegion" }, + { 0x20, 0x0427, "DataFieldPostalCode" }, + { 0x20, 0x042a, "PropertyLocation" }, + { 0x20, 0x042b, "PropertyLocationDesiredAccuracy" }, + { 0x20, 0x0430, "DataFieldEnvironmental" }, + { 0x20, 0x0431, "DataFieldAtmosphericPressure" }, + { 0x20, 0x0433, "DataFieldRelativeHumidity" }, + { 0x20, 0x0434, "DataFieldTemperature" }, + { 0x20, 0x0435, "DataFieldWindDirection" }, + { 0x20, 0x0436, "DataFieldWindSpeed" }, + { 0x20, 0x0437, "DataFieldAirQualityIndex" }, + { 0x20, 0x0438, "DataFieldEquivalentCO2" }, + { 0x20, 0x0439, "DataFieldVolatileOrganicCompoundConcentration" }, + { 0x20, 0x043a, "DataFieldObjectPresence" }, + { 0x20, 0x043b, "DataFieldObjectProximityRange" }, + { 0x20, 0x043c, "DataFieldObjectProximityOutofRange" }, + { 0x20, 0x0440, "PropertyEnvironmental" }, + { 0x20, 0x0441, "PropertyReferencePressure" }, + { 0x20, 0x0450, "DataFieldMotion" }, + { 0x20, 0x0451, "DataFieldMotionState" }, + { 0x20, 0x0452, "DataFieldAcceleration" }, + { 0x20, 0x0453, "DataFieldAccelerationAxisX" }, + { 0x20, 0x0454, "DataFieldAccelerationAxisY" }, + { 0x20, 0x0455, "DataFieldAccelerationAxisZ" }, + { 0x20, 0x0456, "DataFieldAngularVelocity" }, + { 0x20, 0x0457, "DataFieldAngularVelocityaboutXAxis" }, + { 0x20, 0x0458, "DataFieldAngularVelocityaboutYAxis" }, + { 0x20, 0x0459, "DataFieldAngularVelocityaboutZAxis" }, + { 0x20, 0x045a, "DataFieldAngularPosition" }, + { 0x20, 0x045b, "DataFieldAngularPositionaboutXAxis" }, + { 0x20, 0x045c, "DataFieldAngularPositionaboutYAxis" }, + { 0x20, 0x045d, "DataFieldAngularPositionaboutZAxis" }, + { 0x20, 0x045e, "DataFieldMotionSpeed" }, + { 0x20, 0x045f, "DataFieldMotionIntensity" }, + { 0x20, 0x0470, "DataFieldOrientation" }, + { 0x20, 0x0471, "DataFieldHeading" }, + { 0x20, 0x0472, "DataFieldHeadingXAxis" }, + { 0x20, 0x0473, "DataFieldHeadingYAxis" }, + { 0x20, 0x0474, "DataFieldHeadingZAxis" }, + { 0x20, 0x0475, "DataFieldHeadingCompensatedMagneticNorth" }, + { 0x20, 0x0476, "DataFieldHeadingCompensatedTrueNorth" }, + { 0x20, 0x0477, "DataFieldHeadingMagneticNorth" }, + { 0x20, 0x0478, "DataFieldHeadingTrueNorth" }, + { 0x20, 0x0479, "DataFieldDistance" }, + { 0x20, 0x047a, "DataFieldDistanceXAxis" }, + { 0x20, 0x047b, "DataFieldDistanceYAxis" }, + { 0x20, 0x047c, "DataFieldDistanceZAxis" }, + { 0x20, 0x047d, "DataFieldDistanceOutofRange" }, + { 0x20, 0x047e, "DataFieldTilt" }, + { 0x20, 0x047f, "DataFieldTiltXAxis" }, + { 0x20, 0x0480, "DataFieldTiltYAxis" }, + { 0x20, 0x0481, "DataFieldTiltZAxis" }, + { 0x20, 0x0482, "DataFieldRotationMatrix" }, + { 0x20, 0x0483, "DataFieldQuaternion" }, + { 0x20, 0x0484, "DataFieldMagneticFlux" }, + { 0x20, 0x0485, "DataFieldMagneticFluxXAxis" }, + { 0x20, 0x0486, "DataFieldMagneticFluxYAxis" }, + { 0x20, 0x0487, "DataFieldMagneticFluxZAxis" }, + { 0x20, 0x0488, "DataFieldMagnetometerAccuracy" }, + { 0x20, 0x0489, "DataFieldSimpleOrientationDirection" }, + { 0x20, 0x0490, "DataFieldMechanical" }, + { 0x20, 0x0491, "DataFieldBooleanSwitchState" }, + { 0x20, 0x0492, "DataFieldBooleanSwitchArrayStates" }, + { 0x20, 0x0493, "DataFieldMultivalueSwitchValue" }, + { 0x20, 0x0494, "DataFieldForce" }, + { 0x20, 0x0495, "DataFieldAbsolutePressure" }, + { 0x20, 0x0496, "DataFieldGaugePressure" }, + { 0x20, 0x0497, "DataFieldStrain" }, + { 0x20, 0x0498, "DataFieldWeight" }, + { 0x20, 0x04a0, "PropertyMechanical" }, + { 0x20, 0x04a1, "PropertyVibrationState" }, + { 0x20, 0x04a2, "PropertyForwardVibrationSpeed" }, + { 0x20, 0x04a3, "PropertyBackwardVibrationSpeed" }, + { 0x20, 0x04b0, "DataFieldBiometric" }, + { 0x20, 0x04b1, "DataFieldHumanPresence" }, + { 0x20, 0x04b2, "DataFieldHumanProximityRange" }, + { 0x20, 0x04b3, "DataFieldHumanProximityOutofRange" }, + { 0x20, 0x04b4, "DataFieldHumanTouchState" }, + { 0x20, 0x04b5, "DataFieldBloodPressure" }, + { 0x20, 0x04b6, "DataFieldBloodPressureDiastolic" }, + { 0x20, 0x04b7, "DataFieldBloodPressureSystolic" }, + { 0x20, 0x04b8, "DataFieldHeartRate" }, + { 0x20, 0x04b9, "DataFieldRestingHeartRate" }, + { 0x20, 0x04ba, "DataFieldHeartbeatInterval" }, + { 0x20, 0x04bb, "DataFieldRespiratoryRate" }, + { 0x20, 0x04bc, "DataFieldSpO2" }, + { 0x20, 0x04bd, "DataFieldHumanAttentionDetected" }, + { 0x20, 0x04be, "DataFieldHumanHeadAzimuth" }, + { 0x20, 0x04bf, "DataFieldHumanHeadAltitude" }, + { 0x20, 0x04c0, "DataFieldHumanHeadRoll" }, + { 0x20, 0x04c1, "DataFieldHumanHeadPitch" }, + { 0x20, 0x04c2, "DataFieldHumanHeadYaw" }, + { 0x20, 0x04c3, "DataFieldHumanCorrelationId" }, + { 0x20, 0x04d0, "DataFieldLight" }, + { 0x20, 0x04d1, "DataFieldIlluminance" }, + { 0x20, 0x04d2, "DataFieldColorTemperature" }, + { 0x20, 0x04d3, "DataFieldChromaticity" }, + { 0x20, 0x04d4, "DataFieldChromaticityX" }, + { 0x20, 0x04d5, "DataFieldChromaticityY" }, + { 0x20, 0x04d6, "DataFieldConsumerIRSentenceReceive" }, + { 0x20, 0x04d7, "DataFieldInfraredLight" }, + { 0x20, 0x04d8, "DataFieldRedLight" }, + { 0x20, 0x04d9, "DataFieldGreenLight" }, + { 0x20, 0x04da, "DataFieldBlueLight" }, + { 0x20, 0x04db, "DataFieldUltravioletALight" }, + { 0x20, 0x04dc, "DataFieldUltravioletBLight" }, + { 0x20, 0x04dd, "DataFieldUltravioletIndex" }, + { 0x20, 0x04de, "DataFieldNearInfraredLight" }, + { 0x20, 0x04df, "PropertyLight" }, + { 0x20, 0x04e0, "PropertyConsumerIRSentenceSend" }, + { 0x20, 0x04e2, "PropertyAutoBrightnessPreferred" }, + { 0x20, 0x04e3, "PropertyAutoColorPreferred" }, + { 0x20, 0x04f0, "DataFieldScanner" }, + { 0x20, 0x04f1, "DataFieldRFIDTag40Bit" }, + { 0x20, 0x04f2, "DataFieldNFCSentenceReceive" }, + { 0x20, 0x04f8, "PropertyScanner" }, + { 0x20, 0x04f9, "PropertyNFCSentenceSend" }, + { 0x20, 0x0500, "DataFieldElectrical" }, + { 0x20, 0x0501, "DataFieldCapacitance" }, + { 0x20, 0x0502, "DataFieldCurrent" }, + { 0x20, 0x0503, "DataFieldElectricalPower" }, + { 0x20, 0x0504, "DataFieldInductance" }, + { 0x20, 0x0505, "DataFieldResistance" }, + { 0x20, 0x0506, "DataFieldVoltage" }, + { 0x20, 0x0507, "DataFieldFrequency" }, + { 0x20, 0x0508, "DataFieldPeriod" }, + { 0x20, 0x0509, "DataFieldPercentofRange" }, + { 0x20, 0x0520, "DataFieldTime" }, + { 0x20, 0x0521, "DataFieldYear" }, + { 0x20, 0x0522, "DataFieldMonth" }, + { 0x20, 0x0523, "DataFieldDay" }, + { 0x20, 0x0524, "DataFieldDayofWeek" }, + { 0x20, 0x0525, "DataFieldHour" }, + { 0x20, 0x0526, "DataFieldMinute" }, + { 0x20, 0x0527, "DataFieldSecond" }, + { 0x20, 0x0528, "DataFieldMillisecond" }, + { 0x20, 0x0529, "DataFieldTimestamp" }, + { 0x20, 0x052a, "DataFieldJulianDayofYear" }, + { 0x20, 0x052b, "DataFieldTimeSinceSystemBoot" }, + { 0x20, 0x0530, "PropertyTime" }, + { 0x20, 0x0531, "PropertyTimeZoneOffsetfromUTC" }, + { 0x20, 0x0532, "PropertyTimeZoneName" }, + { 0x20, 0x0533, "PropertyDaylightSavingsTimeObserved" }, + { 0x20, 0x0534, "PropertyTimeTrimAdjustment" }, + { 0x20, 0x0535, "PropertyArmAlarm" }, + { 0x20, 0x0540, "DataFieldCustom" }, + { 0x20, 0x0541, "DataFieldCustomUsage" }, + { 0x20, 0x0542, "DataFieldCustomBooleanArray" }, + { 0x20, 0x0543, "DataFieldCustomValue" }, + { 0x20, 0x0544, "DataFieldCustomValue1" }, + { 0x20, 0x0545, "DataFieldCustomValue2" }, + { 0x20, 0x0546, "DataFieldCustomValue3" }, + { 0x20, 0x0547, "DataFieldCustomValue4" }, + { 0x20, 0x0548, "DataFieldCustomValue5" }, + { 0x20, 0x0549, "DataFieldCustomValue6" }, + { 0x20, 0x054a, "DataFieldCustomValue7" }, + { 0x20, 0x054b, "DataFieldCustomValue8" }, + { 0x20, 0x054c, "DataFieldCustomValue9" }, + { 0x20, 0x054d, "DataFieldCustomValue10" }, + { 0x20, 0x054e, "DataFieldCustomValue11" }, + { 0x20, 0x054f, "DataFieldCustomValue12" }, + { 0x20, 0x0550, "DataFieldCustomValue13" }, + { 0x20, 0x0551, "DataFieldCustomValue14" }, + { 0x20, 0x0552, "DataFieldCustomValue15" }, + { 0x20, 0x0553, "DataFieldCustomValue16" }, + { 0x20, 0x0554, "DataFieldCustomValue17" }, + { 0x20, 0x0555, "DataFieldCustomValue18" }, + { 0x20, 0x0556, "DataFieldCustomValue19" }, + { 0x20, 0x0557, "DataFieldCustomValue20" }, + { 0x20, 0x0558, "DataFieldCustomValue21" }, + { 0x20, 0x0559, "DataFieldCustomValue22" }, + { 0x20, 0x055a, "DataFieldCustomValue23" }, + { 0x20, 0x055b, "DataFieldCustomValue24" }, + { 0x20, 0x055c, "DataFieldCustomValue25" }, + { 0x20, 0x055d, "DataFieldCustomValue26" }, + { 0x20, 0x055e, "DataFieldCustomValue27" }, + { 0x20, 0x055f, "DataFieldCustomValue28" }, + { 0x20, 0x0560, "DataFieldGeneric" }, + { 0x20, 0x0561, "DataFieldGenericGUIDorPROPERTYKEY" }, + { 0x20, 0x0562, "DataFieldGenericCategoryGUID" }, + { 0x20, 0x0563, "DataFieldGenericTypeGUID" }, + { 0x20, 0x0564, "DataFieldGenericEventPROPERTYKEY" }, + { 0x20, 0x0565, "DataFieldGenericPropertyPROPERTYKEY" }, + { 0x20, 0x0566, "DataFieldGenericDataFieldPROPERTYKEY" }, + { 0x20, 0x0567, "DataFieldGenericEvent" }, + { 0x20, 0x0568, "DataFieldGenericProperty" }, + { 0x20, 0x0569, "DataFieldGenericDataField" }, + { 0x20, 0x056a, "DataFieldEnumeratorTableRowIndex" }, + { 0x20, 0x056b, "DataFieldEnumeratorTableRowCount" }, + { 0x20, 0x056c, "DataFieldGenericGUIDorPROPERTYKEYkind" }, + { 0x20, 0x056d, "DataFieldGenericGUID" }, + { 0x20, 0x056e, "DataFieldGenericPROPERTYKEY" }, + { 0x20, 0x056f, "DataFieldGenericTopLevelCollectionID" }, + { 0x20, 0x0570, "DataFieldGenericReportID" }, + { 0x20, 0x0571, "DataFieldGenericReportItemPositionIndex" }, + { 0x20, 0x0572, "DataFieldGenericFirmwareVARTYPE" }, + { 0x20, 0x0573, "DataFieldGenericUnitofMeasure" }, + { 0x20, 0x0574, "DataFieldGenericUnitExponent" }, + { 0x20, 0x0575, "DataFieldGenericReportSize" }, + { 0x20, 0x0576, "DataFieldGenericReportCount" }, + { 0x20, 0x0580, "PropertyGeneric" }, + { 0x20, 0x0581, "PropertyEnumeratorTableRowIndex" }, + { 0x20, 0x0582, "PropertyEnumeratorTableRowCount" }, + { 0x20, 0x0590, "DataFieldPersonalActivity" }, + { 0x20, 0x0591, "DataFieldActivityType" }, + { 0x20, 0x0592, "DataFieldActivityState" }, + { 0x20, 0x0593, "DataFieldDevicePosition" }, + { 0x20, 0x0594, "DataFieldStepCount" }, + { 0x20, 0x0595, "DataFieldStepCountReset" }, + { 0x20, 0x0596, "DataFieldStepDuration" }, + { 0x20, 0x0597, "DataFieldStepType" }, + { 0x20, 0x05a0, "PropertyMinimumActivityDetectionInterval" }, + { 0x20, 0x05a1, "PropertySupportedActivityTypes" }, + { 0x20, 0x05a2, "PropertySubscribedActivityTypes" }, + { 0x20, 0x05a3, "PropertySupportedStepTypes" }, + { 0x20, 0x05a4, "PropertySubscribedStepTypes" }, + { 0x20, 0x05a5, "PropertyFloorHeight" }, + { 0x20, 0x05b0, "DataFieldCustomTypeID" }, + { 0x20, 0x05c0, "PropertyCustom" }, + { 0x20, 0x05c1, "PropertyCustomValue1" }, + { 0x20, 0x05c2, "PropertyCustomValue2" }, + { 0x20, 0x05c3, "PropertyCustomValue3" }, + { 0x20, 0x05c4, "PropertyCustomValue4" }, + { 0x20, 0x05c5, "PropertyCustomValue5" }, + { 0x20, 0x05c6, "PropertyCustomValue6" }, + { 0x20, 0x05c7, "PropertyCustomValue7" }, + { 0x20, 0x05c8, "PropertyCustomValue8" }, + { 0x20, 0x05c9, "PropertyCustomValue9" }, + { 0x20, 0x05ca, "PropertyCustomValue10" }, + { 0x20, 0x05cb, "PropertyCustomValue11" }, + { 0x20, 0x05cc, "PropertyCustomValue12" }, + { 0x20, 0x05cd, "PropertyCustomValue13" }, + { 0x20, 0x05ce, "PropertyCustomValue14" }, + { 0x20, 0x05cf, "PropertyCustomValue15" }, + { 0x20, 0x05d0, "PropertyCustomValue16" }, + { 0x20, 0x05e0, "DataFieldHinge" }, + { 0x20, 0x05e1, "DataFieldHingeAngle" }, + { 0x20, 0x05f0, "DataFieldGestureSensor" }, + { 0x20, 0x05f1, "DataFieldGestureState" }, + { 0x20, 0x05f2, "DataFieldHingeFoldInitialAngle" }, + { 0x20, 0x05f3, "DataFieldHingeFoldFinalAngle" }, + { 0x20, 0x05f4, "DataFieldHingeFoldContributingPanel" }, + { 0x20, 0x05f5, "DataFieldHingeFoldType" }, + { 0x20, 0x0800, "SensorStateUndefined" }, + { 0x20, 0x0801, "SensorStateReady" }, + { 0x20, 0x0802, "SensorStateNotAvailable" }, + { 0x20, 0x0803, "SensorStateNoData" }, + { 0x20, 0x0804, "SensorStateInitializing" }, + { 0x20, 0x0805, "SensorStateAccessDenied" }, + { 0x20, 0x0806, "SensorStateError" }, + { 0x20, 0x0810, "SensorEventUnknown" }, + { 0x20, 0x0811, "SensorEventStateChanged" }, + { 0x20, 0x0812, "SensorEventPropertyChanged" }, + { 0x20, 0x0813, "SensorEventDataUpdated" }, + { 0x20, 0x0814, "SensorEventPollResponse" }, + { 0x20, 0x0815, "SensorEventChangeSensitivity" }, + { 0x20, 0x0816, "SensorEventRangeMaximumReached" }, + { 0x20, 0x0817, "SensorEventRangeMinimumReached" }, + { 0x20, 0x0818, "SensorEventHighThresholdCrossUpward" }, + { 0x20, 0x0819, "SensorEventHighThresholdCrossDownward" }, + { 0x20, 0x081a, "SensorEventLowThresholdCrossUpward" }, + { 0x20, 0x081b, "SensorEventLowThresholdCrossDownward" }, + { 0x20, 0x081c, "SensorEventZeroThresholdCrossUpward" }, + { 0x20, 0x081d, "SensorEventZeroThresholdCrossDownward" }, + { 0x20, 0x081e, "SensorEventPeriodExceeded" }, + { 0x20, 0x081f, "SensorEventFrequencyExceeded" }, + { 0x20, 0x0820, "SensorEventComplexTrigger" }, + { 0x20, 0x0830, "ConnectionTypePCIntegrated" }, + { 0x20, 0x0831, "ConnectionTypePCAttached" }, + { 0x20, 0x0832, "ConnectionTypePCExternal" }, + { 0x20, 0x0840, "ReportingStateReportNoEvents" }, + { 0x20, 0x0841, "ReportingStateReportAllEvents" }, + { 0x20, 0x0842, "ReportingStateReportThresholdEvents" }, + { 0x20, 0x0843, "ReportingStateWakeOnNoEvents" }, + { 0x20, 0x0844, "ReportingStateWakeOnAllEvents" }, + { 0x20, 0x0845, "ReportingStateWakeOnThresholdEvents" }, + { 0x20, 0x0846, "ReportingStateAnytime" }, + { 0x20, 0x0850, "PowerStateUndefined" }, + { 0x20, 0x0851, "PowerStateD0FullPower" }, + { 0x20, 0x0852, "PowerStateD1LowPower" }, + { 0x20, 0x0853, "PowerStateD2StandbyPowerwithWakeup" }, + { 0x20, 0x0854, "PowerStateD3SleepwithWakeup" }, + { 0x20, 0x0855, "PowerStateD4PowerOff" }, + { 0x20, 0x0860, "AccuracyDefault" }, + { 0x20, 0x0861, "AccuracyHigh" }, + { 0x20, 0x0862, "AccuracyMedium" }, + { 0x20, 0x0863, "AccuracyLow" }, + { 0x20, 0x0870, "FixQualityNoFix" }, + { 0x20, 0x0871, "FixQualityGPS" }, + { 0x20, 0x0872, "FixQualityDGPS" }, + { 0x20, 0x0880, "FixTypeNoFix" }, + { 0x20, 0x0881, "FixTypeGPSSPSModeFixValid" }, + { 0x20, 0x0882, "FixTypeDGPSSPSModeFixValid" }, + { 0x20, 0x0883, "FixTypeGPSPPSModeFixValid" }, + { 0x20, 0x0884, "FixTypeRealTimeKinematic" }, + { 0x20, 0x0885, "FixTypeFloatRTK" }, + { 0x20, 0x0886, "FixTypeEstimateddeadreckoned" }, + { 0x20, 0x0887, "FixTypeManualInputMode" }, + { 0x20, 0x0888, "FixTypeSimulatorMode" }, + { 0x20, 0x0890, "GPSOperationModeManual" }, + { 0x20, 0x0891, "GPSOperationModeAutomatic" }, + { 0x20, 0x08a0, "GPSSelectionModeAutonomous" }, + { 0x20, 0x08a1, "GPSSelectionModeDGPS" }, + { 0x20, 0x08a2, "GPSSelectionModeEstimateddeadreckoned" }, + { 0x20, 0x08a3, "GPSSelectionModeManualInput" }, + { 0x20, 0x08a4, "GPSSelectionModeSimulator" }, + { 0x20, 0x08a5, "GPSSelectionModeDataNotValid" }, + { 0x20, 0x08b0, "GPSStatusDataValid" }, + { 0x20, 0x08b1, "GPSStatusDataNotValid" }, + { 0x20, 0x08c0, "DayofWeekSunday" }, + { 0x20, 0x08c1, "DayofWeekMonday" }, + { 0x20, 0x08c2, "DayofWeekTuesday" }, + { 0x20, 0x08c3, "DayofWeekWednesday" }, + { 0x20, 0x08c4, "DayofWeekThursday" }, + { 0x20, 0x08c5, "DayofWeekFriday" }, + { 0x20, 0x08c6, "DayofWeekSaturday" }, + { 0x20, 0x08d0, "KindCategory" }, + { 0x20, 0x08d1, "KindType" }, + { 0x20, 0x08d2, "KindEvent" }, + { 0x20, 0x08d3, "KindProperty" }, + { 0x20, 0x08d4, "KindDataField" }, + { 0x20, 0x08e0, "MagnetometerAccuracyLow" }, + { 0x20, 0x08e1, "MagnetometerAccuracyMedium" }, + { 0x20, 0x08e2, "MagnetometerAccuracyHigh" }, + { 0x20, 0x08f0, "SimpleOrientationDirectionNotRotated" }, + { 0x20, 0x08f1, "SimpleOrientationDirectionRotated90DegreesCCW" }, + { 0x20, 0x08f2, "SimpleOrientationDirectionRotated180DegreesCCW" }, + { 0x20, 0x08f3, "SimpleOrientationDirectionRotated270DegreesCCW" }, + { 0x20, 0x08f4, "SimpleOrientationDirectionFaceUp" }, + { 0x20, 0x08f5, "SimpleOrientationDirectionFaceDown" }, + { 0x20, 0x0900, "VT_NULL" }, + { 0x20, 0x0901, "VT_BOOL" }, + { 0x20, 0x0902, "VT_UI1" }, + { 0x20, 0x0903, "VT_I1" }, + { 0x20, 0x0904, "VT_UI2" }, + { 0x20, 0x0905, "VT_I2" }, + { 0x20, 0x0906, "VT_UI4" }, + { 0x20, 0x0907, "VT_I4" }, + { 0x20, 0x0908, "VT_UI8" }, + { 0x20, 0x0909, "VT_I8" }, + { 0x20, 0x090a, "VT_R4" }, + { 0x20, 0x090b, "VT_R8" }, + { 0x20, 0x090c, "VT_WSTR" }, + { 0x20, 0x090d, "VT_STR" }, + { 0x20, 0x090e, "VT_CLSID" }, + { 0x20, 0x090f, "VT_VECTORVT_UI1" }, + { 0x20, 0x0910, "VT_F16E0" }, + { 0x20, 0x0911, "VT_F16E1" }, + { 0x20, 0x0912, "VT_F16E2" }, + { 0x20, 0x0913, "VT_F16E3" }, + { 0x20, 0x0914, "VT_F16E4" }, + { 0x20, 0x0915, "VT_F16E5" }, + { 0x20, 0x0916, "VT_F16E6" }, + { 0x20, 0x0917, "VT_F16E7" }, + { 0x20, 0x0918, "VT_F16E8" }, + { 0x20, 0x0919, "VT_F16E9" }, + { 0x20, 0x091a, "VT_F16EA" }, + { 0x20, 0x091b, "VT_F16EB" }, + { 0x20, 0x091c, "VT_F16EC" }, + { 0x20, 0x091d, "VT_F16ED" }, + { 0x20, 0x091e, "VT_F16EE" }, + { 0x20, 0x091f, "VT_F16EF" }, + { 0x20, 0x0920, "VT_F32E0" }, + { 0x20, 0x0921, "VT_F32E1" }, + { 0x20, 0x0922, "VT_F32E2" }, + { 0x20, 0x0923, "VT_F32E3" }, + { 0x20, 0x0924, "VT_F32E4" }, + { 0x20, 0x0925, "VT_F32E5" }, + { 0x20, 0x0926, "VT_F32E6" }, + { 0x20, 0x0927, "VT_F32E7" }, + { 0x20, 0x0928, "VT_F32E8" }, + { 0x20, 0x0929, "VT_F32E9" }, + { 0x20, 0x092a, "VT_F32EA" }, + { 0x20, 0x092b, "VT_F32EB" }, + { 0x20, 0x092c, "VT_F32EC" }, + { 0x20, 0x092d, "VT_F32ED" }, + { 0x20, 0x092e, "VT_F32EE" }, + { 0x20, 0x092f, "VT_F32EF" }, + { 0x20, 0x0930, "ActivityTypeUnknown" }, + { 0x20, 0x0931, "ActivityTypeStationary" }, + { 0x20, 0x0932, "ActivityTypeFidgeting" }, + { 0x20, 0x0933, "ActivityTypeWalking" }, + { 0x20, 0x0934, "ActivityTypeRunning" }, + { 0x20, 0x0935, "ActivityTypeInVehicle" }, + { 0x20, 0x0936, "ActivityTypeBiking" }, + { 0x20, 0x0937, "ActivityTypeIdle" }, + { 0x20, 0x0940, "UnitNotSpecified" }, + { 0x20, 0x0941, "UnitLux" }, + { 0x20, 0x0942, "UnitDegreesKelvin" }, + { 0x20, 0x0943, "UnitDegreesCelsius" }, + { 0x20, 0x0944, "UnitPascal" }, + { 0x20, 0x0945, "UnitNewton" }, + { 0x20, 0x0946, "UnitMetersSecond" }, + { 0x20, 0x0947, "UnitKilogram" }, + { 0x20, 0x0948, "UnitMeter" }, + { 0x20, 0x0949, "UnitMetersSecondSecond" }, + { 0x20, 0x094a, "UnitFarad" }, + { 0x20, 0x094b, "UnitAmpere" }, + { 0x20, 0x094c, "UnitWatt" }, + { 0x20, 0x094d, "UnitHenry" }, + { 0x20, 0x094e, "UnitOhm" }, + { 0x20, 0x094f, "UnitVolt" }, + { 0x20, 0x0950, "UnitHertz" }, + { 0x20, 0x0951, "UnitBar" }, + { 0x20, 0x0952, "UnitDegreesAnticlockwise" }, + { 0x20, 0x0953, "UnitDegreesClockwise" }, + { 0x20, 0x0954, "UnitDegrees" }, + { 0x20, 0x0955, "UnitDegreesSecond" }, + { 0x20, 0x0956, "UnitDegreesSecondSecond" }, + { 0x20, 0x0957, "UnitKnot" }, + { 0x20, 0x0958, "UnitPercent" }, + { 0x20, 0x0959, "UnitSecond" }, + { 0x20, 0x095a, "UnitMillisecond" }, + { 0x20, 0x095b, "UnitG" }, + { 0x20, 0x095c, "UnitBytes" }, + { 0x20, 0x095d, "UnitMilligauss" }, + { 0x20, 0x095e, "UnitBits" }, + { 0x20, 0x0960, "ActivityStateNoStateChange" }, + { 0x20, 0x0961, "ActivityStateStartActivity" }, + { 0x20, 0x0962, "ActivityStateEndActivity" }, + { 0x20, 0x0970, "Exponent0" }, + { 0x20, 0x0971, "Exponent1" }, + { 0x20, 0x0972, "Exponent2" }, + { 0x20, 0x0973, "Exponent3" }, + { 0x20, 0x0974, "Exponent4" }, + { 0x20, 0x0975, "Exponent5" }, + { 0x20, 0x0976, "Exponent6" }, + { 0x20, 0x0977, "Exponent7" }, + { 0x20, 0x0978, "Exponent8" }, + { 0x20, 0x0979, "Exponent9" }, + { 0x20, 0x097a, "ExponentA" }, + { 0x20, 0x097b, "ExponentB" }, + { 0x20, 0x097c, "ExponentC" }, + { 0x20, 0x097d, "ExponentD" }, + { 0x20, 0x097e, "ExponentE" }, + { 0x20, 0x097f, "ExponentF" }, + { 0x20, 0x0980, "DevicePositionUnknown" }, + { 0x20, 0x0981, "DevicePositionUnchanged" }, + { 0x20, 0x0982, "DevicePositionOnDesk" }, + { 0x20, 0x0983, "DevicePositionInHand" }, + { 0x20, 0x0984, "DevicePositionMovinginBag" }, + { 0x20, 0x0985, "DevicePositionStationaryinBag" }, + { 0x20, 0x0990, "StepTypeUnknown" }, + { 0x20, 0x0991, "StepTypeWalking" }, + { 0x20, 0x0992, "StepTypeRunning" }, + { 0x20, 0x09a0, "GestureStateUnknown" }, + { 0x20, 0x09a1, "GestureStateStarted" }, + { 0x20, 0x09a2, "GestureStateCompleted" }, + { 0x20, 0x09a3, "GestureStateCancelled" }, + { 0x20, 0x09b0, "HingeFoldContributingPanelUnknown" }, + { 0x20, 0x09b1, "HingeFoldContributingPanelPanel1" }, + { 0x20, 0x09b2, "HingeFoldContributingPanelPanel2" }, + { 0x20, 0x09b3, "HingeFoldContributingPanelBoth" }, + { 0x20, 0x09b4, "HingeFoldTypeUnknown" }, + { 0x20, 0x09b5, "HingeFoldTypeIncreasing" }, + { 0x20, 0x09b6, "HingeFoldTypeDecreasing" }, + { 0x20, 0x09c0, "HumanPresenceDetectionTypeVendorDefinedNonBiometric" }, + { 0x20, 0x09c1, "HumanPresenceDetectionTypeVendorDefinedBiometric" }, + { 0x20, 0x09c2, "HumanPresenceDetectionTypeFacialBiometric" }, + { 0x20, 0x09c3, "HumanPresenceDetectionTypeAudioBiometric" }, + { 0x20, 0x1000, "ModifierChangeSensitivityAbsolute" }, + { 0x20, 0x2000, "ModifierMaximum" }, + { 0x20, 0x3000, "ModifierMinimum" }, + { 0x20, 0x4000, "ModifierAccuracy" }, + { 0x20, 0x5000, "ModifierResolution" }, + { 0x20, 0x6000, "ModifierThresholdHigh" }, + { 0x20, 0x7000, "ModifierThresholdLow" }, + { 0x20, 0x8000, "ModifierCalibrationOffset" }, + { 0x20, 0x9000, "ModifierCalibrationMultiplier" }, + { 0x20, 0xa000, "ModifierReportInterval" }, + { 0x20, 0xb000, "ModifierFrequencyMax" }, + { 0x20, 0xc000, "ModifierPeriodMax" }, + { 0x20, 0xd000, "ModifierChangeSensitivityPercentofRange" }, + { 0x20, 0xe000, "ModifierChangeSensitivityPercentRelative" }, + { 0x20, 0xf000, "ModifierVendorReserved" }, + { 0x40, 0, "MedicalInstrument" }, + { 0x40, 0x0001, "MedicalUltrasound" }, + { 0x40, 0x0020, "VCRAcquisition" }, + { 0x40, 0x0021, "FreezeThaw" }, + { 0x40, 0x0022, "ClipStore" }, + { 0x40, 0x0023, "Update" }, + { 0x40, 0x0024, "Next" }, + { 0x40, 0x0025, "Save" }, + { 0x40, 0x0026, "Print" }, + { 0x40, 0x0027, "MicrophoneEnable" }, + { 0x40, 0x0040, "Cine" }, + { 0x40, 0x0041, "TransmitPower" }, + { 0x40, 0x0042, "Volume" }, + { 0x40, 0x0043, "Focus" }, + { 0x40, 0x0044, "Depth" }, + { 0x40, 0x0060, "SoftStepPrimary" }, + { 0x40, 0x0061, "SoftStepSecondary" }, + { 0x40, 0x0070, "DepthGainCompensation" }, + { 0x40, 0x0080, "ZoomSelect" }, + { 0x40, 0x0081, "ZoomAdjust" }, + { 0x40, 0x0082, "SpectralDopplerModeSelect" }, + { 0x40, 0x0083, "SpectralDopplerAdjust" }, + { 0x40, 0x0084, "ColorDopplerModeSelect" }, + { 0x40, 0x0085, "ColorDopplerAdjust" }, + { 0x40, 0x0086, "MotionModeSelect" }, + { 0x40, 0x0087, "MotionModeAdjust" }, + { 0x40, 0x0088, "2DModeSelect" }, + { 0x40, 0x0089, "2DModeAdjust" }, + { 0x40, 0x00a0, "SoftControlSelect" }, + { 0x40, 0x00a1, "SoftControlAdjust" }, + { 0x41, 0, "BrailleDisplay" }, + { 0x41, 0x0001, "BrailleDisplay" }, + { 0x41, 0x0002, "BrailleRow" }, + { 0x41, 0x0003, "8DotBrailleCell" }, + { 0x41, 0x0004, "6DotBrailleCell" }, + { 0x41, 0x0005, "NumberofBrailleCells" }, + { 0x41, 0x0006, "ScreenReaderControl" }, + { 0x41, 0x0007, "ScreenReaderIdentifier" }, + { 0x41, 0x00fa, "RouterSet1" }, + { 0x41, 0x00fb, "RouterSet2" }, + { 0x41, 0x00fc, "RouterSet3" }, + { 0x41, 0x0100, "RouterKey" }, + { 0x41, 0x0101, "RowRouterKey" }, + { 0x41, 0x0200, "BrailleButtons" }, + { 0x41, 0x0201, "BrailleKeyboardDot1" }, + { 0x41, 0x0202, "BrailleKeyboardDot2" }, + { 0x41, 0x0203, "BrailleKeyboardDot3" }, + { 0x41, 0x0204, "BrailleKeyboardDot4" }, + { 0x41, 0x0205, "BrailleKeyboardDot5" }, + { 0x41, 0x0206, "BrailleKeyboardDot6" }, + { 0x41, 0x0207, "BrailleKeyboardDot7" }, + { 0x41, 0x0208, "BrailleKeyboardDot8" }, + { 0x41, 0x0209, "BrailleKeyboardSpace" }, + { 0x41, 0x020a, "BrailleKeyboardLeftSpace" }, + { 0x41, 0x020b, "BrailleKeyboardRightSpace" }, + { 0x41, 0x020c, "BrailleFaceControls" }, + { 0x41, 0x020d, "BrailleLeftControls" }, + { 0x41, 0x020e, "BrailleRightControls" }, + { 0x41, 0x020f, "BrailleTopControls" }, + { 0x41, 0x0210, "BrailleJoystickCenter" }, + { 0x41, 0x0211, "BrailleJoystickUp" }, + { 0x41, 0x0212, "BrailleJoystickDown" }, + { 0x41, 0x0213, "BrailleJoystickLeft" }, + { 0x41, 0x0214, "BrailleJoystickRight" }, + { 0x41, 0x0215, "BrailleDPadCenter" }, + { 0x41, 0x0216, "BrailleDPadUp" }, + { 0x41, 0x0217, "BrailleDPadDown" }, + { 0x41, 0x0218, "BrailleDPadLeft" }, + { 0x41, 0x0219, "BrailleDPadRight" }, + { 0x41, 0x021a, "BraillePanLeft" }, + { 0x41, 0x021b, "BraillePanRight" }, + { 0x41, 0x021c, "BrailleRockerUp" }, + { 0x41, 0x021d, "BrailleRockerDown" }, + { 0x41, 0x021e, "BrailleRockerPress" }, + { 0x59, 0, "LightingAndIllumination" }, + { 0x59, 0x0001, "LampArray" }, + { 0x59, 0x0002, "LampArrayAttributesReport" }, + { 0x59, 0x0003, "LampCount" }, + { 0x59, 0x0004, "BoundingBoxWidthInMicrometers" }, + { 0x59, 0x0005, "BoundingBoxHeightInMicrometers" }, + { 0x59, 0x0006, "BoundingBoxDepthInMicrometers" }, + { 0x59, 0x0007, "LampArrayKind" }, + { 0x59, 0x0008, "MinUpdateIntervalInMicroseconds" }, + { 0x59, 0x0020, "LampAttributesRequestReport" }, + { 0x59, 0x0021, "LampId" }, + { 0x59, 0x0022, "LampAttributesResponseReport" }, + { 0x59, 0x0023, "PositionXInMicrometers" }, + { 0x59, 0x0024, "PositionYInMicrometers" }, + { 0x59, 0x0025, "PositionZInMicrometers" }, + { 0x59, 0x0026, "LampPurposes" }, + { 0x59, 0x0027, "UpdateLatencyInMicroseconds" }, + { 0x59, 0x0028, "RedLevelCount" }, + { 0x59, 0x0029, "GreenLevelCount" }, + { 0x59, 0x002a, "BlueLevelCount" }, + { 0x59, 0x002b, "IntensityLevelCount" }, + { 0x59, 0x002c, "IsProgrammable" }, + { 0x59, 0x002d, "InputBinding" }, + { 0x59, 0x0050, "LampMultiUpdateReport" }, + { 0x59, 0x0051, "RedUpdateChannel" }, + { 0x59, 0x0052, "GreenUpdateChannel" }, + { 0x59, 0x0053, "BlueUpdateChannel" }, + { 0x59, 0x0054, "IntensityUpdateChannel" }, + { 0x59, 0x0055, "LampUpdateFlags" }, + { 0x59, 0x0060, "LampRangeUpdateReport" }, + { 0x59, 0x0061, "LampIdStart" }, + { 0x59, 0x0062, "LampIdEnd" }, + { 0x59, 0x0070, "LampArrayControlReport" }, + { 0x59, 0x0071, "AutonomousMode" }, + { 0x80, 0, "Monitor" }, + { 0x80, 0x0001, "MonitorControl" }, + { 0x80, 0x0002, "EDIDInformation" }, + { 0x80, 0x0003, "VDIFInformation" }, + { 0x80, 0x0004, "VESAVersion" }, + { 0x81, 0, "MonitorEnumerated" }, + { 0x82, 0, "VESAVirtualControls" }, + { 0x82, 0x0001, "Degauss" }, + { 0x82, 0x0010, "Brightness" }, + { 0x82, 0x0012, "Contrast" }, + { 0x82, 0x0016, "RedVideoGain" }, + { 0x82, 0x0018, "GreenVideoGain" }, + { 0x82, 0x001a, "BlueVideoGain" }, + { 0x82, 0x001c, "Focus" }, + { 0x82, 0x0020, "HorizontalPosition" }, + { 0x82, 0x0022, "HorizontalSize" }, + { 0x82, 0x0024, "HorizontalPincushion" }, + { 0x82, 0x0026, "HorizontalPincushionBalance" }, + { 0x82, 0x0028, "HorizontalMisconvergence" }, + { 0x82, 0x002a, "HorizontalLinearity" }, + { 0x82, 0x002c, "HorizontalLinearityBalance" }, + { 0x82, 0x0030, "VerticalPosition" }, + { 0x82, 0x0032, "VerticalSize" }, + { 0x82, 0x0034, "VerticalPincushion" }, + { 0x82, 0x0036, "VerticalPincushionBalance" }, + { 0x82, 0x0038, "VerticalMisconvergence" }, + { 0x82, 0x003a, "VerticalLinearity" }, + { 0x82, 0x003c, "VerticalLinearityBalance" }, + { 0x82, 0x0040, "ParallelogramDistortionKeyBalance" }, + { 0x82, 0x0042, "TrapezoidalDistortionKey" }, + { 0x82, 0x0044, "TiltRotation" }, + { 0x82, 0x0046, "TopCornerDistortionControl" }, + { 0x82, 0x0048, "TopCornerDistortionBalance" }, + { 0x82, 0x004a, "BottomCornerDistortionControl" }, + { 0x82, 0x004c, "BottomCornerDistortionBalance" }, + { 0x82, 0x0056, "HorizontalMoire" }, + { 0x82, 0x0058, "VerticalMoire" }, + { 0x82, 0x005e, "InputLevelSelect" }, + { 0x82, 0x0060, "InputSourceSelect" }, + { 0x82, 0x006c, "RedVideoBlackLevel" }, + { 0x82, 0x006e, "GreenVideoBlackLevel" }, + { 0x82, 0x0070, "BlueVideoBlackLevel" }, + { 0x82, 0x00a2, "AutoSizeCenter" }, + { 0x82, 0x00a4, "PolarityHorizontalSynchronization" }, + { 0x82, 0x00a6, "PolarityVerticalSynchronization" }, + { 0x82, 0x00a8, "SynchronizationType" }, + { 0x82, 0x00aa, "ScreenOrientation" }, + { 0x82, 0x00ac, "HorizontalFrequency" }, + { 0x82, 0x00ae, "VerticalFrequency" }, + { 0x82, 0x00b0, "Settings" }, + { 0x82, 0x00ca, "OnScreenDisplay" }, + { 0x82, 0x00d4, "StereoMode" }, + { 0x84, 0, "Power" }, + { 0x84, 0x0001, "iName" }, + { 0x84, 0x0002, "PresentStatus" }, + { 0x84, 0x0003, "ChangedStatus" }, + { 0x84, 0x0004, "UPS" }, + { 0x84, 0x0005, "PowerSupply" }, + { 0x84, 0x0010, "BatterySystem" }, + { 0x84, 0x0011, "BatterySystemId" }, + { 0x84, 0x0012, "Battery" }, + { 0x84, 0x0013, "BatteryId" }, + { 0x84, 0x0014, "Charger" }, + { 0x84, 0x0015, "ChargerId" }, + { 0x84, 0x0016, "PowerConverter" }, + { 0x84, 0x0017, "PowerConverterId" }, + { 0x84, 0x0018, "OutletSystem" }, + { 0x84, 0x0019, "OutletSystemId" }, + { 0x84, 0x001a, "Input" }, + { 0x84, 0x001b, "InputId" }, + { 0x84, 0x001c, "Output" }, + { 0x84, 0x001d, "OutputId" }, + { 0x84, 0x001e, "Flow" }, + { 0x84, 0x001f, "FlowId" }, + { 0x84, 0x0020, "Outlet" }, + { 0x84, 0x0021, "OutletId" }, + { 0x84, 0x0022, "Gang" }, + { 0x84, 0x0023, "GangId" }, + { 0x84, 0x0024, "PowerSummary" }, + { 0x84, 0x0025, "PowerSummaryId" }, + { 0x84, 0x0030, "Voltage" }, + { 0x84, 0x0031, "Current" }, + { 0x84, 0x0032, "Frequency" }, + { 0x84, 0x0033, "ApparentPower" }, + { 0x84, 0x0034, "ActivePower" }, + { 0x84, 0x0035, "PercentLoad" }, + { 0x84, 0x0036, "Temperature" }, + { 0x84, 0x0037, "Humidity" }, + { 0x84, 0x0038, "BadCount" }, + { 0x84, 0x0040, "ConfigVoltage" }, + { 0x84, 0x0041, "ConfigCurrent" }, + { 0x84, 0x0042, "ConfigFrequency" }, + { 0x84, 0x0043, "ConfigApparentPower" }, + { 0x84, 0x0044, "ConfigActivePower" }, + { 0x84, 0x0045, "ConfigPercentLoad" }, + { 0x84, 0x0046, "ConfigTemperature" }, + { 0x84, 0x0047, "ConfigHumidity" }, + { 0x84, 0x0050, "SwitchOnControl" }, + { 0x84, 0x0051, "SwitchOffControl" }, + { 0x84, 0x0052, "ToggleControl" }, + { 0x84, 0x0053, "LowVoltageTransfer" }, + { 0x84, 0x0054, "HighVoltageTransfer" }, + { 0x84, 0x0055, "DelayBeforeReboot" }, + { 0x84, 0x0056, "DelayBeforeStartup" }, + { 0x84, 0x0057, "DelayBeforeShutdown" }, + { 0x84, 0x0058, "Test" }, + { 0x84, 0x0059, "ModuleReset" }, + { 0x84, 0x005a, "AudibleAlarmControl" }, + { 0x84, 0x0060, "Present" }, + { 0x84, 0x0061, "Good" }, + { 0x84, 0x0062, "InternalFailure" }, + { 0x84, 0x0063, "VoltagOutOfRange" }, + { 0x84, 0x0064, "FrequencyOutOfRange" }, + { 0x84, 0x0065, "Overload" }, + { 0x84, 0x0066, "OverCharged" }, + { 0x84, 0x0067, "OverTemperature" }, + { 0x84, 0x0068, "ShutdownRequested" }, + { 0x84, 0x0069, "ShutdownImminent" }, + { 0x84, 0x006b, "SwitchOnOff" }, + { 0x84, 0x006c, "Switchable" }, + { 0x84, 0x006d, "Used" }, + { 0x84, 0x006e, "Boost" }, + { 0x84, 0x006f, "Buck" }, + { 0x84, 0x0070, "Initialized" }, + { 0x84, 0x0071, "Tested" }, + { 0x84, 0x0072, "AwaitingPower" }, + { 0x84, 0x0073, "CommunicationLost" }, + { 0x84, 0x00fd, "iManufacturer" }, + { 0x84, 0x00fe, "iProduct" }, + { 0x84, 0x00ff, "iSerialNumber" }, + { 0x85, 0, "BatterySystem" }, + { 0x85, 0x0001, "SmartBatteryBatteryMode" }, + { 0x85, 0x0002, "SmartBatteryBatteryStatus" }, + { 0x85, 0x0003, "SmartBatteryAlarmWarning" }, + { 0x85, 0x0004, "SmartBatteryChargerMode" }, + { 0x85, 0x0005, "SmartBatteryChargerStatus" }, + { 0x85, 0x0006, "SmartBatteryChargerSpecInfo" }, + { 0x85, 0x0007, "SmartBatterySelectorState" }, + { 0x85, 0x0008, "SmartBatterySelectorPresets" }, + { 0x85, 0x0009, "SmartBatterySelectorInfo" }, + { 0x85, 0x0010, "OptionalMfgFunction1" }, + { 0x85, 0x0011, "OptionalMfgFunction2" }, + { 0x85, 0x0012, "OptionalMfgFunction3" }, + { 0x85, 0x0013, "OptionalMfgFunction4" }, + { 0x85, 0x0014, "OptionalMfgFunction5" }, + { 0x85, 0x0015, "ConnectionToSMBus" }, + { 0x85, 0x0016, "OutputConnection" }, + { 0x85, 0x0017, "ChargerConnection" }, + { 0x85, 0x0018, "BatteryInsertion" }, + { 0x85, 0x0019, "UseNext" }, + { 0x85, 0x001a, "OKToUse" }, + { 0x85, 0x001b, "BatterySupported" }, + { 0x85, 0x001c, "SelectorRevision" }, + { 0x85, 0x001d, "ChargingIndicator" }, + { 0x85, 0x0028, "ManufacturerAccess" }, + { 0x85, 0x0029, "RemainingCapacityLimit" }, + { 0x85, 0x002a, "RemainingTimeLimit" }, + { 0x85, 0x002b, "AtRate" }, + { 0x85, 0x002c, "CapacityMode" }, + { 0x85, 0x002d, "BroadcastToCharger" }, + { 0x85, 0x002e, "PrimaryBattery" }, + { 0x85, 0x002f, "ChargeController" }, + { 0x85, 0x0040, "TerminateCharge" }, + { 0x85, 0x0041, "TerminateDischarge" }, + { 0x85, 0x0042, "BelowRemainingCapacityLimit" }, + { 0x85, 0x0043, "RemainingTimeLimitExpired" }, + { 0x85, 0x0044, "Charging" }, + { 0x85, 0x0045, "Discharging" }, + { 0x85, 0x0046, "FullyCharged" }, + { 0x85, 0x0047, "FullyDischarged" }, + { 0x85, 0x0048, "ConditioningFlag" }, + { 0x85, 0x0049, "AtRateOK" }, + { 0x85, 0x004a, "SmartBatteryErrorCode" }, + { 0x85, 0x004b, "NeedReplacement" }, + { 0x85, 0x0060, "AtRateTimeToFull" }, + { 0x85, 0x0061, "AtRateTimeToEmpty" }, + { 0x85, 0x0062, "AverageCurrent" }, + { 0x85, 0x0063, "MaxError" }, + { 0x85, 0x0064, "RelativeStateOfCharge" }, + { 0x85, 0x0065, "AbsoluteStateOfCharge" }, + { 0x85, 0x0066, "RemainingCapacity" }, + { 0x85, 0x0067, "FullChargeCapacity" }, + { 0x85, 0x0068, "RunTimeToEmpty" }, + { 0x85, 0x0069, "AverageTimeToEmpty" }, + { 0x85, 0x006a, "AverageTimeToFull" }, + { 0x85, 0x006b, "CycleCount" }, + { 0x85, 0x0080, "BatteryPackModelLevel" }, + { 0x85, 0x0081, "InternalChargeController" }, + { 0x85, 0x0082, "PrimaryBatterySupport" }, + { 0x85, 0x0083, "DesignCapacity" }, + { 0x85, 0x0084, "SpecificationInfo" }, + { 0x85, 0x0085, "ManufactureDate" }, + { 0x85, 0x0086, "SerialNumber" }, + { 0x85, 0x0087, "iManufacturerName" }, + { 0x85, 0x0088, "iDeviceName" }, + { 0x85, 0x0089, "iDeviceChemistry" }, + { 0x85, 0x008a, "ManufacturerData" }, + { 0x85, 0x008b, "Rechargable" }, + { 0x85, 0x008c, "WarningCapacityLimit" }, + { 0x85, 0x008d, "CapacityGranularity1" }, + { 0x85, 0x008e, "CapacityGranularity2" }, + { 0x85, 0x008f, "iOEMInformation" }, + { 0x85, 0x00c0, "InhibitCharge" }, + { 0x85, 0x00c1, "EnablePolling" }, + { 0x85, 0x00c2, "ResetToZero" }, + { 0x85, 0x00d0, "ACPresent" }, + { 0x85, 0x00d1, "BatteryPresent" }, + { 0x85, 0x00d2, "PowerFail" }, + { 0x85, 0x00d3, "AlarmInhibited" }, + { 0x85, 0x00d4, "ThermistorUnderRange" }, + { 0x85, 0x00d5, "ThermistorHot" }, + { 0x85, 0x00d6, "ThermistorCold" }, + { 0x85, 0x00d7, "ThermistorOverRange" }, + { 0x85, 0x00d8, "VoltageOutOfRange" }, + { 0x85, 0x00d9, "CurrentOutOfRange" }, + { 0x85, 0x00da, "CurrentNotRegulated" }, + { 0x85, 0x00db, "VoltageNotRegulated" }, + { 0x85, 0x00dc, "MasterMode" }, + { 0x85, 0x00f0, "ChargerSelectorSupport" }, + { 0x85, 0x00f1, "ChargerSpec" }, + { 0x85, 0x00f2, "Level2" }, + { 0x85, 0x00f3, "Level3" }, + { 0x8c, 0, "BarcodeScanner" }, + { 0x8c, 0x0001, "BarcodeBadgeReader" }, + { 0x8c, 0x0002, "BarcodeScanner" }, + { 0x8c, 0x0003, "DumbBarCodeScanner" }, + { 0x8c, 0x0004, "CordlessScannerBase" }, + { 0x8c, 0x0005, "BarCodeScannerCradle" }, + { 0x8c, 0x0010, "AttributeReport" }, + { 0x8c, 0x0011, "SettingsReport" }, + { 0x8c, 0x0012, "ScannedDataReport" }, + { 0x8c, 0x0013, "RawScannedDataReport" }, + { 0x8c, 0x0014, "TriggerReport" }, + { 0x8c, 0x0015, "StatusReport" }, + { 0x8c, 0x0016, "UPCEANControlReport" }, + { 0x8c, 0x0017, "EAN23LabelControlReport" }, + { 0x8c, 0x0018, "Code39ControlReport" }, + { 0x8c, 0x0019, "Interleaved2of5ControlReport" }, + { 0x8c, 0x001a, "Standard2of5ControlReport" }, + { 0x8c, 0x001b, "MSIPlesseyControlReport" }, + { 0x8c, 0x001c, "CodabarControlReport" }, + { 0x8c, 0x001d, "Code128ControlReport" }, + { 0x8c, 0x001e, "Misc1DControlReport" }, + { 0x8c, 0x001f, "2DControlReport" }, + { 0x8c, 0x0030, "AimingPointerMode" }, + { 0x8c, 0x0031, "BarCodePresentSensor" }, + { 0x8c, 0x0032, "Class1ALaser" }, + { 0x8c, 0x0033, "Class2Laser" }, + { 0x8c, 0x0034, "HeaterPresent" }, + { 0x8c, 0x0035, "ContactScanner" }, + { 0x8c, 0x0036, "ElectronicArticleSurveillanceNotification" }, + { 0x8c, 0x0037, "ConstantElectronicArticleSurveillance" }, + { 0x8c, 0x0038, "ErrorIndication" }, + { 0x8c, 0x0039, "FixedBeeper" }, + { 0x8c, 0x003a, "GoodDecodeIndication" }, + { 0x8c, 0x003b, "HandsFreeScanning" }, + { 0x8c, 0x003c, "IntrinsicallySafe" }, + { 0x8c, 0x003d, "KlasseEinsLaser" }, + { 0x8c, 0x003e, "LongRangeScanner" }, + { 0x8c, 0x003f, "MirrorSpeedControl" }, + { 0x8c, 0x0040, "NotOnFileIndication" }, + { 0x8c, 0x0041, "ProgrammableBeeper" }, + { 0x8c, 0x0042, "Triggerless" }, + { 0x8c, 0x0043, "Wand" }, + { 0x8c, 0x0044, "WaterResistant" }, + { 0x8c, 0x0045, "MultiRangeScanner" }, + { 0x8c, 0x0046, "ProximitySensor" }, + { 0x8c, 0x004d, "FragmentDecoding" }, + { 0x8c, 0x004e, "ScannerReadConfidence" }, + { 0x8c, 0x004f, "DataPrefix" }, + { 0x8c, 0x0050, "PrefixAIMI" }, + { 0x8c, 0x0051, "PrefixNone" }, + { 0x8c, 0x0052, "PrefixProprietary" }, + { 0x8c, 0x0055, "ActiveTime" }, + { 0x8c, 0x0056, "AimingLaserPattern" }, + { 0x8c, 0x0057, "BarCodePresent" }, + { 0x8c, 0x0058, "BeeperState" }, + { 0x8c, 0x0059, "LaserOnTime" }, + { 0x8c, 0x005a, "LaserState" }, + { 0x8c, 0x005b, "LockoutTime" }, + { 0x8c, 0x005c, "MotorState" }, + { 0x8c, 0x005d, "MotorTimeout" }, + { 0x8c, 0x005e, "PowerOnResetScanner" }, + { 0x8c, 0x005f, "PreventReadofBarcodes" }, + { 0x8c, 0x0060, "InitiateBarcodeRead" }, + { 0x8c, 0x0061, "TriggerState" }, + { 0x8c, 0x0062, "TriggerMode" }, + { 0x8c, 0x0063, "TriggerModeBlinkingLaserOn" }, + { 0x8c, 0x0064, "TriggerModeContinuousLaserOn" }, + { 0x8c, 0x0065, "TriggerModeLaseronwhilePulled" }, + { 0x8c, 0x0066, "TriggerModeLaserstaysonafterrelease" }, + { 0x8c, 0x006d, "CommitParameterstoNVM" }, + { 0x8c, 0x006e, "ParameterScanning" }, + { 0x8c, 0x006f, "ParametersChanged" }, + { 0x8c, 0x0070, "Setparameterdefaultvalues" }, + { 0x8c, 0x0075, "ScannerInCradle" }, + { 0x8c, 0x0076, "ScannerInRange" }, + { 0x8c, 0x007a, "AimDuration" }, + { 0x8c, 0x007b, "GoodReadLampDuration" }, + { 0x8c, 0x007c, "GoodReadLampIntensity" }, + { 0x8c, 0x007d, "GoodReadLED" }, + { 0x8c, 0x007e, "GoodReadToneFrequency" }, + { 0x8c, 0x007f, "GoodReadToneLength" }, + { 0x8c, 0x0080, "GoodReadToneVolume" }, + { 0x8c, 0x0082, "NoReadMessage" }, + { 0x8c, 0x0083, "NotonFileVolume" }, + { 0x8c, 0x0084, "PowerupBeep" }, + { 0x8c, 0x0085, "SoundErrorBeep" }, + { 0x8c, 0x0086, "SoundGoodReadBeep" }, + { 0x8c, 0x0087, "SoundNotOnFileBeep" }, + { 0x8c, 0x0088, "GoodReadWhentoWrite" }, + { 0x8c, 0x0089, "GRWTIAfterDecode" }, + { 0x8c, 0x008a, "GRWTIBeepLampaftertransmit" }, + { 0x8c, 0x008b, "GRWTINoBeepLampuseatall" }, + { 0x8c, 0x0091, "BooklandEAN" }, + { 0x8c, 0x0092, "ConvertEAN8to13Type" }, + { 0x8c, 0x0093, "ConvertUPCAtoEAN13" }, + { 0x8c, 0x0094, "ConvertUPCEtoA" }, + { 0x8c, 0x0095, "EAN13" }, + { 0x8c, 0x0096, "EAN8" }, + { 0x8c, 0x0097, "EAN99128Mandatory" }, + { 0x8c, 0x0098, "EAN99P5128Optional" }, + { 0x8c, 0x0099, "EnableEANTwoLabel" }, + { 0x8c, 0x009a, "UPCEAN" }, + { 0x8c, 0x009b, "UPCEANCouponCode" }, + { 0x8c, 0x009c, "UPCEANPeriodicals" }, + { 0x8c, 0x009d, "UPCA" }, + { 0x8c, 0x009e, "UPCAwith128Mandatory" }, + { 0x8c, 0x009f, "UPCAwith128Optional" }, + { 0x8c, 0x00a0, "UPCAwithP5Optional" }, + { 0x8c, 0x00a1, "UPCE" }, + { 0x8c, 0x00a2, "UPCE1" }, + { 0x8c, 0x00a9, "Periodical" }, + { 0x8c, 0x00aa, "PeriodicalAutoDiscriminate2" }, + { 0x8c, 0x00ab, "PeriodicalOnlyDecodewith2" }, + { 0x8c, 0x00ac, "PeriodicalIgnore2" }, + { 0x8c, 0x00ad, "PeriodicalAutoDiscriminate5" }, + { 0x8c, 0x00ae, "PeriodicalOnlyDecodewith5" }, + { 0x8c, 0x00af, "PeriodicalIgnore5" }, + { 0x8c, 0x00b0, "Check" }, + { 0x8c, 0x00b1, "CheckDisablePrice" }, + { 0x8c, 0x00b2, "CheckEnable4digitPrice" }, + { 0x8c, 0x00b3, "CheckEnable5digitPrice" }, + { 0x8c, 0x00b4, "CheckEnableEuropean4digitPrice" }, + { 0x8c, 0x00b5, "CheckEnableEuropean5digitPrice" }, + { 0x8c, 0x00b7, "EANTwoLabel" }, + { 0x8c, 0x00b8, "EANThreeLabel" }, + { 0x8c, 0x00b9, "EAN8FlagDigit1" }, + { 0x8c, 0x00ba, "EAN8FlagDigit2" }, + { 0x8c, 0x00bb, "EAN8FlagDigit3" }, + { 0x8c, 0x00bc, "EAN13FlagDigit1" }, + { 0x8c, 0x00bd, "EAN13FlagDigit2" }, + { 0x8c, 0x00be, "EAN13FlagDigit3" }, + { 0x8c, 0x00bf, "AddEAN23LabelDefinition" }, + { 0x8c, 0x00c0, "ClearallEAN23LabelDefinitions" }, + { 0x8c, 0x00c3, "Codabar" }, + { 0x8c, 0x00c4, "Code128" }, + { 0x8c, 0x00c7, "Code39" }, + { 0x8c, 0x00c8, "Code93" }, + { 0x8c, 0x00c9, "FullASCIIConversion" }, + { 0x8c, 0x00ca, "Interleaved2of5" }, + { 0x8c, 0x00cb, "ItalianPharmacyCode" }, + { 0x8c, 0x00cc, "MSIPlessey" }, + { 0x8c, 0x00cd, "Standard2of5IATA" }, + { 0x8c, 0x00ce, "Standard2of5" }, + { 0x8c, 0x00d3, "TransmitStartStop" }, + { 0x8c, 0x00d4, "TriOptic" }, + { 0x8c, 0x00d5, "UCCEAN128" }, + { 0x8c, 0x00d6, "CheckDigit" }, + { 0x8c, 0x00d7, "CheckDigitDisable" }, + { 0x8c, 0x00d8, "CheckDigitEnableInterleaved2of5OPCC" }, + { 0x8c, 0x00d9, "CheckDigitEnableInterleaved2of5USS" }, + { 0x8c, 0x00da, "CheckDigitEnableStandard2of5OPCC" }, + { 0x8c, 0x00db, "CheckDigitEnableStandard2of5USS" }, + { 0x8c, 0x00dc, "CheckDigitEnableOneMSIPlessey" }, + { 0x8c, 0x00dd, "CheckDigitEnableTwoMSIPlessey" }, + { 0x8c, 0x00de, "CheckDigitCodabarEnable" }, + { 0x8c, 0x00df, "CheckDigitCode39Enable" }, + { 0x8c, 0x00f0, "TransmitCheckDigit" }, + { 0x8c, 0x00f1, "DisableCheckDigitTransmit" }, + { 0x8c, 0x00f2, "EnableCheckDigitTransmit" }, + { 0x8c, 0x00fb, "SymbologyIdentifier1" }, + { 0x8c, 0x00fc, "SymbologyIdentifier2" }, + { 0x8c, 0x00fd, "SymbologyIdentifier3" }, + { 0x8c, 0x00fe, "DecodedData" }, + { 0x8c, 0x00ff, "DecodeDataContinued" }, + { 0x8c, 0x0100, "BarSpaceData" }, + { 0x8c, 0x0101, "ScannerDataAccuracy" }, + { 0x8c, 0x0102, "RawDataPolarity" }, + { 0x8c, 0x0103, "PolarityInvertedBarCode" }, + { 0x8c, 0x0104, "PolarityNormalBarCode" }, + { 0x8c, 0x0106, "MinimumLengthtoDecode" }, + { 0x8c, 0x0107, "MaximumLengthtoDecode" }, + { 0x8c, 0x0108, "DiscreteLengthtoDecode1" }, + { 0x8c, 0x0109, "DiscreteLengthtoDecode2" }, + { 0x8c, 0x010a, "DataLengthMethod" }, + { 0x8c, 0x010b, "DLMethodReadany" }, + { 0x8c, 0x010c, "DLMethodCheckinRange" }, + { 0x8c, 0x010d, "DLMethodCheckforDiscrete" }, + { 0x8c, 0x0110, "AztecCode" }, + { 0x8c, 0x0111, "BC412" }, + { 0x8c, 0x0112, "ChannelCode" }, + { 0x8c, 0x0113, "Code16" }, + { 0x8c, 0x0114, "Code32" }, + { 0x8c, 0x0115, "Code49" }, + { 0x8c, 0x0116, "CodeOne" }, + { 0x8c, 0x0117, "Colorcode" }, + { 0x8c, 0x0118, "DataMatrix" }, + { 0x8c, 0x0119, "MaxiCode" }, + { 0x8c, 0x011a, "MicroPDF" }, + { 0x8c, 0x011b, "PDF417" }, + { 0x8c, 0x011c, "PosiCode" }, + { 0x8c, 0x011d, "QRCode" }, + { 0x8c, 0x011e, "SuperCode" }, + { 0x8c, 0x011f, "UltraCode" }, + { 0x8c, 0x0120, "USD5SlugCode" }, + { 0x8c, 0x0121, "VeriCode" }, + { 0x8d, 0, "Scales" }, + { 0x8d, 0x0001, "Scales" }, + { 0x8d, 0x0020, "ScaleDevice" }, + { 0x8d, 0x0021, "ScaleClass" }, + { 0x8d, 0x0022, "ScaleClassIMetric" }, + { 0x8d, 0x0023, "ScaleClassIIMetric" }, + { 0x8d, 0x0024, "ScaleClassIIIMetric" }, + { 0x8d, 0x0025, "ScaleClassIIILMetric" }, + { 0x8d, 0x0026, "ScaleClassIVMetric" }, + { 0x8d, 0x0027, "ScaleClassIIIEnglish" }, + { 0x8d, 0x0028, "ScaleClassIIILEnglish" }, + { 0x8d, 0x0029, "ScaleClassIVEnglish" }, + { 0x8d, 0x002a, "ScaleClassGeneric" }, + { 0x8d, 0x0030, "ScaleAttributeReport" }, + { 0x8d, 0x0031, "ScaleControlReport" }, + { 0x8d, 0x0032, "ScaleDataReport" }, + { 0x8d, 0x0033, "ScaleStatusReport" }, + { 0x8d, 0x0034, "ScaleWeightLimitReport" }, + { 0x8d, 0x0035, "ScaleStatisticsReport" }, + { 0x8d, 0x0040, "DataWeight" }, + { 0x8d, 0x0041, "DataScaling" }, + { 0x8d, 0x0050, "WeightUnit" }, + { 0x8d, 0x0051, "WeightUnitMilligram" }, + { 0x8d, 0x0052, "WeightUnitGram" }, + { 0x8d, 0x0053, "WeightUnitKilogram" }, + { 0x8d, 0x0054, "WeightUnitCarats" }, + { 0x8d, 0x0055, "WeightUnitTaels" }, + { 0x8d, 0x0056, "WeightUnitGrains" }, + { 0x8d, 0x0057, "WeightUnitPennyweights" }, + { 0x8d, 0x0058, "WeightUnitMetricTon" }, + { 0x8d, 0x0059, "WeightUnitAvoirTon" }, + { 0x8d, 0x005a, "WeightUnitTroyOunce" }, + { 0x8d, 0x005b, "WeightUnitOunce" }, + { 0x8d, 0x005c, "WeightUnitPound" }, + { 0x8d, 0x0060, "CalibrationCount" }, + { 0x8d, 0x0061, "ReZeroCount" }, + { 0x8d, 0x0070, "ScaleStatus" }, + { 0x8d, 0x0071, "ScaleStatusFault" }, + { 0x8d, 0x0072, "ScaleStatusStableatCenterofZero" }, + { 0x8d, 0x0073, "ScaleStatusInMotion" }, + { 0x8d, 0x0074, "ScaleStatusWeightStable" }, + { 0x8d, 0x0075, "ScaleStatusUnderZero" }, + { 0x8d, 0x0076, "ScaleStatusOverWeightLimit" }, + { 0x8d, 0x0077, "ScaleStatusRequiresCalibration" }, + { 0x8d, 0x0078, "ScaleStatusRequiresRezeroing" }, + { 0x8d, 0x0080, "ZeroScale" }, + { 0x8d, 0x0081, "EnforcedZeroReturn" }, + { 0x8e, 0, "MagneticStripeReader" }, + { 0x8e, 0x0001, "MSRDeviceReadOnly" }, + { 0x8e, 0x0011, "Track1Length" }, + { 0x8e, 0x0012, "Track2Length" }, + { 0x8e, 0x0013, "Track3Length" }, + { 0x8e, 0x0014, "TrackJISLength" }, + { 0x8e, 0x0020, "TrackData" }, + { 0x8e, 0x0021, "Track1Data" }, + { 0x8e, 0x0022, "Track2Data" }, + { 0x8e, 0x0023, "Track3Data" }, + { 0x8e, 0x0024, "TrackJISData" }, + { 0x90, 0, "CameraControl" }, + { 0x90, 0x0020, "CameraAutofocus" }, + { 0x90, 0x0021, "CameraShutter" }, + { 0x91, 0, "Arcade" }, + { 0x91, 0x0001, "GeneralPurposeIOCard" }, + { 0x91, 0x0002, "CoinDoor" }, + { 0x91, 0x0003, "WatchdogTimer" }, + { 0x91, 0x0030, "GeneralPurposeAnalogInputState" }, + { 0x91, 0x0031, "GeneralPurposeDigitalInputState" }, + { 0x91, 0x0032, "GeneralPurposeOpticalInputState" }, + { 0x91, 0x0033, "GeneralPurposeDigitalOutputState" }, + { 0x91, 0x0034, "NumberofCoinDoors" }, + { 0x91, 0x0035, "CoinDrawerDropCount" }, + { 0x91, 0x0036, "CoinDrawerStart" }, + { 0x91, 0x0037, "CoinDrawerService" }, + { 0x91, 0x0038, "CoinDrawerTilt" }, + { 0x91, 0x0039, "CoinDoorTest" }, + { 0x91, 0x0040, "CoinDoorLockout" }, + { 0x91, 0x0041, "WatchdogTimeout" }, + { 0x91, 0x0042, "WatchdogAction" }, + { 0x91, 0x0043, "WatchdogReboot" }, + { 0x91, 0x0044, "WatchdogRestart" }, + { 0x91, 0x0045, "AlarmInput" }, + { 0x91, 0x0046, "CoinDoorCounter" }, + { 0x91, 0x0047, "IODirectionMapping" }, + { 0x91, 0x0048, "SetIODirectionMapping" }, + { 0x91, 0x0049, "ExtendedOpticalInputState" }, + { 0x91, 0x004a, "PinPadInputState" }, + { 0x91, 0x004b, "PinPadStatus" }, + { 0x91, 0x004c, "PinPadOutput" }, + { 0x91, 0x004d, "PinPadCommand" }, + { 0xf1d0, 0, "FIDOAlliance" }, + { 0xf1d0, 0x0001, "U2FAuthenticatorDevice" }, + { 0xf1d0, 0x0020, "InputReportData" }, + { 0xf1d0, 0x0021, "OutputReportData" }, + /* pages 0xff00 to 0xffff are vendor-specific */ + { 0xffff, 0, "Vendor-specific-FF" }, + { 0, 0, NULL } }; /* Either output directly into simple seq_file, or (if f == NULL) @@ -510,8 +2881,12 @@ static char *resolv_usage_page(unsigned page, struct seq_file *f) { char *hid_resolv_usage(unsigned usage, struct seq_file *f) { const struct hid_usage_entry *p; + const struct hid_usage_entry *m; char *buf = NULL; int len = 0; + const char *modifier = NULL; + unsigned int usage_modifier = usage & 0xF000; + unsigned int usage_actual = usage & 0xFFFF; buf = resolv_usage_page(usage >> 16, f); if (IS_ERR(buf)) { @@ -529,16 +2904,33 @@ char *hid_resolv_usage(unsigned usage, struct seq_file *f) { } for (p = hid_usage_table; p->description; p++) if (p->page == (usage >> 16)) { + if (p->page == 0x20 && usage_modifier) { + for (m = p; m->description; m++) { + if (p->page == m->page && m->usage + == usage_modifier) { + modifier = m->description; + break; + } + } + if (modifier) + usage_actual = usage_actual & 0x0FFF; + } + + if (!modifier) + modifier = ""; + for(++p; p->description && p->usage != 0; p++) - if (p->usage == (usage & 0xffff)) { + if (p->usage == usage_actual) { if (!f) snprintf(buf + len, HID_DEBUG_BUFSIZE - len, - "%s", p->description); + "%s%s", p->description, + modifier); else seq_printf(f, - "%s", - p->description); + "%s%s", + p->description, + modifier); return buf; } break; @@ -753,12 +3145,12 @@ static const char *events[EV_MAX + 1] = { [EV_MSC] = "Misc", [EV_LED] = "LED", [EV_SND] = "Sound", [EV_REP] = "Repeat", [EV_FF] = "ForceFeedback", [EV_PWR] = "Power", - [EV_FF_STATUS] = "ForceFeedbackStatus", + [EV_FF_STATUS] = "ForceFeedbackStatus", [EV_SW] = "Software", }; -static const char *syncs[3] = { +static const char *syncs[SYN_CNT] = { [SYN_REPORT] = "Report", [SYN_CONFIG] = "Config", - [SYN_MT_REPORT] = "MT Report", + [SYN_MT_REPORT] = "MT Report", [SYN_DROPPED] = "Dropped", }; static const char *keys[KEY_MAX + 1] = { @@ -917,9 +3309,9 @@ static const char *keys[KEY_MAX + 1] = { [KEY_EPG] = "EPG", [KEY_PVR] = "PVR", [KEY_MHP] = "MHP", [KEY_LANGUAGE] = "Language", [KEY_TITLE] = "Title", [KEY_SUBTITLE] = "Subtitle", - [KEY_ANGLE] = "Angle", [KEY_ZOOM] = "Zoom", + [KEY_ANGLE] = "Angle", [KEY_MODE] = "Mode", [KEY_KEYBOARD] = "Keyboard", - [KEY_SCREEN] = "Screen", [KEY_PC] = "PC", + [KEY_PC] = "PC", [KEY_TV] = "TV", [KEY_TV2] = "TV2", [KEY_VCR] = "VCR", [KEY_VCR2] = "VCR2", [KEY_SAT] = "Sat", [KEY_SAT2] = "Sat2", @@ -974,6 +3366,8 @@ static const char *keys[KEY_MAX + 1] = { [KEY_CAMERA_ACCESS_ENABLE] = "CameraAccessEnable", [KEY_CAMERA_ACCESS_DISABLE] = "CameraAccessDisable", [KEY_CAMERA_ACCESS_TOGGLE] = "CameraAccessToggle", + [KEY_ACCESSIBILITY] = "Accessibility", + [KEY_DO_NOT_DISTURB] = "DoNotDisturb", [KEY_DICTATE] = "Dictate", [KEY_MICMUTE] = "MicrophoneMute", [KEY_BRIGHTNESS_MIN] = "BrightnessMin", @@ -995,6 +3389,102 @@ static const char *keys[KEY_MAX + 1] = { [KEY_MACRO22] = "Macro22", [KEY_MACRO23] = "Macro23", [KEY_MACRO24] = "Macro24", [KEY_MACRO25] = "Macro25", [KEY_MACRO26] = "Macro26", [KEY_MACRO27] = "Macro27", [KEY_MACRO28] = "Macro28", [KEY_MACRO29] = "Macro29", [KEY_MACRO30] = "Macro30", + [BTN_TRIGGER_HAPPY1] = "TriggerHappy1", [BTN_TRIGGER_HAPPY2] = "TriggerHappy2", + [BTN_TRIGGER_HAPPY3] = "TriggerHappy3", [BTN_TRIGGER_HAPPY4] = "TriggerHappy4", + [BTN_TRIGGER_HAPPY5] = "TriggerHappy5", [BTN_TRIGGER_HAPPY6] = "TriggerHappy6", + [BTN_TRIGGER_HAPPY7] = "TriggerHappy7", [BTN_TRIGGER_HAPPY8] = "TriggerHappy8", + [BTN_TRIGGER_HAPPY9] = "TriggerHappy9", [BTN_TRIGGER_HAPPY10] = "TriggerHappy10", + [BTN_TRIGGER_HAPPY11] = "TriggerHappy11", [BTN_TRIGGER_HAPPY12] = "TriggerHappy12", + [BTN_TRIGGER_HAPPY13] = "TriggerHappy13", [BTN_TRIGGER_HAPPY14] = "TriggerHappy14", + [BTN_TRIGGER_HAPPY15] = "TriggerHappy15", [BTN_TRIGGER_HAPPY16] = "TriggerHappy16", + [BTN_TRIGGER_HAPPY17] = "TriggerHappy17", [BTN_TRIGGER_HAPPY18] = "TriggerHappy18", + [BTN_TRIGGER_HAPPY19] = "TriggerHappy19", [BTN_TRIGGER_HAPPY20] = "TriggerHappy20", + [BTN_TRIGGER_HAPPY21] = "TriggerHappy21", [BTN_TRIGGER_HAPPY22] = "TriggerHappy22", + [BTN_TRIGGER_HAPPY23] = "TriggerHappy23", [BTN_TRIGGER_HAPPY24] = "TriggerHappy24", + [BTN_TRIGGER_HAPPY25] = "TriggerHappy25", [BTN_TRIGGER_HAPPY26] = "TriggerHappy26", + [BTN_TRIGGER_HAPPY27] = "TriggerHappy27", [BTN_TRIGGER_HAPPY28] = "TriggerHappy28", + [BTN_TRIGGER_HAPPY29] = "TriggerHappy29", [BTN_TRIGGER_HAPPY30] = "TriggerHappy30", + [BTN_TRIGGER_HAPPY31] = "TriggerHappy31", [BTN_TRIGGER_HAPPY32] = "TriggerHappy32", + [BTN_TRIGGER_HAPPY33] = "TriggerHappy33", [BTN_TRIGGER_HAPPY34] = "TriggerHappy34", + [BTN_TRIGGER_HAPPY35] = "TriggerHappy35", [BTN_TRIGGER_HAPPY36] = "TriggerHappy36", + [BTN_TRIGGER_HAPPY37] = "TriggerHappy37", [BTN_TRIGGER_HAPPY38] = "TriggerHappy38", + [BTN_TRIGGER_HAPPY39] = "TriggerHappy39", [BTN_TRIGGER_HAPPY40] = "TriggerHappy40", + [BTN_STYLUS3] = "Stylus3", [BTN_TOOL_QUINTTAP] = "ToolQuintTap", + [KEY_10CHANNELSDOWN] = "10ChannelsDown", + [KEY_10CHANNELSUP] = "10ChannelsUp", + [KEY_3D_MODE] = "3DMode", [KEY_ADDRESSBOOK] = "Addressbook", + [KEY_ALS_TOGGLE] = "ALSToggle", [KEY_ASPECT_RATIO] = "AspectRatio", + [KEY_ATTENDANT_OFF] = "AttendantOff", [KEY_ATTENDANT_ON] = "AttendantOn", + [KEY_ATTENDANT_TOGGLE] = "AttendantToggle", + [KEY_AUDIO_DESC] = "AudioDesc", + [KEY_AUTOPILOT_ENGAGE_TOGGLE] = "AutoPiloteEngage", + [KEY_BATTERY] = "Battery", [KEY_BLUETOOTH] = "BlueTooth", + [KEY_BRIGHTNESS_CYCLE] = "BrightnessCycle", + [KEY_BRIGHTNESS_MENU] = "BrightnessMenu", + [KEY_BRL_DOT1] = "BrlDot1", [KEY_BRL_DOT10] = "BrlDot10", + [KEY_BRL_DOT2] = "BrlDot2", [KEY_BRL_DOT3] = "BrlDot3", + [KEY_BRL_DOT4] = "BrlDot4", [KEY_BRL_DOT5] = "BrlDot5", + [KEY_BRL_DOT6] = "BrlDot6", [KEY_BRL_DOT7] = "BrlDot7", + [KEY_BRL_DOT8] = "BrlDot8", [KEY_BRL_DOT9] = "BrlDot9", + [KEY_CAMERA_DOWN] = "CameraDown", [KEY_CAMERA_FOCUS] = "CameraFocus", + [KEY_CAMERA_LEFT] = "CameraLeft", [KEY_CAMERA_RIGHT] = "CameraRight", + [KEY_CAMERA_UP] = "CameraUp", [KEY_CAMERA_ZOOMIN] = "CameraZoomIn", + [KEY_CAMERA_ZOOMOUT] = "CameraZoomOut", [KEY_CLEARVU_SONAR] = "ClearVUSonar", + [KEY_CONTEXT_MENU] = "ContextMenu", [KEY_DATA] = "Data", + [KEY_DATABASE] = "DataBase", [KEY_DISPLAY_OFF] = "DisplayOff", + [KEY_DISPLAYTOGGLE] = "DisplayToggle", [KEY_DOLLAR] = "Dollar", + [KEY_DUAL_RANGE_RADAR] = "DualRangeRadat", + [KEY_EDITOR] = "Editor", [KEY_EURO] = "Euro", + [KEY_FASTREVERSE] = "FastReverse", [KEY_FISHING_CHART] = "FishingChart", + [KEY_FN_RIGHT_SHIFT] = "FnRightShift", [KEY_FRAMEBACK] = "FrameBack", + [KEY_FRAMEFORWARD] = "FrameForward", [KEY_FULL_SCREEN] = "FullScreen", + [KEY_GAMES] = "Games", [KEY_GRAPHICSEDITOR] = "GraphicsEditor", + [KEY_HANGUP_PHONE] = "HangUpPhone", + [KEY_IMAGES] = "Images", [KEY_KBD_LCD_MENU1] = "KbdLcdMenu1", + [KEY_KBD_LCD_MENU2] = "KbdLcdMenu2", [KEY_KBD_LCD_MENU3] = "KbdLcdMenu3", + [KEY_KBD_LCD_MENU4] = "KbdLcdMenu4", [KEY_KBD_LCD_MENU5] = "KbdLcdMenu5", + [KEY_LEFT_DOWN] = "LeftDown", [KEY_LEFT_UP] = "LeftUp", + [KEY_LIGHTS_TOGGLE] = "LightToggle", [KEY_MACRO_PRESET1] = "MacroPreset1", + [KEY_MACRO_PRESET2] = "MacroPreset2", [KEY_MACRO_PRESET3] = "MacroPrest3", + [KEY_MACRO_PRESET_CYCLE] = "MacroPresetCycle", + [KEY_MACRO_RECORD_START] = "MacroRecordStart", + [KEY_MACRO_RECORD_STOP] = "MacroRecordStop", + [KEY_MARK_WAYPOINT] = "MarkWayPoint", [KEY_MEDIA_REPEAT] = "MediaRepeat", + [KEY_MEDIA_TOP_MENU] = "MediaTopMenu", [KEY_MESSENGER] = "Messanger", + [KEY_NAV_CHART] = "NavChar", [KEY_NAV_INFO] = "NavInfo", + [KEY_NEWS] = "News", [KEY_NEXT_ELEMENT] = "NextElement", + [KEY_NEXT_FAVORITE] = "NextFavorite", [KEY_NOTIFICATION_CENTER] = "NotificationCenter", + [KEY_NUMERIC_0] = "Numeric0", [KEY_NUMERIC_1] = "Numeric1", + [KEY_NUMERIC_11] = "Numceric11", [KEY_NUMERIC_12] = "Numeric12", + [KEY_NUMERIC_2] = "Numeric2", [KEY_NUMERIC_3] = "Numeric3", + [KEY_NUMERIC_4] = "Numeric4", [KEY_NUMERIC_5] = "Numeric5", + [KEY_NUMERIC_6] = "Numeric6", [KEY_NUMERIC_7] = "Numeric7", + [KEY_NUMERIC_8] = "Numeric8", [KEY_NUMERIC_9] = "Numeric9", + [KEY_NUMERIC_A] = "NumericA", [KEY_NUMERIC_B] = "NumericB", + [KEY_NUMERIC_C] = "NumericC", [KEY_NUMERIC_D] = "NumericD", + [KEY_NUMERIC_POUND] = "NumericPound", [KEY_NUMERIC_STAR] = "NumericStar", + [KEY_ONSCREEN_KEYBOARD] = "OnScreenKeyBoard", + [KEY_PAUSE_RECORD] = "PauseRecord", [KEY_PICKUP_PHONE] = "PickUpPhone", + [KEY_PRESENTATION] = "Presentation", [KEY_PREVIOUS_ELEMENT] = "PreviousElement", + [KEY_PRIVACY_SCREEN_TOGGLE] = "PrivacyScreenToggle", + [KEY_RADAR_OVERLAY] = "RadarOverLay", + [KEY_RFKILL] = "RFKill", [KEY_RIGHT_DOWN] = "RightDown", + [KEY_RIGHT_UP] = "RightUp", [KEY_ROOT_MENU] = "RootMenu", + [KEY_ROTATE_LOCK_TOGGLE] = "RotateLockToggle", + [KEY_SCALE] = "Scale", [KEY_SELECTIVE_SCREENSHOT] = "SelectiveScreenshot", + [KEY_SIDEVU_SONAR] = "SideVUSonar", [KEY_SINGLE_RANGE_RADAR] = "SingleRangeRadar", + [KEY_SLOWREVERSE] = "SlowReverse", [KEY_SOS] = "SOS", + [KEY_SPREADSHEET] = "SpreadSheet", [KEY_STOP_RECORD] = "StopRecord", + [KEY_TOUCHPAD_OFF] = "TouchPadOff", [KEY_TOUCHPAD_ON] = "TouchPadOn", + [KEY_TOUCHPAD_TOGGLE] = "TouchPadToggle", + [KEY_TRADITIONAL_SONAR] = "TraditionalSonar", + [KEY_UNMUTE] = "Unmute", [KEY_UWB] = "UWB", + [KEY_VIDEO_NEXT] = "VideoNext", [KEY_VIDEOPHONE] = "VideoPhone", + [KEY_VIDEO_PREV] = "VideoPrev", [KEY_VOD] = "VOD", + [KEY_VOICEMAIL] = "VoiceMail", [KEY_WLAN] = "WLAN", + [KEY_WORDPROCESSOR] = "WordProcessor", [KEY_WPS_BUTTON] = "WPSButton", + [KEY_WWAN] = "WWAN", [KEY_ZOOMIN] = "ZoomIn", + [KEY_ZOOMOUT] = "ZoomOut", [KEY_ZOOMRESET] = "ZoomReset", }; static const char *relatives[REL_MAX + 1] = { @@ -1003,6 +3493,8 @@ static const char *relatives[REL_MAX + 1] = { [REL_RY] = "Ry", [REL_RZ] = "Rz", [REL_HWHEEL] = "HWheel", [REL_DIAL] = "Dial", [REL_WHEEL] = "Wheel", [REL_MISC] = "Misc", + [REL_WHEEL_HI_RES] = "WheelHiRes", + [REL_HWHEEL_HI_RES] = "HWheelHiRes" }; static const char *absolutes[ABS_CNT] = { @@ -1020,6 +3512,7 @@ static const char *absolutes[ABS_CNT] = { [ABS_TILT_Y] = "YTilt", [ABS_TOOL_WIDTH] = "ToolWidth", [ABS_VOLUME] = "Volume", [ABS_PROFILE] = "Profile", [ABS_MISC] = "Misc", + [ABS_MT_SLOT] = "MTSlot", [ABS_MT_TOUCH_MAJOR] = "MTMajor", [ABS_MT_TOUCH_MINOR] = "MTMinor", [ABS_MT_WIDTH_MAJOR] = "MTMajorW", @@ -1029,11 +3522,17 @@ static const char *absolutes[ABS_CNT] = { [ABS_MT_POSITION_Y] = "MTPositionY", [ABS_MT_TOOL_TYPE] = "MTToolType", [ABS_MT_BLOB_ID] = "MTBlobID", + [ABS_MT_TRACKING_ID] = "MTTrackingID", + [ABS_MT_PRESSURE] = "MTPressure", + [ABS_MT_DISTANCE] = "MTDistance", + [ABS_MT_TOOL_X] = "MTToolX", + [ABS_MT_TOOL_Y] = "MTToolY", }; static const char *misc[MSC_MAX + 1] = { [MSC_SERIAL] = "Serial", [MSC_PULSELED] = "Pulseled", - [MSC_GESTURE] = "Gesture", [MSC_RAW] = "RawData" + [MSC_GESTURE] = "Gesture", [MSC_RAW] = "RawData", + [MSC_SCAN] = "Scan", [MSC_TIMESTAMP] = "TimeStamp", }; static const char *leds[LED_MAX + 1] = { @@ -1041,7 +3540,8 @@ static const char *leds[LED_MAX + 1] = { [LED_SCROLLL] = "ScrollLock", [LED_COMPOSE] = "Compose", [LED_KANA] = "Kana", [LED_SLEEP] = "Sleep", [LED_SUSPEND] = "Suspend", [LED_MUTE] = "Mute", - [LED_MISC] = "Misc", + [LED_MISC] = "Misc", [LED_MAIL] = "Mail", + [LED_CHARGING] = "Charging", }; static const char *repeats[REP_MAX + 1] = { @@ -1053,17 +3553,71 @@ static const char *sounds[SND_MAX + 1] = { [SND_TONE] = "Tone" }; +static const char *software[SW_CNT] = { + [SW_LID] = "Lid", + [SW_TABLET_MODE] = "TabletMode", + [SW_HEADPHONE_INSERT] = "HeadPhoneInsert", + [SW_RFKILL_ALL] = "RFKillAll", + [SW_MICROPHONE_INSERT] = "MicrophoneInsert", + [SW_DOCK] = "Dock", + [SW_LINEOUT_INSERT] = "LineOutInsert", + [SW_JACK_PHYSICAL_INSERT] = "JackPhysicalInsert", + [SW_VIDEOOUT_INSERT] = "VideoOutInsert", + [SW_CAMERA_LENS_COVER] = "CameraLensCover", + [SW_KEYPAD_SLIDE] = "KeyPadSlide", + [SW_FRONT_PROXIMITY] = "FrontProximity", + [SW_ROTATE_LOCK] = "RotateLock", + [SW_LINEIN_INSERT] = "LineInInsert", + [SW_MUTE_DEVICE] = "MuteDevice", + [SW_PEN_INSERTED] = "PenInserted", + [SW_MACHINE_COVER] = "MachineCover", +}; + +static const char *force[FF_CNT] = { + [FF_RUMBLE] = "FF_RUMBLE", + [FF_PERIODIC] = "FF_PERIODIC", + [FF_CONSTANT] = "FF_CONSTANT", + [FF_SPRING] = "FF_SPRING", + [FF_FRICTION] = "FF_FRICTION", + [FF_DAMPER] = "FF_DAMPER", + [FF_INERTIA] = "FF_INERTIA", + [FF_RAMP] = "FF_RAMP", + [FF_SQUARE] = "FF_SQUARE", + [FF_TRIANGLE] = "FF_TRIANGLE", + [FF_SINE] = "FF_SINE", + [FF_SAW_UP] = "FF_SAW_UP", + [FF_SAW_DOWN] = "FF_SAW_DOWN", + [FF_CUSTOM] = "FF_CUSTOM", + [FF_GAIN] = "FF_GAIN", + [FF_AUTOCENTER] = "FF_AUTOCENTER", + [FF_MAX] = "FF_MAX", +}; + +static const char *force_status[FF_STATUS_MAX + 1] = { + [FF_STATUS_STOPPED] = "FF_STATUS_STOPPED", + [FF_STATUS_PLAYING] = "FF_STATUS_PLAYING", +}; + static const char **names[EV_MAX + 1] = { [EV_SYN] = syncs, [EV_KEY] = keys, [EV_REL] = relatives, [EV_ABS] = absolutes, [EV_MSC] = misc, [EV_LED] = leds, [EV_SND] = sounds, [EV_REP] = repeats, + [EV_SW] = software, [EV_FF] = force, + [EV_FF_STATUS] = force_status, }; static void hid_resolv_event(__u8 type, __u16 code, struct seq_file *f) { - seq_printf(f, "%s.%s", events[type] ? events[type] : "?", - names[type] ? (names[type][code] ? names[type][code] : "?") : "?"); + if (events[type]) + seq_printf(f, "%s.", events[type]); + else + seq_printf(f, "%02x.", type); + + if (names[type] && names[type][code]) + seq_printf(f, "%s", names[type][code]); + else + seq_printf(f, "%04x", code); } static void hid_dump_input_mapping(struct hid_device *hid, struct seq_file *f) diff --git a/drivers/hid/hid-dr.c b/drivers/hid/hid-dr.c index 947f19f8685f..84e1e90a266b 100644 --- a/drivers/hid/hid-dr.c +++ b/drivers/hid/hid-dr.c @@ -199,7 +199,7 @@ static inline int drff_init(struct hid_device *hid) #define PID0011_RDESC_ORIG_SIZE 101 /* Fixed report descriptor for PID 0x011 joystick */ -static __u8 pid0011_rdesc_fixed[] = { +static const __u8 pid0011_rdesc_fixed[] = { 0x05, 0x01, /* Usage Page (Desktop), */ 0x09, 0x04, /* Usage (Joystick), */ 0xA1, 0x01, /* Collection (Application), */ @@ -228,14 +228,14 @@ static __u8 pid0011_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -static __u8 *dr_report_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) +static const __u8 *dr_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) { switch (hdev->product) { case 0x0011: if (*rsize == PID0011_RDESC_ORIG_SIZE) { - rdesc = pid0011_rdesc_fixed; *rsize = sizeof(pid0011_rdesc_fixed); + return pid0011_rdesc_fixed; } break; } @@ -316,4 +316,5 @@ static struct hid_driver dr_driver = { }; module_hid_driver(dr_driver); +MODULE_DESCRIPTION("Force feedback support for DragonRise Inc. game controllers"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-elecom.c b/drivers/hid/hid-elecom.c index 4fa45ee77503..defcf91fdd14 100644 --- a/drivers/hid/hid-elecom.c +++ b/drivers/hid/hid-elecom.c @@ -53,7 +53,7 @@ static void mouse_button_fixup(struct hid_device *hdev, rdesc[padding_bit + 1] = MOUSE_BUTTONS_MAX - nbuttons; } -static __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { switch (hdev->product) { @@ -136,4 +136,5 @@ static struct hid_driver elecom_driver = { }; module_hid_driver(elecom_driver); +MODULE_DESCRIPTION("HID driver for ELECOM devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-elo.c b/drivers/hid/hid-elo.c index 2876cb6a7dca..cf17bdd14d9c 100644 --- a/drivers/hid/hid-elo.c +++ b/drivers/hid/hid-elo.c @@ -313,4 +313,5 @@ static void __exit elo_driver_exit(void) module_exit(elo_driver_exit); MODULE_AUTHOR("Jiri Slaby <jslaby@suse.cz>"); +MODULE_DESCRIPTION("HID driver for ELO usb touchscreen 4000/4500"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-emsff.c b/drivers/hid/hid-emsff.c index c34f2e5a049f..60bfb6a924d7 100644 --- a/drivers/hid/hid-emsff.c +++ b/drivers/hid/hid-emsff.c @@ -144,5 +144,6 @@ static struct hid_driver ems_driver = { }; module_hid_driver(ems_driver); +MODULE_DESCRIPTION("Force feedback support for EMS Trio Linker Plus II"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-evision.c b/drivers/hid/hid-evision.c index ef6b4b435215..bb5997078491 100644 --- a/drivers/hid/hid-evision.c +++ b/drivers/hid/hid-evision.c @@ -50,4 +50,5 @@ static struct hid_driver evision_driver = { }; module_hid_driver(evision_driver); +MODULE_DESCRIPTION("HID driver for EVision devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-ezkey.c b/drivers/hid/hid-ezkey.c index d14f91d78c96..0e28bc0b87fa 100644 --- a/drivers/hid/hid-ezkey.c +++ b/drivers/hid/hid-ezkey.c @@ -75,4 +75,5 @@ static struct hid_driver ez_driver = { }; module_hid_driver(ez_driver); +MODULE_DESCRIPTION("HID driver for some ezkey \"special\" devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-gaff.c b/drivers/hid/hid-gaff.c index ecbd3995a4eb..c6db8b6cc8ee 100644 --- a/drivers/hid/hid-gaff.c +++ b/drivers/hid/hid-gaff.c @@ -169,4 +169,5 @@ static struct hid_driver ga_driver = { }; module_hid_driver(ga_driver); +MODULE_DESCRIPTION("Force feedback support for GreenAsia (Product ID 0x12) based devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-gembird.c b/drivers/hid/hid-gembird.c index c42593fe7116..20a8de766e56 100644 --- a/drivers/hid/hid-gembird.c +++ b/drivers/hid/hid-gembird.c @@ -57,7 +57,7 @@ static const __u8 gembird_jpd_fixed_rdesc[] = { 0x81, 0x02, /* Input (Data,Var,Abs) */ }; -static __u8 *gembird_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *gembird_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { __u8 *new_rdesc; diff --git a/drivers/hid/hid-generic.c b/drivers/hid/hid-generic.c index f9db991d3c5a..9e04c6d0fcc8 100644 --- a/drivers/hid/hid-generic.c +++ b/drivers/hid/hid-generic.c @@ -16,7 +16,7 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/kernel.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <asm/byteorder.h> #include <linux/hid.h> @@ -40,6 +40,9 @@ static bool hid_generic_match(struct hid_device *hdev, if (ignore_special_driver) return true; + if (hdev->quirks & HID_QUIRK_IGNORE_SPECIAL_DRIVER) + return true; + if (hdev->quirks & HID_QUIRK_HAVE_SPECIAL_DRIVER) return false; diff --git a/drivers/hid/hid-glorious.c b/drivers/hid/hid-glorious.c index 281b3a7187ce..5bbd81248053 100644 --- a/drivers/hid/hid-glorious.c +++ b/drivers/hid/hid-glorious.c @@ -26,7 +26,7 @@ MODULE_DESCRIPTION("HID driver for Glorious PC Gaming Race mice"); * keyboard HID report, causing keycodes to be misinterpreted. * Fix this by setting Usage Minimum to 0 in that report. */ -static __u8 *glorious_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *glorious_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { if (*rsize == 213 && diff --git a/drivers/hid/hid-goodix-spi.c b/drivers/hid/hid-goodix-spi.c new file mode 100644 index 000000000000..80c0288a3a38 --- /dev/null +++ b/drivers/hid/hid-goodix-spi.c @@ -0,0 +1,818 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Goodix GT7986U SPI Driver Code for HID. + * + * Copyright (C) 2024 Godix, Inc. + */ +#include <linux/unaligned.h> +#include <linux/delay.h> +#include <linux/hid.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/sizes.h> +#include <linux/spi/spi.h> + +#define GOODIX_DEV_CONFIRM_ADDR 0x10000 +#define GOODIX_HID_DESC_ADDR 0x1058C +#define GOODIX_HID_REPORT_DESC_ADDR 0x105AA +#define GOODIX_HID_SIGN_ADDR 0x10D32 +#define GOODIX_HID_CMD_ADDR 0x10364 +#define GOODIX_HID_REPORT_ADDR 0x22C8C + +#define GOODIX_HID_GET_REPORT_CMD 0x02 +#define GOODIX_HID_SET_REPORT_CMD 0x03 + +#define GOODIX_HID_MAX_INBUF_SIZE 128 +#define GOODIX_HID_ACK_READY_FLAG 0x01 +#define GOODIX_HID_REPORT_READY_FLAG 0x80 + +#define GOODIX_DEV_CONFIRM_VAL 0xAA + +#define GOODIX_SPI_WRITE_FLAG 0xF0 +#define GOODIX_SPI_READ_FLAG 0xF1 +#define GOODIX_SPI_TRANS_PREFIX_LEN 1 +#define GOODIX_REGISTER_WIDTH 4 +#define GOODIX_SPI_READ_DUMMY_LEN 3 +#define GOODIX_SPI_READ_PREFIX_LEN (GOODIX_SPI_TRANS_PREFIX_LEN + \ + GOODIX_REGISTER_WIDTH + \ + GOODIX_SPI_READ_DUMMY_LEN) +#define GOODIX_SPI_WRITE_PREFIX_LEN (GOODIX_SPI_TRANS_PREFIX_LEN + \ + GOODIX_REGISTER_WIDTH) + +#define GOODIX_CHECKSUM_SIZE sizeof(u16) +#define GOODIX_NORMAL_RESET_DELAY_MS 150 + +struct goodix_hid_report_header { + u8 flag; + __le16 size; +} __packed; +#define GOODIX_HID_ACK_HEADER_SIZE sizeof(struct goodix_hid_report_header) + +struct goodix_hid_report_package { + __le16 size; + u8 data[]; +}; + +#define GOODIX_HID_PKG_LEN_SIZE sizeof(u16) +#define GOODIX_HID_COOR_DATA_LEN 82 +#define GOODIX_HID_COOR_PKG_LEN (GOODIX_HID_PKG_LEN_SIZE + \ + GOODIX_HID_COOR_DATA_LEN) + +/* power state */ +#define GOODIX_SPI_POWER_ON 0x00 +#define GOODIX_SPI_POWER_SLEEP 0x01 + +/* flags used to record the current device operating state */ +#define GOODIX_HID_STARTED 0 + +struct goodix_hid_report_event { + struct goodix_hid_report_header hdr; + u8 data[GOODIX_HID_COOR_PKG_LEN]; +} __packed; + +struct goodix_hid_desc { + __le16 desc_length; + __le16 bcd_version; + __le16 report_desc_length; + __le16 report_desc_register; + __le16 input_register; + __le16 max_input_length; + __le16 output_register; + __le16 max_output_length; + __le16 cmd_register; + __le16 data_register; + __le16 vendor_id; + __le16 product_id; + __le16 version_id; + __le32 reserved; +} __packed; + +struct goodix_ts_data { + struct device *dev; + struct spi_device *spi; + struct hid_device *hid; + struct goodix_hid_desc hid_desc; + + struct gpio_desc *reset_gpio; + u32 hid_report_addr; + + unsigned long flags; + /* lock for hid raw request operation */ + struct mutex hid_request_lock; + /* buffer used to store hid report event */ + u8 *event_buf; + u32 hid_max_event_sz; + /* buffer used to do spi data transfer */ + u8 xfer_buf[SZ_2K] ____cacheline_aligned; +}; + +static void *goodix_get_event_report(struct goodix_ts_data *ts, u32 addr, + u8 *data, size_t len) +{ + struct spi_device *spi = to_spi_device(&ts->spi->dev); + struct spi_transfer xfers; + struct spi_message spi_msg; + int error; + + /* buffer format: 0xF1 + addr(4bytes) + dummy(3bytes) + data */ + data[0] = GOODIX_SPI_READ_FLAG; + put_unaligned_be32(addr, data + GOODIX_SPI_TRANS_PREFIX_LEN); + + spi_message_init(&spi_msg); + memset(&xfers, 0, sizeof(xfers)); + xfers.tx_buf = data; + xfers.rx_buf = data; + xfers.len = GOODIX_SPI_READ_PREFIX_LEN + len; + spi_message_add_tail(&xfers, &spi_msg); + + error = spi_sync(spi, &spi_msg); + if (error) { + dev_err(ts->dev, "spi transfer error: %d", error); + return NULL; + } + + return data + GOODIX_SPI_READ_PREFIX_LEN; +} + +static int goodix_spi_read(struct goodix_ts_data *ts, u32 addr, + void *data, size_t len) +{ + struct spi_device *spi = to_spi_device(&ts->spi->dev); + struct spi_transfer xfers; + struct spi_message spi_msg; + int error; + + if (GOODIX_SPI_READ_PREFIX_LEN + len > sizeof(ts->xfer_buf)) { + dev_err(ts->dev, "read data len exceed limit %zu", + sizeof(ts->xfer_buf) - GOODIX_SPI_READ_PREFIX_LEN); + return -EINVAL; + } + + /* buffer format: 0xF1 + addr(4bytes) + dummy(3bytes) + data */ + ts->xfer_buf[0] = GOODIX_SPI_READ_FLAG; + put_unaligned_be32(addr, ts->xfer_buf + GOODIX_SPI_TRANS_PREFIX_LEN); + + spi_message_init(&spi_msg); + memset(&xfers, 0, sizeof(xfers)); + xfers.tx_buf = ts->xfer_buf; + xfers.rx_buf = ts->xfer_buf; + xfers.len = GOODIX_SPI_READ_PREFIX_LEN + len; + spi_message_add_tail(&xfers, &spi_msg); + + error = spi_sync(spi, &spi_msg); + if (error) + dev_err(ts->dev, "spi transfer error: %d", error); + else + memcpy(data, ts->xfer_buf + GOODIX_SPI_READ_PREFIX_LEN, len); + + return error; +} + +static int goodix_spi_write(struct goodix_ts_data *ts, u32 addr, + const void *data, size_t len) +{ + struct spi_device *spi = to_spi_device(&ts->spi->dev); + struct spi_transfer xfers; + struct spi_message spi_msg; + int error; + + if (GOODIX_SPI_WRITE_PREFIX_LEN + len > sizeof(ts->xfer_buf)) { + dev_err(ts->dev, "write data len exceed limit %zu", + sizeof(ts->xfer_buf) - GOODIX_SPI_WRITE_PREFIX_LEN); + return -EINVAL; + } + + /* buffer format: 0xF0 + addr(4bytes) + data */ + ts->xfer_buf[0] = GOODIX_SPI_WRITE_FLAG; + put_unaligned_be32(addr, ts->xfer_buf + GOODIX_SPI_TRANS_PREFIX_LEN); + memcpy(ts->xfer_buf + GOODIX_SPI_WRITE_PREFIX_LEN, data, len); + + spi_message_init(&spi_msg); + memset(&xfers, 0, sizeof(xfers)); + xfers.tx_buf = ts->xfer_buf; + xfers.len = GOODIX_SPI_WRITE_PREFIX_LEN + len; + spi_message_add_tail(&xfers, &spi_msg); + + error = spi_sync(spi, &spi_msg); + if (error) + dev_err(ts->dev, "spi transfer error: %d", error); + + return error; +} + +static int goodix_dev_confirm(struct goodix_ts_data *ts) +{ + u8 tx_buf[8], rx_buf[8]; + int retry = 3; + int error; + + gpiod_set_value_cansleep(ts->reset_gpio, 0); + usleep_range(4000, 4100); + + memset(tx_buf, GOODIX_DEV_CONFIRM_VAL, sizeof(tx_buf)); + while (retry--) { + error = goodix_spi_write(ts, GOODIX_DEV_CONFIRM_ADDR, + tx_buf, sizeof(tx_buf)); + if (error) + return error; + + error = goodix_spi_read(ts, GOODIX_DEV_CONFIRM_ADDR, + rx_buf, sizeof(rx_buf)); + if (error) + return error; + + if (!memcmp(tx_buf, rx_buf, sizeof(tx_buf))) + return 0; + + usleep_range(5000, 5100); + } + + dev_err(ts->dev, "device confirm failed, rx_buf: %*ph", 8, rx_buf); + return -EINVAL; +} + +/** + * goodix_hid_parse() - hid-core .parse() callback + * @hid: hid device instance + * + * This function gets called during call to hid_add_device + * + * Return: 0 on success and non zero on error + */ +static int goodix_hid_parse(struct hid_device *hid) +{ + struct goodix_ts_data *ts = hid->driver_data; + u16 rsize; + int error; + + rsize = le16_to_cpu(ts->hid_desc.report_desc_length); + if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { + dev_err(ts->dev, "invalid report desc size, %d", rsize); + return -EINVAL; + } + + u8 *rdesc __free(kfree) = kzalloc(rsize, GFP_KERNEL); + if (!rdesc) + return -ENOMEM; + + error = goodix_spi_read(ts, GOODIX_HID_REPORT_DESC_ADDR, rdesc, rsize); + if (error) { + dev_err(ts->dev, "failed get report desc, %d", error); + return error; + } + + error = hid_parse_report(hid, rdesc, rsize); + if (error) { + dev_err(ts->dev, "failed parse report, %d", error); + return error; + } + + return 0; +} + +static int goodix_hid_get_report_length(struct hid_report *report) +{ + return ((report->size - 1) >> 3) + 1 + + report->device->report_enum[report->type].numbered + 2; +} + +static void goodix_hid_find_max_report(struct hid_device *hid, unsigned int type, + unsigned int *max) +{ + struct hid_report *report; + unsigned int size; + + list_for_each_entry(report, &hid->report_enum[type].report_list, list) { + size = goodix_hid_get_report_length(report); + if (*max < size) + *max = size; + } +} + +static int goodix_hid_start(struct hid_device *hid) +{ + struct goodix_ts_data *ts = hid->driver_data; + unsigned int bufsize = GOODIX_HID_COOR_PKG_LEN; + u32 report_size; + + goodix_hid_find_max_report(hid, HID_INPUT_REPORT, &bufsize); + goodix_hid_find_max_report(hid, HID_OUTPUT_REPORT, &bufsize); + goodix_hid_find_max_report(hid, HID_FEATURE_REPORT, &bufsize); + + report_size = GOODIX_SPI_READ_PREFIX_LEN + + GOODIX_HID_ACK_HEADER_SIZE + bufsize; + if (report_size <= ts->hid_max_event_sz) + return 0; + + ts->event_buf = devm_krealloc(ts->dev, ts->event_buf, + report_size, GFP_KERNEL); + if (!ts->event_buf) + return -ENOMEM; + + ts->hid_max_event_sz = report_size; + return 0; +} + +static void goodix_hid_stop(struct hid_device *hid) +{ + hid->claimed = 0; +} + +static int goodix_hid_open(struct hid_device *hid) +{ + struct goodix_ts_data *ts = hid->driver_data; + + set_bit(GOODIX_HID_STARTED, &ts->flags); + return 0; +} + +static void goodix_hid_close(struct hid_device *hid) +{ + struct goodix_ts_data *ts = hid->driver_data; + + clear_bit(GOODIX_HID_STARTED, &ts->flags); +} + +/* Return date length of response data */ +static int goodix_hid_check_ack_status(struct goodix_ts_data *ts, u32 *resp_len) +{ + struct goodix_hid_report_header hdr; + int retry = 20; + int error; + int len; + + while (retry--) { + /* + * 3 bytes of hid request response data + * - byte 0: Ack flag, value of 1 for data ready + * - bytes 1-2: Response data length + */ + error = goodix_spi_read(ts, GOODIX_HID_CMD_ADDR, + &hdr, sizeof(hdr)); + if (!error && (hdr.flag & GOODIX_HID_ACK_READY_FLAG)) { + len = le16_to_cpu(hdr.size); + if (len < GOODIX_HID_PKG_LEN_SIZE) { + dev_err(ts->dev, "hrd.size too short: %d", len); + return -EINVAL; + } + *resp_len = len - GOODIX_HID_PKG_LEN_SIZE; + return 0; + } + + /* Wait 10ms for another try */ + usleep_range(10000, 11000); + } + + return -EINVAL; +} + +/** + * goodix_hid_get_raw_report() - Process hidraw GET REPORT operation + * @hid: hid device instance + * @reportnum: Report ID + * @buf: Buffer for store the report date + * @len: Length fo report data + * @report_type: Report type + * + * The function for hid_ll_driver.get_raw_report to handle the HIDRAW ioctl + * get report request. The transmitted data follows the standard i2c-hid + * protocol with a specified header. + * + * Return: The length of the data in the buf on success, negative error code + */ +static int goodix_hid_get_raw_report(struct hid_device *hid, + unsigned char reportnum, + u8 *buf, size_t len, + unsigned char report_type) +{ + struct goodix_ts_data *ts = hid->driver_data; + u16 data_register = le16_to_cpu(ts->hid_desc.data_register); + u16 cmd_register = le16_to_cpu(ts->hid_desc.cmd_register); + u8 tmp_buf[GOODIX_HID_MAX_INBUF_SIZE]; + int tx_len = 0, args_len = 0; + u32 response_data_len; + u8 args[3]; + int error; + + if (report_type == HID_OUTPUT_REPORT) + return -EINVAL; + + if (reportnum == 3) { + /* Get win8 signature data */ + error = goodix_spi_read(ts, GOODIX_HID_SIGN_ADDR, buf, len); + if (error) { + dev_err(ts->dev, "failed get win8 sign: %d", error); + return -EINVAL; + } + return len; + } + + if (reportnum >= 0x0F) + args[args_len++] = reportnum; + + put_unaligned_le16(data_register, args + args_len); + args_len += sizeof(data_register); + + /* Clean 3 bytes of hid ack header data */ + memset(tmp_buf, 0, GOODIX_HID_ACK_HEADER_SIZE); + tx_len += GOODIX_HID_ACK_HEADER_SIZE; + + put_unaligned_le16(cmd_register, tmp_buf + tx_len); + tx_len += sizeof(cmd_register); + + tmp_buf[tx_len] = (report_type == HID_FEATURE_REPORT ? 0x03 : 0x01) << 4; + tmp_buf[tx_len] |= reportnum >= 0x0F ? 0x0F : reportnum; + tx_len++; + + tmp_buf[tx_len++] = GOODIX_HID_GET_REPORT_CMD; + + memcpy(tmp_buf + tx_len, args, args_len); + tx_len += args_len; + + /* Step1: write report request info */ + error = goodix_spi_write(ts, GOODIX_HID_CMD_ADDR, tmp_buf, tx_len); + if (error) { + dev_err(ts->dev, "failed send read feature cmd, %d", error); + return error; + } + + /* No need read response data */ + if (!len) + return 0; + + /* Step2: check response data status */ + error = goodix_hid_check_ack_status(ts, &response_data_len); + if (error) + return error; + + /* Empty reprot response */ + if (!response_data_len) + return 0; + len = min(len, response_data_len); + /* Step3: read response data(skip 2bytes of hid pkg length) */ + error = goodix_spi_read(ts, GOODIX_HID_CMD_ADDR + + GOODIX_HID_ACK_HEADER_SIZE + + GOODIX_HID_PKG_LEN_SIZE, buf, len); + if (error) { + dev_err(ts->dev, "failed read hid response data, %d", error); + return error; + } + + if (buf[0] != reportnum) { + dev_err(ts->dev, "incorrect report (%d vs %d expected)", + buf[0], reportnum); + return -EINVAL; + } + return len; +} + +/** + * goodix_hid_set_raw_report() - process hidraw SET REPORT operation + * @hid: HID device + * @reportnum: Report ID + * @buf: Buffer for communication + * @len: Length of data in the buffer + * @report_type: Report type + * + * The function for hid_ll_driver.get_raw_report to handle the HIDRAW ioctl + * set report request. The transmitted data follows the standard i2c-hid + * protocol with a specified header. + * + * Return: The length of the data sent, negative error code on failure + */ +static int goodix_hid_set_raw_report(struct hid_device *hid, + unsigned char reportnum, + __u8 *buf, size_t len, + unsigned char report_type) +{ + struct goodix_ts_data *ts = hid->driver_data; + u16 data_register = le16_to_cpu(ts->hid_desc.data_register); + u16 cmd_register = le16_to_cpu(ts->hid_desc.cmd_register); + int tx_len = 0, args_len = 0; + u8 tmp_buf[GOODIX_HID_MAX_INBUF_SIZE]; + u8 args[5]; + int error; + + if (reportnum >= 0x0F) { + args[args_len++] = reportnum; + reportnum = 0x0F; + } + + put_unaligned_le16(data_register, args + args_len); + args_len += sizeof(data_register); + + put_unaligned_le16(GOODIX_HID_PKG_LEN_SIZE + len, args + args_len); + args_len += GOODIX_HID_PKG_LEN_SIZE; + + /* Clean 3 bytes of hid ack header data */ + memset(tmp_buf, 0, GOODIX_HID_ACK_HEADER_SIZE); + tx_len += GOODIX_HID_ACK_HEADER_SIZE; + + put_unaligned_le16(cmd_register, tmp_buf + tx_len); + tx_len += sizeof(cmd_register); + + tmp_buf[tx_len++] = ((report_type == HID_FEATURE_REPORT ? 0x03 : 0x02) << 4) | reportnum; + tmp_buf[tx_len++] = GOODIX_HID_SET_REPORT_CMD; + + memcpy(tmp_buf + tx_len, args, args_len); + tx_len += args_len; + + memcpy(tmp_buf + tx_len, buf, len); + tx_len += len; + + error = goodix_spi_write(ts, GOODIX_HID_CMD_ADDR, tmp_buf, tx_len); + if (error) { + dev_err(ts->dev, "failed send report: %*ph", tx_len, tmp_buf); + return error; + } + return len; +} + +static int goodix_hid_raw_request(struct hid_device *hid, + unsigned char reportnum, + __u8 *buf, size_t len, + unsigned char rtype, int reqtype) +{ + struct goodix_ts_data *ts = hid->driver_data; + int error = -EINVAL; + + guard(mutex)(&ts->hid_request_lock); + switch (reqtype) { + case HID_REQ_GET_REPORT: + error = goodix_hid_get_raw_report(hid, reportnum, buf, + len, rtype); + break; + case HID_REQ_SET_REPORT: + if (buf[0] == reportnum) + error = goodix_hid_set_raw_report(hid, reportnum, + buf, len, rtype); + break; + default: + break; + } + + return error; +} + +static struct hid_ll_driver goodix_hid_ll_driver = { + .parse = goodix_hid_parse, + .start = goodix_hid_start, + .stop = goodix_hid_stop, + .open = goodix_hid_open, + .close = goodix_hid_close, + .raw_request = goodix_hid_raw_request +}; + +static irqreturn_t goodix_hid_irq(int irq, void *data) +{ + struct goodix_ts_data *ts = data; + struct goodix_hid_report_event *event; + struct goodix_hid_report_package *pkg; + u16 report_size; + + if (!test_bit(GOODIX_HID_STARTED, &ts->flags)) + return IRQ_HANDLED; + /* + * First, read buffer with space for header and coordinate package: + * - event header = 3 bytes + * - coordinate event = GOODIX_HID_COOR_PKG_LEN bytes + * + * If the data size info in the event header exceeds + * GOODIX_HID_COOR_PKG_LEN, it means that there are other packages + * besides the coordinate package. + */ + event = goodix_get_event_report(ts, ts->hid_report_addr, ts->event_buf, + GOODIX_HID_ACK_HEADER_SIZE + + GOODIX_HID_COOR_PKG_LEN); + if (!event) { + dev_err(ts->dev, "failed get coordinate data"); + return IRQ_HANDLED; + } + + /* Check coordinate data valid falg */ + if (event->hdr.flag != GOODIX_HID_REPORT_READY_FLAG) + return IRQ_HANDLED; + + pkg = (struct goodix_hid_report_package *)event->data; + if (le16_to_cpu(pkg->size) < GOODIX_HID_PKG_LEN_SIZE) { + dev_err(ts->dev, "invalid coordinate event package size, %d", + le16_to_cpu(pkg->size)); + return IRQ_HANDLED; + } + hid_input_report(ts->hid, HID_INPUT_REPORT, pkg->data, + le16_to_cpu(pkg->size) - GOODIX_HID_PKG_LEN_SIZE, 1); + + report_size = le16_to_cpu(event->hdr.size); + /* Check if there are other packages */ + if (report_size <= GOODIX_HID_COOR_PKG_LEN) + return IRQ_HANDLED; + + if (report_size >= ts->hid_max_event_sz) { + dev_err(ts->dev, "package size exceed limit %d vs %d", + report_size, ts->hid_max_event_sz); + return IRQ_HANDLED; + } + + /* Read the package behind the coordinate data */ + pkg = goodix_get_event_report(ts, ts->hid_report_addr + sizeof(*event), + ts->event_buf, + report_size - GOODIX_HID_COOR_PKG_LEN); + if (!pkg) { + dev_err(ts->dev, "failed read attachment data content"); + return IRQ_HANDLED; + } + + hid_input_report(ts->hid, HID_INPUT_REPORT, pkg->data, + le16_to_cpu(pkg->size) - GOODIX_HID_PKG_LEN_SIZE, 1); + + return IRQ_HANDLED; +} + +static int goodix_hid_init(struct goodix_ts_data *ts) +{ + struct hid_device *hid; + int error; + + /* Get hid descriptor */ + error = goodix_spi_read(ts, GOODIX_HID_DESC_ADDR, &ts->hid_desc, + sizeof(ts->hid_desc)); + if (error) { + dev_err(ts->dev, "failed get hid desc, %d", error); + return error; + } + + hid = hid_allocate_device(); + if (IS_ERR(hid)) + return PTR_ERR(hid); + + hid->driver_data = ts; + hid->ll_driver = &goodix_hid_ll_driver; + hid->bus = BUS_SPI; + hid->dev.parent = &ts->spi->dev; + + hid->version = le16_to_cpu(ts->hid_desc.bcd_version); + hid->vendor = le16_to_cpu(ts->hid_desc.vendor_id); + hid->product = le16_to_cpu(ts->hid_desc.product_id); + snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "hid-gdix", + hid->vendor, hid->product); + + error = hid_add_device(hid); + if (error) { + dev_err(ts->dev, "failed add hid device, %d", error); + hid_destroy_device(hid); + return error; + } + + ts->hid = hid; + return 0; +} + +static int goodix_spi_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct goodix_ts_data *ts; + int error; + + /* init spi_device */ + spi->mode = SPI_MODE_0; + spi->bits_per_word = 8; + error = spi_setup(spi); + if (error) + return error; + + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + mutex_init(&ts->hid_request_lock); + spi_set_drvdata(spi, ts); + ts->spi = spi; + ts->dev = dev; + ts->hid_max_event_sz = GOODIX_SPI_READ_PREFIX_LEN + + GOODIX_HID_ACK_HEADER_SIZE + GOODIX_HID_COOR_PKG_LEN; + ts->event_buf = devm_kmalloc(dev, ts->hid_max_event_sz, GFP_KERNEL); + if (!ts->event_buf) + return -ENOMEM; + + ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(ts->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(ts->reset_gpio), + "failed to request reset gpio\n"); + + ts->hid_report_addr = GOODIX_HID_REPORT_ADDR; + error = goodix_dev_confirm(ts); + if (error) + return error; + + /* Waits 150ms for firmware to fully boot */ + msleep(GOODIX_NORMAL_RESET_DELAY_MS); + + error = goodix_hid_init(ts); + if (error) { + dev_err(dev, "failed init hid device"); + return error; + } + + error = devm_request_threaded_irq(&ts->spi->dev, ts->spi->irq, + NULL, goodix_hid_irq, IRQF_ONESHOT, + "goodix_spi_hid", ts); + if (error) { + dev_err(ts->dev, "could not register interrupt, irq = %d, %d", + ts->spi->irq, error); + goto err_destroy_hid; + } + + return 0; + +err_destroy_hid: + hid_destroy_device(ts->hid); + return error; +} + +static void goodix_spi_remove(struct spi_device *spi) +{ + struct goodix_ts_data *ts = spi_get_drvdata(spi); + + disable_irq(spi->irq); + hid_destroy_device(ts->hid); +} + +static int goodix_spi_set_power(struct goodix_ts_data *ts, int power_state) +{ + u8 power_control_cmd[] = {0x00, 0x00, 0x00, 0x87, 0x02, 0x00, 0x08}; + int error; + + /* value 0 for power on, 1 for power sleep */ + power_control_cmd[5] = power_state; + + guard(mutex)(&ts->hid_request_lock); + error = goodix_spi_write(ts, GOODIX_HID_CMD_ADDR, power_control_cmd, + sizeof(power_control_cmd)); + if (error) { + dev_err(ts->dev, "failed set power mode: %s", + power_state == GOODIX_SPI_POWER_ON ? "on" : "sleep"); + return error; + } + return 0; +} + +static int goodix_spi_suspend(struct device *dev) +{ + struct goodix_ts_data *ts = dev_get_drvdata(dev); + + disable_irq(ts->spi->irq); + return goodix_spi_set_power(ts, GOODIX_SPI_POWER_SLEEP); +} + +static int goodix_spi_resume(struct device *dev) +{ + struct goodix_ts_data *ts = dev_get_drvdata(dev); + + enable_irq(ts->spi->irq); + return goodix_spi_set_power(ts, GOODIX_SPI_POWER_ON); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(goodix_spi_pm_ops, + goodix_spi_suspend, goodix_spi_resume); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id goodix_spi_acpi_match[] = { + { "GXTS7986" }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, goodix_spi_acpi_match); +#endif + +#ifdef CONFIG_OF +static const struct of_device_id goodix_spi_of_match[] = { + { .compatible = "goodix,gt7986u-spifw", }, + { } +}; +MODULE_DEVICE_TABLE(of, goodix_spi_of_match); +#endif + +static const struct spi_device_id goodix_spi_ids[] = { + { "gt7986u" }, + { }, +}; +MODULE_DEVICE_TABLE(spi, goodix_spi_ids); + +static struct spi_driver goodix_spi_driver = { + .driver = { + .name = "goodix-spi-hid", + .acpi_match_table = ACPI_PTR(goodix_spi_acpi_match), + .of_match_table = of_match_ptr(goodix_spi_of_match), + .pm = pm_sleep_ptr(&goodix_spi_pm_ops), + }, + .probe = goodix_spi_probe, + .remove = goodix_spi_remove, + .id_table = goodix_spi_ids, +}; +module_spi_driver(goodix_spi_driver); + +MODULE_DESCRIPTION("Goodix SPI driver for HID touchscreen"); +MODULE_AUTHOR("Goodix, Inc."); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-google-hammer.c b/drivers/hid/hid-google-hammer.c index c6bdb9c4ef3e..0f292b5d3e26 100644 --- a/drivers/hid/hid-google-hammer.c +++ b/drivers/hid/hid-google-hammer.c @@ -23,7 +23,7 @@ #include <linux/platform_data/cros_ec_proto.h> #include <linux/platform_device.h> #include <linux/pm_wakeup.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "hid-ids.h" #include "hid-vivaldi-common.h" @@ -255,7 +255,7 @@ out: return retval; } -static int cbas_ec_remove(struct platform_device *pdev) +static void cbas_ec_remove(struct platform_device *pdev) { struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); @@ -266,7 +266,6 @@ static int cbas_ec_remove(struct platform_device *pdev) cbas_ec_set_input(NULL); mutex_unlock(&cbas_ec_reglock); - return 0; } static const struct acpi_device_id cbas_ec_acpi_ids[] = { @@ -419,38 +418,15 @@ static int hammer_event(struct hid_device *hid, struct hid_field *field, return 0; } -static bool hammer_has_usage(struct hid_device *hdev, unsigned int report_type, - unsigned application, unsigned usage) -{ - struct hid_report_enum *re = &hdev->report_enum[report_type]; - struct hid_report *report; - int i, j; - - list_for_each_entry(report, &re->report_list, list) { - if (report->application != application) - continue; - - for (i = 0; i < report->maxfield; i++) { - struct hid_field *field = report->field[i]; - - for (j = 0; j < field->maxusage; j++) - if (field->usage[j].hid == usage) - return true; - } - } - - return false; -} - static bool hammer_has_folded_event(struct hid_device *hdev) { - return hammer_has_usage(hdev, HID_INPUT_REPORT, + return !!hid_find_field(hdev, HID_INPUT_REPORT, HID_GD_KEYBOARD, HID_USAGE_KBD_FOLDED); } static bool hammer_has_backlight_control(struct hid_device *hdev) { - return hammer_has_usage(hdev, HID_OUTPUT_REPORT, + return !!hid_find_field(hdev, HID_OUTPUT_REPORT, HID_GD_KEYBOARD, HID_AD_BRIGHTNESS); } @@ -642,4 +618,5 @@ static void __exit hammer_exit(void) } module_exit(hammer_exit); +MODULE_DESCRIPTION("HID driver for Google Hammer device."); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-google-stadiaff.c b/drivers/hid/hid-google-stadiaff.c index 3731575562ab..6b38d2421d3d 100644 --- a/drivers/hid/hid-google-stadiaff.c +++ b/drivers/hid/hid-google-stadiaff.c @@ -155,4 +155,5 @@ static struct hid_driver stadia_driver = { }; module_hid_driver(stadia_driver); +MODULE_DESCRIPTION("Google Stadia controller rumble support."); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-gyration.c b/drivers/hid/hid-gyration.c index b99a611479b3..6606b57abe83 100644 --- a/drivers/hid/hid-gyration.c +++ b/drivers/hid/hid-gyration.c @@ -87,4 +87,5 @@ static struct hid_driver gyration_driver = { }; module_hid_driver(gyration_driver); +MODULE_DESCRIPTION("HID driver for some gyration \"special\" devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-holtek-kbd.c b/drivers/hid/hid-holtek-kbd.c index b346d68a06f5..d13543517a6c 100644 --- a/drivers/hid/hid-holtek-kbd.c +++ b/drivers/hid/hid-holtek-kbd.c @@ -27,7 +27,7 @@ * to the boot interface. */ -static __u8 holtek_kbd_rdesc_fixed[] = { +static const __u8 holtek_kbd_rdesc_fixed[] = { /* Original report descriptor, with reduced number of consumer usages */ 0x05, 0x01, /* Usage Page (Desktop), */ 0x09, 0x80, /* Usage (Sys Control), */ @@ -102,14 +102,14 @@ static __u8 holtek_kbd_rdesc_fixed[] = { 0xC0, /* End Collection */ }; -static __u8 *holtek_kbd_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *holtek_kbd_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { struct usb_interface *intf = to_usb_interface(hdev->dev.parent); if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { - rdesc = holtek_kbd_rdesc_fixed; *rsize = sizeof(holtek_kbd_rdesc_fixed); + return holtek_kbd_rdesc_fixed; } return rdesc; } @@ -180,4 +180,5 @@ static struct hid_driver holtek_kbd_driver = { }; module_hid_driver(holtek_kbd_driver); +MODULE_DESCRIPTION("HID driver for Holtek keyboard"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-holtek-mouse.c b/drivers/hid/hid-holtek-mouse.c index 7c907939bfae..b618a1646c13 100644 --- a/drivers/hid/hid-holtek-mouse.c +++ b/drivers/hid/hid-holtek-mouse.c @@ -29,8 +29,8 @@ * - USB ID 04d9:a0c2, sold as ETEKCITY Scroll T-140 Gaming Mouse */ -static __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) +static const __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, + __u8 *rdesc, unsigned int *rsize) { struct usb_interface *intf = to_usb_interface(hdev->dev.parent); @@ -110,4 +110,5 @@ static struct hid_driver holtek_mouse_driver = { }; module_hid_driver(holtek_mouse_driver); +MODULE_DESCRIPTION("HID driver for Holtek gaming mice"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c index f33485d83d24..0fb210e40a41 100644 --- a/drivers/hid/hid-hyperv.c +++ b/drivers/hid/hid-hyperv.c @@ -422,6 +422,25 @@ static int mousevsc_hid_raw_request(struct hid_device *hid, return 0; } +static int mousevsc_hid_probe(struct hid_device *hid_dev, const struct hid_device_id *id) +{ + int ret; + + ret = hid_parse(hid_dev); + if (ret) { + hid_err(hid_dev, "parse failed\n"); + return ret; + } + + ret = hid_hw_start(hid_dev, HID_CONNECT_HIDINPUT | HID_CONNECT_HIDDEV); + if (ret) { + hid_err(hid_dev, "hw start failed\n"); + return ret; + } + + return 0; +} + static const struct hid_ll_driver mousevsc_ll_driver = { .parse = mousevsc_hid_parse, .open = mousevsc_hid_open, @@ -431,7 +450,16 @@ static const struct hid_ll_driver mousevsc_ll_driver = { .raw_request = mousevsc_hid_raw_request, }; -static struct hid_driver mousevsc_hid_driver; +static const struct hid_device_id mousevsc_devices[] = { + { HID_DEVICE(BUS_VIRTUAL, HID_GROUP_ANY, 0x045E, 0x0621) }, + { } +}; + +static struct hid_driver mousevsc_hid_driver = { + .name = "hid-hyperv", + .id_table = mousevsc_devices, + .probe = mousevsc_hid_probe, +}; static int mousevsc_probe(struct hv_device *device, const struct hv_vmbus_device_id *dev_id) @@ -473,7 +501,6 @@ static int mousevsc_probe(struct hv_device *device, } hid_dev->ll_driver = &mousevsc_ll_driver; - hid_dev->driver = &mousevsc_hid_driver; hid_dev->bus = BUS_VIRTUAL; hid_dev->vendor = input_dev->hid_dev_info.vendor; hid_dev->product = input_dev->hid_dev_info.product; @@ -488,20 +515,6 @@ static int mousevsc_probe(struct hv_device *device, if (ret) goto probe_err2; - - ret = hid_parse(hid_dev); - if (ret) { - hid_err(hid_dev, "parse failed\n"); - goto probe_err2; - } - - ret = hid_hw_start(hid_dev, HID_CONNECT_HIDINPUT | HID_CONNECT_HIDDEV); - - if (ret) { - hid_err(hid_dev, "hw start failed\n"); - goto probe_err2; - } - device_init_wakeup(&device->device, true); input_dev->connected = true; @@ -579,12 +592,23 @@ static struct hv_driver mousevsc_drv = { static int __init mousevsc_init(void) { - return vmbus_driver_register(&mousevsc_drv); + int ret; + + ret = hid_register_driver(&mousevsc_hid_driver); + if (ret) + return ret; + + ret = vmbus_driver_register(&mousevsc_drv); + if (ret) + hid_unregister_driver(&mousevsc_hid_driver); + + return ret; } static void __exit mousevsc_exit(void) { vmbus_driver_unregister(&mousevsc_drv); + hid_unregister_driver(&mousevsc_hid_driver); } MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 8376fb5e2d0b..7e400624908e 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -94,6 +94,7 @@ #define USB_DEVICE_ID_APPLE_MAGICMOUSE2 0x0269 #define USB_DEVICE_ID_APPLE_MAGICTRACKPAD 0x030e #define USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 0x0265 +#define USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC 0x0324 #define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI 0x020e #define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f #define USB_DEVICE_ID_APPLE_GEYSER_ANSI 0x0214 @@ -183,6 +184,7 @@ #define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242 #define USB_DEVICE_ID_APPLE_IRCONTROL5 0x8243 #define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2021 0x029c +#define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_2024 0x0320 #define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_FINGERPRINT_2021 0x029a #define USB_DEVICE_ID_APPLE_MAGIC_KEYBOARD_NUMPAD_2021 0x029f #define USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT 0x8102 @@ -208,6 +210,9 @@ #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_Z13_LIGHTBAR 0x18c6 +#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY 0x1abe +#define USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY_X 0x1b4c #define USB_DEVICE_ID_ASUSTEK_ROG_CLAYMORE_II_KEYBOARD 0x196b #define USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD 0x1869 @@ -414,22 +419,8 @@ #define USB_DEVICE_ID_TOSHIBA_CLICK_L9W 0x0401 #define USB_DEVICE_ID_HP_X2 0x074d #define USB_DEVICE_ID_HP_X2_10_COVER 0x0755 -#define I2C_DEVICE_ID_HP_ENVY_X360_15 0x2d05 -#define I2C_DEVICE_ID_HP_ENVY_X360_15T_DR100 0x29CF -#define I2C_DEVICE_ID_HP_ENVY_X360_EU0009NV 0x2CF9 -#define I2C_DEVICE_ID_HP_SPECTRE_X360_15 0x2817 -#define I2C_DEVICE_ID_HP_SPECTRE_X360_13_AW0020NG 0x29DF -#define I2C_DEVICE_ID_ASUS_TP420IA_TOUCHSCREEN 0x2BC8 -#define I2C_DEVICE_ID_ASUS_GV301RA_TOUCHSCREEN 0x2C82 #define USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN 0x2544 #define USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN 0x2706 -#define I2C_DEVICE_ID_SURFACE_GO_TOUCHSCREEN 0x261A -#define I2C_DEVICE_ID_SURFACE_GO2_TOUCHSCREEN 0x2A1C -#define I2C_DEVICE_ID_LENOVO_YOGA_C630_TOUCHSCREEN 0x279F -#define I2C_DEVICE_ID_HP_SPECTRE_X360_13T_AW100 0x29F5 -#define I2C_DEVICE_ID_HP_SPECTRE_X360_14T_EA100_V1 0x2BED -#define I2C_DEVICE_ID_HP_SPECTRE_X360_14T_EA100_V2 0x2BEE -#define I2C_DEVICE_ID_HP_ENVY_X360_15_EU0556NG 0x2D02 #define I2C_DEVICE_ID_CHROMEBOOK_TROGDOR_POMPOM 0x2F81 #define USB_VENDOR_ID_ELECOM 0x056e @@ -516,7 +507,10 @@ #define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PIT_E100 0xe100 #define I2C_VENDOR_ID_GOODIX 0x27c6 +#define I2C_DEVICE_ID_GOODIX_01E8 0x01e8 +#define I2C_DEVICE_ID_GOODIX_01E9 0x01e9 #define I2C_DEVICE_ID_GOODIX_01F0 0x01f0 +#define I2C_DEVICE_ID_GOODIX_0D42 0x0d42 #define USB_VENDOR_ID_GOODTOUCH 0x1aad #define USB_DEVICE_ID_GOODTOUCH_000f 0x000f @@ -742,6 +736,10 @@ #define USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2 0x501A #define USB_DEVICE_ID_KYE_PENSKETCH_T609A 0x501B +#define USB_VENDOR_ID_KYSONA 0x3554 +#define USB_DEVICE_ID_KYSONA_M600_DONGLE 0xF57C +#define USB_DEVICE_ID_KYSONA_M600_WIRED 0xF57D + #define USB_VENDOR_ID_LABTEC 0x1020 #define USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD 0x0006 #define USB_DEVICE_ID_LABTEC_ODDOR_HANDBRAKE 0x8888 @@ -803,6 +801,7 @@ #define USB_DEVICE_ID_LENOVO_X1_TAB 0x60a3 #define USB_DEVICE_ID_LENOVO_X1_TAB3 0x60b5 #define USB_DEVICE_ID_LENOVO_X12_TAB 0x60fe +#define USB_DEVICE_ID_LENOVO_X12_TAB2 0x61ae #define USB_DEVICE_ID_LENOVO_OPTICAL_USB_MOUSE_600E 0x600e #define USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_608D 0x608d #define USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_6019 0x6019 @@ -823,6 +822,7 @@ #define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e #define USB_DEVICE_ID_LOGITECH_T651 0xb00c #define USB_DEVICE_ID_LOGITECH_DINOVO_EDGE_KBD 0xb309 +#define USB_DEVICE_ID_LOGITECH_CASA_TOUCHPAD 0xbb00 #define USB_DEVICE_ID_LOGITECH_C007 0xc007 #define USB_DEVICE_ID_LOGITECH_C077 0xc077 #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 @@ -874,6 +874,7 @@ #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1 0xc539 #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_1 0xc53f #define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_POWERPLAY 0xc53a +#define USB_DEVICE_ID_LOGITECH_BOLT_RECEIVER 0xc548 #define USB_DEVICE_ID_SPACETRAVELLER 0xc623 #define USB_DEVICE_ID_SPACENAVIGATOR 0xc626 #define USB_DEVICE_ID_DINOVO_DESKTOP 0xc704 @@ -1042,6 +1043,8 @@ #define USB_DEVICE_ID_PLANTRONICS_BLACKWIRE_3220_SERIES 0xc056 #define USB_DEVICE_ID_PLANTRONICS_BLACKWIRE_3215_SERIES 0xc057 #define USB_DEVICE_ID_PLANTRONICS_BLACKWIRE_3225_SERIES 0xc058 +#define USB_DEVICE_ID_PLANTRONICS_BLACKWIRE_3325_SERIES 0x430c +#define USB_DEVICE_ID_PLANTRONICS_ENCOREPRO_500_SERIES 0x431e #define USB_VENDOR_ID_PANASONIC 0x04da #define USB_DEVICE_ID_PANABOARD_UBT780 0x1044 @@ -1086,11 +1089,14 @@ #define USB_VENDOR_ID_PRODIGE 0x05af #define USB_DEVICE_ID_PRODIGE_CORDLESS 0x3062 +#define I2C_VENDOR_ID_QTEC 0x6243 + #define USB_VENDOR_ID_QUANTA 0x0408 #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH 0x3000 #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001 0x3001 #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3003 0x3003 #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008 0x3008 +#define USB_DEVICE_ID_QUANTA_HP_5MP_CAMERA_5473 0x5473 #define I2C_VENDOR_ID_RAYDIUM 0x2386 #define I2C_PRODUCT_ID_RAYDIUM_4B33 0x4b33 @@ -1297,6 +1303,7 @@ #define USB_VENDOR_ID_TOPRE 0x0853 #define USB_DEVICE_ID_TOPRE_REALFORCE_R2_108 0x0148 #define USB_DEVICE_ID_TOPRE_REALFORCE_R2_87 0x0146 +#define USB_DEVICE_ID_TOPRE_REALFORCE_R3S_87 0x0313 #define USB_VENDOR_ID_TOPSEED 0x0766 #define USB_DEVICE_ID_TOPSEED_CYBERLINK 0x0204 diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index e03d300d2bac..9d80635a91eb 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -373,10 +373,6 @@ static const struct hid_device_id hid_battery_quirks[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DINOVO_EDGE_KBD), HID_BATTERY_QUIRK_IGNORE }, - { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_ASUS_TP420IA_TOUCHSCREEN), - HID_BATTERY_QUIRK_IGNORE }, - { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_ASUS_GV301RA_TOUCHSCREEN), - HID_BATTERY_QUIRK_IGNORE }, { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN), HID_BATTERY_QUIRK_IGNORE }, { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN), @@ -387,32 +383,13 @@ static const struct hid_device_id hid_battery_quirks[] = { HID_BATTERY_QUIRK_AVOID_QUERY }, { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_SW), HID_BATTERY_QUIRK_AVOID_QUERY }, - { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_ENVY_X360_15), - HID_BATTERY_QUIRK_IGNORE }, - { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_ENVY_X360_15T_DR100), - HID_BATTERY_QUIRK_IGNORE }, - { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_ENVY_X360_EU0009NV), - HID_BATTERY_QUIRK_IGNORE }, - { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_SPECTRE_X360_15), - HID_BATTERY_QUIRK_IGNORE }, - { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_SPECTRE_X360_13_AW0020NG), - HID_BATTERY_QUIRK_IGNORE }, - { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_SURFACE_GO_TOUCHSCREEN), - HID_BATTERY_QUIRK_IGNORE }, - { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_SURFACE_GO2_TOUCHSCREEN), - HID_BATTERY_QUIRK_IGNORE }, - { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_LENOVO_YOGA_C630_TOUCHSCREEN), - HID_BATTERY_QUIRK_IGNORE }, - { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_SPECTRE_X360_13T_AW100), - HID_BATTERY_QUIRK_IGNORE }, - { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_SPECTRE_X360_14T_EA100_V1), - HID_BATTERY_QUIRK_IGNORE }, - { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_SPECTRE_X360_14T_EA100_V2), - HID_BATTERY_QUIRK_IGNORE }, - { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_ENVY_X360_15_EU0556NG), - HID_BATTERY_QUIRK_IGNORE }, { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_CHROMEBOOK_TROGDOR_POMPOM), HID_BATTERY_QUIRK_AVOID_QUERY }, + /* + * Elan I2C-HID touchscreens seem to all report a non present battery, + * set HID_BATTERY_QUIRK_IGNORE for all Elan I2C-HID devices. + */ + { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, HID_ANY_ID), HID_BATTERY_QUIRK_IGNORE }, {} }; @@ -833,9 +810,31 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel break; } + if ((usage->hid & 0xf0) == 0x90) { /* SystemControl & D-pad */ + switch (usage->hid) { + case HID_GD_UP: usage->hat_dir = 1; break; + case HID_GD_DOWN: usage->hat_dir = 5; break; + case HID_GD_RIGHT: usage->hat_dir = 3; break; + case HID_GD_LEFT: usage->hat_dir = 7; break; + case HID_GD_DO_NOT_DISTURB: + map_key_clear(KEY_DO_NOT_DISTURB); break; + default: goto unknown; + } + + if (usage->hid <= HID_GD_LEFT) { + if (field->dpad) { + map_abs(field->dpad); + goto ignore; + } + map_abs(ABS_HAT0X); + } + break; + } + if ((usage->hid & 0xf0) == 0xa0) { /* SystemControl */ switch (usage->hid & 0xf) { case 0x9: map_key_clear(KEY_MICMUTE); break; + case 0xa: map_key_clear(KEY_ACCESSIBILITY); break; default: goto ignore; } break; @@ -858,22 +857,6 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel if (field->application == HID_GD_SYSTEM_CONTROL) goto ignore; - if ((usage->hid & 0xf0) == 0x90) { /* D-pad */ - switch (usage->hid) { - case HID_GD_UP: usage->hat_dir = 1; break; - case HID_GD_DOWN: usage->hat_dir = 5; break; - case HID_GD_RIGHT: usage->hat_dir = 3; break; - case HID_GD_LEFT: usage->hat_dir = 7; break; - default: goto unknown; - } - if (field->dpad) { - map_abs(field->dpad); - goto ignore; - } - map_abs(ABS_HAT0X); - break; - } - switch (usage->hid) { /* These usage IDs map directly to the usage codes. */ case HID_GD_X: case HID_GD_Y: case HID_GD_Z: diff --git a/drivers/hid/hid-ite.c b/drivers/hid/hid-ite.c index 75ebfcf31889..8e42780a2663 100644 --- a/drivers/hid/hid-ite.c +++ b/drivers/hid/hid-ite.c @@ -13,7 +13,7 @@ #define QUIRK_TOUCHPAD_ON_OFF_REPORT BIT(0) -static __u8 *ite_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) +static const __u8 *ite_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); @@ -141,4 +141,5 @@ static struct hid_driver ite_driver = { module_hid_driver(ite_driver); MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_DESCRIPTION("HID driver for some ITE \"special\" devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-kensington.c b/drivers/hid/hid-kensington.c index b31f7f431a3f..16839027981f 100644 --- a/drivers/hid/hid-kensington.c +++ b/drivers/hid/hid-kensington.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * HID driver for Kensigton Slimblade Trackball + * HID driver for Kensington Slimblade Trackball * * Copyright (c) 2009 Jiri Kosina */ @@ -46,4 +46,5 @@ static struct hid_driver ks_driver = { }; module_hid_driver(ks_driver); +MODULE_DESCRIPTION("HID driver for Kensington Slimblade Trackball"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-keytouch.c b/drivers/hid/hid-keytouch.c index 73bf8642dfe3..b9abd53a5864 100644 --- a/drivers/hid/hid-keytouch.c +++ b/drivers/hid/hid-keytouch.c @@ -16,7 +16,7 @@ /* Replace the broken report descriptor of this device with rather * a default one */ -static __u8 keytouch_fixed_rdesc[] = { +static const __u8 keytouch_fixed_rdesc[] = { 0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x05, 0x07, 0x19, 0xe0, 0x29, 0xe7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08, 0x81, 0x01, 0x95, 0x03, 0x75, 0x01, 0x05, 0x08, 0x19, 0x01, 0x29, 0x03, 0x91, @@ -24,15 +24,13 @@ static __u8 keytouch_fixed_rdesc[] = { 0x26, 0xff, 0x00, 0x05, 0x07, 0x19, 0x00, 0x2a, 0xff, 0x00, 0x81, 0x00, 0xc0 }; -static __u8 *keytouch_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *keytouch_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { hid_info(hdev, "fixing up Keytouch IEC report descriptor\n"); - rdesc = keytouch_fixed_rdesc; *rsize = sizeof(keytouch_fixed_rdesc); - - return rdesc; + return keytouch_fixed_rdesc; } static const struct hid_device_id keytouch_devices[] = { @@ -48,5 +46,6 @@ static struct hid_driver keytouch_driver = { }; module_hid_driver(keytouch_driver); +MODULE_DESCRIPTION("HID driver for Keytouch devices not fully compliant with HID standard"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jiri Kosina"); diff --git a/drivers/hid/hid-kye.c b/drivers/hid/hid-kye.c index eb9bf2829937..bd96bfa7af70 100644 --- a/drivers/hid/hid-kye.c +++ b/drivers/hid/hid-kye.c @@ -8,7 +8,7 @@ * Copyright (c) 2023 David Yang */ -#include <asm-generic/unaligned.h> +#include <linux/unaligned.h> #include <linux/device.h> #include <linux/hid.h> #include <linux/module.h> @@ -209,7 +209,7 @@ static const __u8 pensketch_t609a_control_rdesc[] = { 0xC0 /* End Collection */ }; -/* Fix indexes in kye_tablet_fixup if you change this */ +/* Fix indexes in kye_tablet_fixup() if you change this */ static const __u8 kye_tablet_rdesc[] = { 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ 0x09, 0x01, /* Usage (01h), */ @@ -262,12 +262,16 @@ static const __u8 kye_tablet_rdesc[] = { 0x27, 0xFF, 0x07, 0x00, 0x00, /* Logical Maximum (2047), */ 0x81, 0x02, /* Input (Variable), */ 0xC0, /* End Collection, */ - 0xC0, /* End Collection, */ - 0x05, 0x0D, /* Usage Page (Digitizer), */ - 0x09, 0x21, /* Usage (Puck), */ + 0xC0 /* End Collection, */ +}; + +/* Fix indexes in kye_tablet_fixup() if you change this */ +static const __u8 kye_tablet_mouse_rdesc[] = { + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x02, /* Usage (Mouse), */ 0xA1, 0x01, /* Collection (Application), */ 0x85, 0x11, /* Report ID (17), */ - 0x09, 0x21, /* Usage (Puck), */ + 0x09, 0x01, /* Usage (Pointer), */ 0xA0, /* Collection (Physical), */ 0x05, 0x09, /* Usage Page (Button), */ 0x19, 0x01, /* Usage Minimum (01h), */ @@ -280,7 +284,7 @@ static const __u8 kye_tablet_rdesc[] = { 0x95, 0x04, /* Report Count (4), */ 0x81, 0x01, /* Input (Constant), */ 0x05, 0x0D, /* Usage Page (Digitizer), */ - 0x09, 0x32, /* Usage (In Range), */ + 0x09, 0x37, /* Usage (Data Valid), */ 0x95, 0x01, /* Report Count (1), */ 0x81, 0x02, /* Input (Variable), */ 0x05, 0x01, /* Usage Page (Desktop), */ @@ -317,7 +321,7 @@ static const struct kye_tablet_info { __s32 y_physical_maximum; __s8 unit_exponent; __s8 unit; - bool has_punk; + bool has_mouse; unsigned int control_rsize; const __u8 *control_rdesc; } kye_tablets_info[] = { @@ -402,7 +406,7 @@ static __u8 *kye_consumer_control_fixup(struct hid_device *hdev, __u8 *rdesc, static __u8 *kye_tablet_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { const struct kye_tablet_info *info; - unsigned int newsize; + __u8 *newdesc = rdesc; if (*rsize < sizeof(kye_tablet_rdesc)) { hid_warn(hdev, @@ -420,40 +424,49 @@ static __u8 *kye_tablet_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int return rdesc; } - newsize = info->has_punk ? sizeof(kye_tablet_rdesc) : 112; - memcpy(rdesc, kye_tablet_rdesc, newsize); - - put_unaligned_le32(info->x_logical_maximum, rdesc + 66); - put_unaligned_le32(info->x_physical_maximum, rdesc + 72); - rdesc[77] = info->unit; - rdesc[79] = info->unit_exponent; - put_unaligned_le32(info->y_logical_maximum, rdesc + 87); - put_unaligned_le32(info->y_physical_maximum, rdesc + 92); - put_unaligned_le32(info->pressure_logical_maximum, rdesc + 104); - - if (info->has_punk) { - put_unaligned_le32(info->x_logical_maximum, rdesc + 156); - put_unaligned_le32(info->x_physical_maximum, rdesc + 162); - rdesc[167] = info->unit; - rdesc[169] = info->unit_exponent; - put_unaligned_le32(info->y_logical_maximum, rdesc + 177); - put_unaligned_le32(info->y_physical_maximum, rdesc + 182); + memcpy(newdesc, kye_tablet_rdesc, sizeof(kye_tablet_rdesc)); + + put_unaligned_le32(info->x_logical_maximum, newdesc + 66); + put_unaligned_le32(info->x_physical_maximum, newdesc + 72); + newdesc[77] = info->unit; + newdesc[79] = info->unit_exponent; + put_unaligned_le32(info->y_logical_maximum, newdesc + 87); + put_unaligned_le32(info->y_physical_maximum, newdesc + 92); + put_unaligned_le32(info->pressure_logical_maximum, newdesc + 104); + + newdesc += sizeof(kye_tablet_rdesc); + + if (info->has_mouse) { + if (newdesc + sizeof(kye_tablet_mouse_rdesc) > rdesc + *rsize) + hid_err(hdev, "control desc unexpectedly large\n"); + else { + memcpy(newdesc, kye_tablet_mouse_rdesc, sizeof(kye_tablet_mouse_rdesc)); + + put_unaligned_le32(info->x_logical_maximum, newdesc + 44); + put_unaligned_le32(info->x_physical_maximum, newdesc + 50); + newdesc[55] = info->unit; + newdesc[57] = info->unit_exponent; + put_unaligned_le32(info->y_logical_maximum, newdesc + 65); + put_unaligned_le32(info->y_physical_maximum, newdesc + 70); + + newdesc += sizeof(kye_tablet_mouse_rdesc); + } } if (info->control_rsize) { - if (newsize + info->control_rsize > *rsize) - hid_err(hdev, "control rdesc unexpectedly large"); + if (newdesc + info->control_rsize > rdesc + *rsize) + hid_err(hdev, "control desc unexpectedly large\n"); else { - memcpy(rdesc + newsize, info->control_rdesc, info->control_rsize); - newsize += info->control_rsize; + memcpy(newdesc, info->control_rdesc, info->control_rsize); + newdesc += info->control_rsize; } } - *rsize = newsize; + *rsize = newdesc - rdesc; return rdesc; } -static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { switch (hdev->product) { @@ -658,4 +671,5 @@ static struct hid_driver kye_driver = { }; module_hid_driver(kye_driver); +MODULE_DESCRIPTION("HID driver for Kye/Genius devices not fully compliant with HID standard"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-kysona.c b/drivers/hid/hid-kysona.c new file mode 100644 index 000000000000..d4c0406b3323 --- /dev/null +++ b/drivers/hid/hid-kysona.c @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * USB HID driver for Kysona + * Kysona M600 mice. + * + * Copyright (c) 2024 Lode Willems <me@lodewillems.com> + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/usb.h> + +#include "hid-ids.h" + +#define BATTERY_TIMEOUT_MS 5000 + +#define BATTERY_REPORT_ID 4 + +struct kysona_drvdata { + struct hid_device *hdev; + bool online; + + struct power_supply_desc battery_desc; + struct power_supply *battery; + u8 battery_capacity; + bool battery_charging; + u16 battery_voltage; + struct delayed_work battery_work; +}; + +static enum power_supply_property kysona_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_SCOPE, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_ONLINE +}; + +static int kysona_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct kysona_drvdata *drv_data = power_supply_get_drvdata(psy); + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = drv_data->online; + break; + case POWER_SUPPLY_PROP_STATUS: + if (drv_data->online) + val->intval = drv_data->battery_charging ? + POWER_SUPPLY_STATUS_CHARGING : + POWER_SUPPLY_STATUS_DISCHARGING; + else + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + break; + case POWER_SUPPLY_PROP_SCOPE: + val->intval = POWER_SUPPLY_SCOPE_DEVICE; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = drv_data->battery_capacity; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + /* hardware reports voltage in mV. sysfs expects uV */ + val->intval = drv_data->battery_voltage * 1000; + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = drv_data->hdev->name; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static const char kysona_battery_request[] = { + 0x08, BATTERY_REPORT_ID, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49 +}; + +static int kysona_m600_fetch_battery(struct hid_device *hdev) +{ + u8 *write_buf; + int ret; + + /* Request battery information */ + write_buf = kmemdup(kysona_battery_request, sizeof(kysona_battery_request), GFP_KERNEL); + if (!write_buf) + return -ENOMEM; + + ret = hid_hw_raw_request(hdev, kysona_battery_request[0], + write_buf, sizeof(kysona_battery_request), + HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); + if (ret < (int)sizeof(kysona_battery_request)) { + hid_err(hdev, "hid_hw_raw_request() failed with %d\n", ret); + ret = -ENODATA; + } + kfree(write_buf); + return ret; +} + +static void kysona_fetch_battery(struct hid_device *hdev) +{ + int ret = kysona_m600_fetch_battery(hdev); + + if (ret < 0) + hid_dbg(hdev, + "Battery query failed (err: %d)\n", ret); +} + +static void kysona_battery_timer_tick(struct work_struct *work) +{ + struct kysona_drvdata *drv_data = container_of(work, + struct kysona_drvdata, battery_work.work); + struct hid_device *hdev = drv_data->hdev; + + kysona_fetch_battery(hdev); + schedule_delayed_work(&drv_data->battery_work, + msecs_to_jiffies(BATTERY_TIMEOUT_MS)); +} + +static int kysona_battery_probe(struct hid_device *hdev) +{ + struct kysona_drvdata *drv_data = hid_get_drvdata(hdev); + struct power_supply_config pscfg = { .drv_data = drv_data }; + int ret = 0; + + drv_data->online = false; + drv_data->battery_capacity = 100; + drv_data->battery_voltage = 4200; + + drv_data->battery_desc.properties = kysona_battery_props; + drv_data->battery_desc.num_properties = ARRAY_SIZE(kysona_battery_props); + drv_data->battery_desc.get_property = kysona_battery_get_property; + drv_data->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY; + drv_data->battery_desc.use_for_apm = 0; + drv_data->battery_desc.name = devm_kasprintf(&hdev->dev, GFP_KERNEL, + "kysona-%s-battery", + strlen(hdev->uniq) ? + hdev->uniq : dev_name(&hdev->dev)); + if (!drv_data->battery_desc.name) + return -ENOMEM; + + drv_data->battery = devm_power_supply_register(&hdev->dev, + &drv_data->battery_desc, &pscfg); + if (IS_ERR(drv_data->battery)) { + ret = PTR_ERR(drv_data->battery); + drv_data->battery = NULL; + hid_err(hdev, "Unable to register battery device\n"); + return ret; + } + + power_supply_powers(drv_data->battery, &hdev->dev); + + INIT_DELAYED_WORK(&drv_data->battery_work, kysona_battery_timer_tick); + kysona_fetch_battery(hdev); + schedule_delayed_work(&drv_data->battery_work, + msecs_to_jiffies(BATTERY_TIMEOUT_MS)); + + return ret; +} + +static int kysona_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + struct kysona_drvdata *drv_data; + struct usb_interface *usbif; + + if (!hid_is_usb(hdev)) + return -EINVAL; + + usbif = to_usb_interface(hdev->dev.parent); + + drv_data = devm_kzalloc(&hdev->dev, sizeof(*drv_data), GFP_KERNEL); + if (!drv_data) + return -ENOMEM; + + hid_set_drvdata(hdev, drv_data); + drv_data->hdev = hdev; + + ret = hid_parse(hdev); + if (ret) + return ret; + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) + return ret; + + if (usbif->cur_altsetting->desc.bInterfaceNumber == 1) { + if (kysona_battery_probe(hdev) < 0) + hid_err(hdev, "Kysona hid battery_probe failed: %d\n", ret); + } + + return 0; +} + +static int kysona_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + struct kysona_drvdata *drv_data = hid_get_drvdata(hdev); + + if (drv_data->battery && size == sizeof(kysona_battery_request) && + data[0] == 8 && data[1] == BATTERY_REPORT_ID) { + drv_data->battery_capacity = data[6]; + drv_data->battery_charging = data[7]; + drv_data->battery_voltage = (data[8] << 8) | data[9]; + drv_data->online = true; + } + + return 0; +} + +static void kysona_remove(struct hid_device *hdev) +{ + struct kysona_drvdata *drv_data = hid_get_drvdata(hdev); + + if (drv_data->battery) + cancel_delayed_work_sync(&drv_data->battery_work); + + hid_hw_stop(hdev); +} + +static const struct hid_device_id kysona_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_KYSONA, USB_DEVICE_ID_KYSONA_M600_DONGLE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KYSONA, USB_DEVICE_ID_KYSONA_M600_WIRED) }, + { } +}; +MODULE_DEVICE_TABLE(hid, kysona_devices); + +static struct hid_driver kysona_driver = { + .name = "kysona", + .id_table = kysona_devices, + .probe = kysona_probe, + .raw_event = kysona_raw_event, + .remove = kysona_remove +}; +module_hid_driver(kysona_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("HID driver for Kysona devices"); +MODULE_AUTHOR("Lode Willems <me@lodewillems.com>"); diff --git a/drivers/hid/hid-lcpower.c b/drivers/hid/hid-lcpower.c index 8acd3ee5ada5..58953d11ded7 100644 --- a/drivers/hid/hid-lcpower.c +++ b/drivers/hid/hid-lcpower.c @@ -53,4 +53,5 @@ static struct hid_driver ts_driver = { }; module_hid_driver(ts_driver); +MODULE_DESCRIPTION("HID driver for LC Power Model RC1000MCE"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c index f86c1ea83a03..a7d9ca02779e 100644 --- a/drivers/hid/hid-lenovo.c +++ b/drivers/hid/hid-lenovo.c @@ -32,11 +32,20 @@ #include <linux/leds.h> #include <linux/workqueue.h> +#include <linux/platform_profile.h> + #include "hid-ids.h" /* Userspace expects F20 for mic-mute KEY_MICMUTE does not work */ #define LENOVO_KEY_MICMUTE KEY_F20 +/* HID raw events for ThinkPad X12 Tabs*/ +#define TP_X12_RAW_HOTKEY_FN_F4 0x00020003 +#define TP_X12_RAW_HOTKEY_FN_F8 0x38001003 +#define TP_X12_RAW_HOTKEY_FN_F10 0x00000803 +#define TP_X12_RAW_HOTKEY_FN_F12 0x00000403 +#define TP_X12_RAW_HOTKEY_FN_SPACE 0x18001003 + struct lenovo_drvdata { u8 led_report[3]; /* Must be first for proper alignment */ int led_state; @@ -71,6 +80,14 @@ struct lenovo_drvdata { #define TP10UBKBD_LED_OFF 1 #define TP10UBKBD_LED_ON 2 +/* Function to report raw_events as key events*/ +static inline void report_key_event(struct input_dev *input, int keycode) +{ + input_report_key(input, keycode, 1); + input_report_key(input, keycode, 0); + input_sync(input); +} + static int lenovo_led_set_tp10ubkbd(struct hid_device *hdev, u8 led_code, enum led_brightness value) { @@ -133,7 +150,7 @@ static const __u8 lenovo_tpIIbtkbd_need_fixup_collection[] = { 0x81, 0x01, /* Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */ }; -static __u8 *lenovo_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *lenovo_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { switch (hdev->product) { @@ -472,7 +489,10 @@ static int lenovo_input_mapping(struct hid_device *hdev, case USB_DEVICE_ID_LENOVO_TP10UBKBD: return lenovo_input_mapping_tp10_ultrabook_kbd(hdev, hi, field, usage, bit, max); + case USB_DEVICE_ID_LENOVO_X12_TAB: + case USB_DEVICE_ID_LENOVO_X12_TAB2: case USB_DEVICE_ID_LENOVO_X1_TAB: + case USB_DEVICE_ID_LENOVO_X1_TAB3: return lenovo_input_mapping_x1_tab_kbd(hdev, hi, field, usage, bit, max); default: return 0; @@ -555,7 +575,7 @@ static ssize_t attr_fn_lock_show(struct device *dev, struct hid_device *hdev = to_hid_device(dev); struct lenovo_drvdata *data = hid_get_drvdata(hdev); - return snprintf(buf, PAGE_SIZE, "%u\n", data->fn_lock); + return sysfs_emit(buf, "%u\n", data->fn_lock); } static ssize_t attr_fn_lock_store(struct device *dev, @@ -581,8 +601,11 @@ static ssize_t attr_fn_lock_store(struct device *dev, case USB_DEVICE_ID_LENOVO_TPIIBTKBD: lenovo_features_set_cptkbd(hdev); break; + case USB_DEVICE_ID_LENOVO_X12_TAB: + case USB_DEVICE_ID_LENOVO_X12_TAB2: case USB_DEVICE_ID_LENOVO_TP10UBKBD: case USB_DEVICE_ID_LENOVO_X1_TAB: + case USB_DEVICE_ID_LENOVO_X1_TAB3: ret = lenovo_led_set_tp10ubkbd(hdev, TP10UBKBD_FN_LOCK_LED, value); if (ret) return ret; @@ -599,8 +622,7 @@ static ssize_t attr_sensitivity_show_cptkbd(struct device *dev, struct hid_device *hdev = to_hid_device(dev); struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev); - return snprintf(buf, PAGE_SIZE, "%u\n", - cptkbd_data->sensitivity); + return sysfs_emit(buf, "%u\n", cptkbd_data->sensitivity); } static ssize_t attr_sensitivity_store_cptkbd(struct device *dev, @@ -628,8 +650,8 @@ static ssize_t attr_middleclick_workaround_show_cptkbd(struct device *dev, struct hid_device *hdev = to_hid_device(dev); struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev); - return snprintf(buf, PAGE_SIZE, "%u\n", - cptkbd_data->middleclick_workaround_cptkbd); + return sysfs_emit(buf, "%u\n", + cptkbd_data->middleclick_workaround_cptkbd); } static ssize_t attr_middleclick_workaround_store_cptkbd(struct device *dev, @@ -679,6 +701,59 @@ static const struct attribute_group lenovo_attr_group_cptkbd = { .attrs = lenovo_attributes_cptkbd, }; +/* Function to handle Lenovo Thinkpad TAB X12's HID raw inputs for fn keys*/ +static int lenovo_raw_event_TP_X12_tab(struct hid_device *hdev, u32 raw_data) +{ + struct hid_input *hidinput; + struct input_dev *input = NULL; + + /* Iterate through all associated input devices */ + list_for_each_entry(hidinput, &hdev->inputs, list) { + input = hidinput->input; + if (!input) + continue; + + switch (raw_data) { + /* fn-F20 being used here for MIC mute*/ + case TP_X12_RAW_HOTKEY_FN_F4: + report_key_event(input, LENOVO_KEY_MICMUTE); + return 1; + /* Power-mode or Airplane mode will be called based on the device*/ + case TP_X12_RAW_HOTKEY_FN_F8: + /* + * TP X12 TAB uses Fn-F8 calls Airplanemode + * Whereas TP X12 TAB2 uses Fn-F8 for toggling + * Power modes + */ + if (hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB) { + report_key_event(input, KEY_RFKILL); + return 1; + } else { + platform_profile_cycle(); + return 1; + } + return 0; + case TP_X12_RAW_HOTKEY_FN_F10: + /* TAB1 has PICKUP Phone and TAB2 use Snipping tool*/ + (hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB) ? + report_key_event(input, KEY_PICKUP_PHONE) : + report_key_event(input, KEY_SELECTIVE_SCREENSHOT); + return 1; + case TP_X12_RAW_HOTKEY_FN_F12: + /* BookMarks/STAR key*/ + report_key_event(input, KEY_BOOKMARKS); + return 1; + case TP_X12_RAW_HOTKEY_FN_SPACE: + /* Keyboard LED backlight toggle*/ + report_key_event(input, KEY_KBDILLUMTOGGLE); + return 1; + default: + break; + } + } + return 0; +} + static int lenovo_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) { @@ -696,6 +771,15 @@ static int lenovo_raw_event(struct hid_device *hdev, data[2] = 0x01; } + /* + * Lenovo TP X12 Tab KBD's Fn+XX is HID raw data defined. Report ID is 0x03 + * e.g.: Raw data received for MIC mute is 0x00020003. + */ + if (unlikely((hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB + || hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB2) + && size >= 3 && report->id == 0x03)) + return lenovo_raw_event_TP_X12_tab(hdev, le32_to_cpu(*(u32 *)data)); + return 0; } @@ -775,8 +859,11 @@ static int lenovo_event(struct hid_device *hdev, struct hid_field *field, case USB_DEVICE_ID_LENOVO_TPIIUSBKBD: case USB_DEVICE_ID_LENOVO_TPIIBTKBD: return lenovo_event_cptkbd(hdev, field, usage, value); + case USB_DEVICE_ID_LENOVO_X12_TAB: + case USB_DEVICE_ID_LENOVO_X12_TAB2: case USB_DEVICE_ID_LENOVO_TP10UBKBD: case USB_DEVICE_ID_LENOVO_X1_TAB: + case USB_DEVICE_ID_LENOVO_X1_TAB3: return lenovo_event_tp10ubkbd(hdev, field, usage, value); default: return 0; @@ -809,7 +896,7 @@ static ssize_t attr_press_to_select_show_tpkbd(struct device *dev, struct hid_device *hdev = to_hid_device(dev); struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); - return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select); + return sysfs_emit(buf, "%u\n", data_pointer->press_to_select); } static ssize_t attr_press_to_select_store_tpkbd(struct device *dev, @@ -839,7 +926,7 @@ static ssize_t attr_dragging_show_tpkbd(struct device *dev, struct hid_device *hdev = to_hid_device(dev); struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); - return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging); + return sysfs_emit(buf, "%u\n", data_pointer->dragging); } static ssize_t attr_dragging_store_tpkbd(struct device *dev, @@ -869,7 +956,7 @@ static ssize_t attr_release_to_select_show_tpkbd(struct device *dev, struct hid_device *hdev = to_hid_device(dev); struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); - return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select); + return sysfs_emit(buf, "%u\n", data_pointer->release_to_select); } static ssize_t attr_release_to_select_store_tpkbd(struct device *dev, @@ -899,7 +986,7 @@ static ssize_t attr_select_right_show_tpkbd(struct device *dev, struct hid_device *hdev = to_hid_device(dev); struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); - return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right); + return sysfs_emit(buf, "%u\n", data_pointer->select_right); } static ssize_t attr_select_right_store_tpkbd(struct device *dev, @@ -929,8 +1016,7 @@ static ssize_t attr_sensitivity_show_tpkbd(struct device *dev, struct hid_device *hdev = to_hid_device(dev); struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); - return snprintf(buf, PAGE_SIZE, "%u\n", - data_pointer->sensitivity); + return sysfs_emit(buf, "%u\n", data_pointer->sensitivity); } static ssize_t attr_sensitivity_store_tpkbd(struct device *dev, @@ -958,8 +1044,7 @@ static ssize_t attr_press_speed_show_tpkbd(struct device *dev, struct hid_device *hdev = to_hid_device(dev); struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev); - return snprintf(buf, PAGE_SIZE, "%u\n", - data_pointer->press_speed); + return sysfs_emit(buf, "%u\n", data_pointer->press_speed); } static ssize_t attr_press_speed_store_tpkbd(struct device *dev, @@ -1057,8 +1142,11 @@ static int lenovo_led_brightness_set(struct led_classdev *led_cdev, case USB_DEVICE_ID_LENOVO_TPKBD: lenovo_led_set_tpkbd(hdev); break; + case USB_DEVICE_ID_LENOVO_X12_TAB: + case USB_DEVICE_ID_LENOVO_X12_TAB2: case USB_DEVICE_ID_LENOVO_TP10UBKBD: case USB_DEVICE_ID_LENOVO_X1_TAB: + case USB_DEVICE_ID_LENOVO_X1_TAB3: ret = lenovo_led_set_tp10ubkbd(hdev, tp10ubkbd_led[led_nr], value); break; } @@ -1242,8 +1330,15 @@ static int lenovo_probe_tp10ubkbd(struct hid_device *hdev) * We cannot read the state, only set it, so we force it to on here * (which should be a no-op) to make sure that our state matches the * keyboard's FN-lock state. This is the same as what Windows does. + * + * For X12 TAB and TAB2, the default windows behaviour Fn-lock Off. + * Adding additional check to ensure the behaviour in case of + * Thinkpad X12 Tabs. */ - data->fn_lock = true; + + data->fn_lock = !(hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB || + hdev->product == USB_DEVICE_ID_LENOVO_X12_TAB2); + lenovo_led_set_tp10ubkbd(hdev, TP10UBKBD_FN_LOCK_LED, data->fn_lock); ret = sysfs_create_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd); @@ -1287,8 +1382,11 @@ static int lenovo_probe(struct hid_device *hdev, case USB_DEVICE_ID_LENOVO_TPIIBTKBD: ret = lenovo_probe_cptkbd(hdev); break; + case USB_DEVICE_ID_LENOVO_X12_TAB: + case USB_DEVICE_ID_LENOVO_X12_TAB2: case USB_DEVICE_ID_LENOVO_TP10UBKBD: case USB_DEVICE_ID_LENOVO_X1_TAB: + case USB_DEVICE_ID_LENOVO_X1_TAB3: ret = lenovo_probe_tp10ubkbd(hdev); break; default: @@ -1373,8 +1471,11 @@ static void lenovo_remove(struct hid_device *hdev) case USB_DEVICE_ID_LENOVO_TPIIBTKBD: lenovo_remove_cptkbd(hdev); break; + case USB_DEVICE_ID_LENOVO_X12_TAB: + case USB_DEVICE_ID_LENOVO_X12_TAB2: case USB_DEVICE_ID_LENOVO_TP10UBKBD: case USB_DEVICE_ID_LENOVO_X1_TAB: + case USB_DEVICE_ID_LENOVO_X1_TAB3: lenovo_remove_tp10ubkbd(hdev); break; } @@ -1424,6 +1525,12 @@ static const struct hid_device_id lenovo_devices[] = { */ { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X1_TAB) }, + { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, + USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X1_TAB3) }, + { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, + USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X12_TAB) }, + { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, + USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X12_TAB2) }, { } }; @@ -1445,4 +1552,5 @@ static struct hid_driver lenovo_driver = { }; module_hid_driver(lenovo_driver); +MODULE_DESCRIPTION("HID driver for IBM/Lenovo"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-letsketch.c b/drivers/hid/hid-letsketch.c index 97f047f18136..8602c63ed9c6 100644 --- a/drivers/hid/hid-letsketch.c +++ b/drivers/hid/hid-letsketch.c @@ -41,7 +41,7 @@ #include <linux/timer.h> #include <linux/usb.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "hid-ids.h" @@ -319,4 +319,5 @@ static struct hid_driver letsketch_driver = { module_hid_driver(letsketch_driver); MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_DESCRIPTION("Driver for the LetSketch / VSON WP9620N drawing tablet"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-lg-g15.c b/drivers/hid/hid-lg-g15.c index acbec1dcf196..53e7b90f9cc3 100644 --- a/drivers/hid/hid-lg-g15.c +++ b/drivers/hid/hid-lg-g15.c @@ -954,4 +954,5 @@ static struct hid_driver lg_g15_driver = { module_hid_driver(lg_g15_driver); MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_DESCRIPTION("HID driver for gaming keys on Logitech gaming keyboards"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index fb3f7258009c..9a2cfa018bd3 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -58,7 +58,7 @@ * These descriptors remove the combined Y axis and instead report * separate throttle (Y) and brake (RZ). */ -static __u8 df_rdesc_fixed[] = { +static const __u8 df_rdesc_fixed[] = { 0x05, 0x01, /* Usage Page (Desktop), */ 0x09, 0x04, /* Usage (Joystick), */ 0xA1, 0x01, /* Collection (Application), */ @@ -124,7 +124,7 @@ static __u8 df_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -static __u8 dfp_rdesc_fixed[] = { +static const __u8 dfp_rdesc_fixed[] = { 0x05, 0x01, /* Usage Page (Desktop), */ 0x09, 0x04, /* Usage (Joystick), */ 0xA1, 0x01, /* Collection (Application), */ @@ -172,7 +172,7 @@ static __u8 dfp_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -static __u8 fv_rdesc_fixed[] = { +static const __u8 fv_rdesc_fixed[] = { 0x05, 0x01, /* Usage Page (Desktop), */ 0x09, 0x04, /* Usage (Joystick), */ 0xA1, 0x01, /* Collection (Application), */ @@ -239,7 +239,7 @@ static __u8 fv_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -static __u8 momo_rdesc_fixed[] = { +static const __u8 momo_rdesc_fixed[] = { 0x05, 0x01, /* Usage Page (Desktop), */ 0x09, 0x04, /* Usage (Joystick), */ 0xA1, 0x01, /* Collection (Application), */ @@ -285,7 +285,7 @@ static __u8 momo_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -static __u8 momo2_rdesc_fixed[] = { +static const __u8 momo2_rdesc_fixed[] = { 0x05, 0x01, /* Usage Page (Desktop), */ 0x09, 0x04, /* Usage (Joystick), */ 0xA1, 0x01, /* Collection (Application), */ @@ -333,7 +333,7 @@ static __u8 momo2_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -static __u8 ffg_rdesc_fixed[] = { +static const __u8 ffg_rdesc_fixed[] = { 0x05, 0x01, /* Usage Page (Desktop), */ 0x09, 0x04, /* Usage (Joystik), */ 0xA1, 0x01, /* Collection (Application), */ @@ -379,7 +379,7 @@ static __u8 ffg_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -static __u8 fg_rdesc_fixed[] = { +static const __u8 fg_rdesc_fixed[] = { 0x05, 0x01, /* Usage Page (Desktop), */ 0x09, 0x04, /* Usage (Joystik), */ 0xA1, 0x01, /* Collection (Application), */ @@ -427,7 +427,7 @@ static __u8 fg_rdesc_fixed[] = { * above the logical maximum described in descriptor. This extends * the original value of 0x28c of logical maximum to 0x104d */ -static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { struct lg_drv_data *drv_data = hid_get_drvdata(hdev); @@ -453,8 +453,8 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, if (*rsize == FG_RDESC_ORIG_SIZE) { hid_info(hdev, "fixing up Logitech Wingman Formula GP report descriptor\n"); - rdesc = fg_rdesc_fixed; *rsize = sizeof(fg_rdesc_fixed); + return fg_rdesc_fixed; } else { hid_info(hdev, "rdesc size test failed for formula gp\n"); @@ -466,8 +466,8 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, if (*rsize == FFG_RDESC_ORIG_SIZE) { hid_info(hdev, "fixing up Logitech Wingman Formula Force GP report descriptor\n"); - rdesc = ffg_rdesc_fixed; *rsize = sizeof(ffg_rdesc_fixed); + return ffg_rdesc_fixed; } break; @@ -476,8 +476,8 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, if (*rsize == DF_RDESC_ORIG_SIZE) { hid_info(hdev, "fixing up Logitech Driving Force report descriptor\n"); - rdesc = df_rdesc_fixed; *rsize = sizeof(df_rdesc_fixed); + return df_rdesc_fixed; } break; @@ -485,8 +485,8 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, if (*rsize == MOMO_RDESC_ORIG_SIZE) { hid_info(hdev, "fixing up Logitech Momo Force (Red) report descriptor\n"); - rdesc = momo_rdesc_fixed; *rsize = sizeof(momo_rdesc_fixed); + return momo_rdesc_fixed; } break; @@ -494,8 +494,8 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, if (*rsize == MOMO2_RDESC_ORIG_SIZE) { hid_info(hdev, "fixing up Logitech Momo Racing Force (Black) report descriptor\n"); - rdesc = momo2_rdesc_fixed; *rsize = sizeof(momo2_rdesc_fixed); + return momo2_rdesc_fixed; } break; @@ -503,8 +503,8 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, if (*rsize == FV_RDESC_ORIG_SIZE) { hid_info(hdev, "fixing up Logitech Formula Vibration report descriptor\n"); - rdesc = fv_rdesc_fixed; *rsize = sizeof(fv_rdesc_fixed); + return fv_rdesc_fixed; } break; @@ -512,8 +512,8 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, if (*rsize == DFP_RDESC_ORIG_SIZE) { hid_info(hdev, "fixing up Logitech Driving Force Pro report descriptor\n"); - rdesc = dfp_rdesc_fixed; *rsize = sizeof(dfp_rdesc_fixed); + return dfp_rdesc_fixed; } break; @@ -942,4 +942,5 @@ module_param_named(lg4ff_no_autoswitch, lg4ff_no_autoswitch, int, S_IRUGO); MODULE_PARM_DESC(lg4ff_no_autoswitch, "Do not switch multimode wheels to their native mode automatically"); #endif +MODULE_DESCRIPTION("HID driver for some logitech \"special\" devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index e3fcf1353fb3..c0a138f21ca4 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -1350,7 +1350,8 @@ int lg4ff_init(struct hid_device *hid) /* Initialize device properties */ if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) { - BUG_ON(mmode_idx == -1); + if (WARN_ON(mmode_idx == -1)) + return -EINVAL; mmode_wheel = &lg4ff_multimode_wheels[mmode_idx]; } lg4ff_init_wheel_data(&entry->wdata, &lg4ff_devices[i], mmode_wheel, real_product_id); diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 3c3c497b6b91..34fa71ceec2b 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -13,7 +13,7 @@ #include <linux/kfifo.h> #include <linux/delay.h> #include <linux/usb.h> /* For to_usb_interface for kvm extra intf check */ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "hid-ids.h" #define DJ_MAX_PAIRED_DEVICES 7 @@ -1284,8 +1284,10 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev, */ msleep(50); - if (retval) + if (retval) { + kfree(dj_report); return retval; + } } /* @@ -2045,6 +2047,7 @@ static struct hid_driver logi_djreceiver_driver = { module_hid_driver(logi_djreceiver_driver); +MODULE_DESCRIPTION("HID driver for Logitech receivers"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Logitech"); MODULE_AUTHOR("Nestor Lopez Casado"); diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index d2f3f234f29d..10a3bc5f931b 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -23,10 +23,11 @@ #include <linux/workqueue.h> #include <linux/atomic.h> #include <linux/fixp-arith.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "usbhid/usbhid.h" #include "hid-ids.h" +MODULE_DESCRIPTION("Support for Logitech devices relying on the HID++ specification"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>"); MODULE_AUTHOR("Nestor Lopez Casado <nlopezcasad@logitech.com>"); @@ -927,7 +928,7 @@ static int hidpp_unifying_init(struct hidpp_device *hidpp) #define CMD_ROOT_GET_PROTOCOL_VERSION 0x10 static int hidpp_root_get_feature(struct hidpp_device *hidpp, u16 feature, - u8 *feature_index, u8 *feature_type) + u8 *feature_index) { struct hidpp_report response; int ret; @@ -944,7 +945,6 @@ static int hidpp_root_get_feature(struct hidpp_device *hidpp, u16 feature, return -ENOENT; *feature_index = response.fap.params[0]; - *feature_type = response.fap.params[1]; return ret; } @@ -1011,13 +1011,11 @@ print_version: static int hidpp_get_serial(struct hidpp_device *hidpp, u32 *serial) { struct hidpp_report response; - u8 feature_type; u8 feature_index; int ret; ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_DEVICE_INFORMATION, - &feature_index, - &feature_type); + &feature_index); if (ret) return ret; @@ -1124,7 +1122,6 @@ static int hidpp_devicenametype_get_device_name(struct hidpp_device *hidpp, static char *hidpp_get_device_name(struct hidpp_device *hidpp) { - u8 feature_type; u8 feature_index; u8 __name_length; char *name; @@ -1132,7 +1129,7 @@ static char *hidpp_get_device_name(struct hidpp_device *hidpp) int ret; ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_GET_DEVICE_NAME_TYPE, - &feature_index, &feature_type); + &feature_index); if (ret) return NULL; @@ -1299,15 +1296,13 @@ static int hidpp20_batterylevel_get_battery_info(struct hidpp_device *hidpp, static int hidpp20_query_battery_info_1000(struct hidpp_device *hidpp) { - u8 feature_type; int ret; int status, capacity, next_capacity, level; if (hidpp->battery.feature_index == 0xff) { ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_BATTERY_LEVEL_STATUS, - &hidpp->battery.feature_index, - &feature_type); + &hidpp->battery.feature_index); if (ret) return ret; } @@ -1488,14 +1483,12 @@ static int hidpp20_map_battery_capacity(struct hid_device *hid_dev, int voltage) static int hidpp20_query_battery_voltage_info(struct hidpp_device *hidpp) { - u8 feature_type; int ret; int status, voltage, level, charge_type; if (hidpp->battery.voltage_feature_index == 0xff) { ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_BATTERY_VOLTAGE, - &hidpp->battery.voltage_feature_index, - &feature_type); + &hidpp->battery.voltage_feature_index); if (ret) return ret; } @@ -1691,7 +1684,6 @@ static int hidpp20_unifiedbattery_get_status(struct hidpp_device *hidpp, static int hidpp20_query_battery_info_1004(struct hidpp_device *hidpp) { - u8 feature_type; int ret; u8 state_of_charge; int status, level; @@ -1699,8 +1691,7 @@ static int hidpp20_query_battery_info_1004(struct hidpp_device *hidpp) if (hidpp->battery.feature_index == 0xff) { ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_UNIFIED_BATTERY, - &hidpp->battery.feature_index, - &feature_type); + &hidpp->battery.feature_index); if (ret) return ret; } @@ -1833,14 +1824,9 @@ static int hidpp_battery_get_property(struct power_supply *psy, static int hidpp_get_wireless_feature_index(struct hidpp_device *hidpp, u8 *feature_index) { - u8 feature_type; - int ret; - - ret = hidpp_root_get_feature(hidpp, - HIDPP_PAGE_WIRELESS_DEVICE_STATUS, - feature_index, &feature_type); - - return ret; + return hidpp_root_get_feature(hidpp, + HIDPP_PAGE_WIRELESS_DEVICE_STATUS, + feature_index); } /* -------------------------------------------------------------------------- */ @@ -1951,14 +1937,11 @@ static bool hidpp20_get_adc_measurement_1f20(struct hidpp_device *hidpp, static int hidpp20_query_adc_measurement_info_1f20(struct hidpp_device *hidpp) { - u8 feature_type; - if (hidpp->battery.adc_measurement_feature_index == 0xff) { int ret; ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_ADC_MEASUREMENT, - &hidpp->battery.adc_measurement_feature_index, - &feature_type); + &hidpp->battery.adc_measurement_feature_index); if (ret) return ret; @@ -2013,15 +1996,13 @@ static int hidpp_hrs_set_highres_scrolling_mode(struct hidpp_device *hidpp, bool enabled, u8 *multiplier) { u8 feature_index; - u8 feature_type; int ret; u8 params[1]; struct hidpp_report response; ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HI_RESOLUTION_SCROLLING, - &feature_index, - &feature_type); + &feature_index); if (ret) return ret; @@ -2048,12 +2029,11 @@ static int hidpp_hrw_get_wheel_capability(struct hidpp_device *hidpp, u8 *multiplier) { u8 feature_index; - u8 feature_type; int ret; struct hidpp_report response; ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL, - &feature_index, &feature_type); + &feature_index); if (ret) goto return_default; @@ -2075,13 +2055,12 @@ static int hidpp_hrw_set_wheel_mode(struct hidpp_device *hidpp, bool invert, bool high_resolution, bool use_hidpp) { u8 feature_index; - u8 feature_type; int ret; u8 params[1]; struct hidpp_report response; ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL, - &feature_index, &feature_type); + &feature_index); if (ret) return ret; @@ -2110,14 +2089,12 @@ static int hidpp_solar_request_battery_event(struct hidpp_device *hidpp) { struct hidpp_report response; u8 params[2] = { 1, 1 }; - u8 feature_type; int ret; if (hidpp->battery.feature_index == 0xff) { ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_SOLAR_KEYBOARD, - &hidpp->battery.solar_feature_index, - &feature_type); + &hidpp->battery.solar_feature_index); if (ret) return ret; } @@ -2521,7 +2498,7 @@ static void hidpp_ff_work_handler(struct work_struct *w) /* regular effect destroyed */ data->effect_ids[wd->params[0]-1] = -1; else if (wd->effect_id >= HIDPP_FF_EFFECTID_AUTOCENTER) - /* autocenter spring destoyed */ + /* autocenter spring destroyed */ data->slot_autocenter = 0; break; case HIDPP_FF_SET_GLOBAL_GAINS: @@ -3097,11 +3074,10 @@ static int wtp_get_config(struct hidpp_device *hidpp) { struct wtp_data *wd = hidpp->private_data; struct hidpp_touchpad_raw_info raw_info = {0}; - u8 feature_type; int ret; ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_TOUCHPAD_RAW_XY, - &wd->mt_feature_index, &feature_type); + &wd->mt_feature_index); if (ret) /* means that the device is not powered up */ return ret; @@ -3295,13 +3271,13 @@ static int m560_raw_event(struct hid_device *hdev, u8 *data, int size) 120); } - v = hid_snto32(hid_field_extract(hdev, data+3, 0, 12), 12); + v = sign_extend32(hid_field_extract(hdev, data + 3, 0, 12), 11); input_report_rel(hidpp->input, REL_X, v); - v = hid_snto32(hid_field_extract(hdev, data+3, 12, 12), 12); + v = sign_extend32(hid_field_extract(hdev, data + 3, 12, 12), 11); input_report_rel(hidpp->input, REL_Y, v); - v = hid_snto32(data[6], 8); + v = sign_extend32(data[6], 7); if (v != 0) hidpp_scroll_counter_handle_scroll(hidpp->input, &hidpp->vertical_wheel_counter, v); @@ -3361,12 +3337,11 @@ static int k400_disable_tap_to_click(struct hidpp_device *hidpp) struct k400_private_data *k400 = hidpp->private_data; struct hidpp_touchpad_fw_items items = {}; int ret; - u8 feature_type; if (!k400->feature_index) { ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_TOUCHPAD_FW_ITEMS, - &k400->feature_index, &feature_type); + &k400->feature_index); if (ret) /* means that the device is not powered up */ return ret; @@ -3438,14 +3413,13 @@ static int g920_get_config(struct hidpp_device *hidpp, struct hidpp_ff_private_data *data) { struct hidpp_report response; - u8 feature_type; int ret; memset(data, 0, sizeof(*data)); /* Find feature and store for later use */ ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_G920_FORCE_FEEDBACK, - &data->feature_index, &feature_type); + &data->feature_index); if (ret) return ret; @@ -3734,17 +3708,16 @@ static int hidpp_initialize_hires_scroll(struct hidpp_device *hidpp) if (hidpp->protocol_major >= 2) { u8 feature_index; - u8 feature_type; ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL, - &feature_index, &feature_type); + &feature_index); if (!ret) { hidpp->capabilities |= HIDPP_CAPABILITY_HIDPP20_HI_RES_WHEEL; hid_dbg(hidpp->hid_dev, "Detected HID++ 2.0 hi-res scroll wheel\n"); return 0; } ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HI_RESOLUTION_SCROLLING, - &feature_index, &feature_type); + &feature_index); if (!ret) { hidpp->capabilities |= HIDPP_CAPABILITY_HIDPP20_HI_RES_SCROLL; hid_dbg(hidpp->hid_dev, "Detected HID++ 2.0 hi-res scrolling\n"); @@ -3766,8 +3739,8 @@ static int hidpp_initialize_hires_scroll(struct hidpp_device *hidpp) /* Generic HID++ devices */ /* -------------------------------------------------------------------------- */ -static u8 *hidpp_report_fixup(struct hid_device *hdev, u8 *rdesc, - unsigned int *rsize) +static const u8 *hidpp_report_fixup(struct hid_device *hdev, u8 *rdesc, + unsigned int *rsize) { struct hidpp_device *hidpp = hid_get_drvdata(hdev); @@ -4603,6 +4576,12 @@ static const struct hid_device_id hidpp_devices[] = { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC081) }, { /* Logitech G903 Gaming Mouse over USB */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC086) }, + { /* Logitech G Pro Gaming Mouse over USB */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC088) }, + { /* MX Vertical over USB */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC08A) }, + { /* Logitech G703 Hero Gaming Mouse over USB */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC090) }, { /* Logitech G903 Hero Gaming Mouse over USB */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC091) }, { /* Logitech G915 TKL Keyboard over USB */ @@ -4613,8 +4592,6 @@ static const struct hid_device_id hidpp_devices[] = { { /* Logitech G923 Wheel (Xbox version) over USB */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G923_XBOX_WHEEL), .driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS }, - { /* Logitech G Pro Gaming Mouse over USB */ - HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC088) }, { /* Logitech G Pro X Superlight Gaming Mouse over USB */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC094) }, { /* Logitech G Pro X Superlight 2 Gaming Mouse over USB */ @@ -4641,9 +4618,13 @@ static const struct hid_device_id hidpp_devices[] = { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb012) }, { /* M720 Triathlon mouse over Bluetooth */ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb015) }, + { /* MX Master 2S mouse over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb019) }, { /* MX Ergo trackball over Bluetooth */ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb01d) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb01e) }, + { /* MX Vertical mouse over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb020) }, { /* Signature M650 over Bluetooth */ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb02a) }, { /* MX Master 3 mouse over Bluetooth */ @@ -4652,6 +4633,8 @@ static const struct hid_device_id hidpp_devices[] = { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb025) }, { /* MX Master 3S mouse over Bluetooth */ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb034) }, + { /* MX Anywhere 3SB mouse over Bluetooth */ + HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb038) }, {} }; diff --git a/drivers/hid/hid-macally.c b/drivers/hid/hid-macally.c index aea46e522008..fe7576458afa 100644 --- a/drivers/hid/hid-macally.c +++ b/drivers/hid/hid-macally.c @@ -18,8 +18,8 @@ MODULE_LICENSE("GPL"); * The Macally ikey keyboard says that its logical and usage maximums are both * 101, but the power key is 102 and the equals key is 103 */ -static __u8 *macally_report_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) +static const __u8 *macally_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) { if (*rsize >= 60 && rdesc[53] == 0x65 && rdesc[59] == 0x65) { hid_info(hdev, diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index a46ff4e8b99f..a76f17158539 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -52,6 +52,7 @@ module_param(report_undeciphered, bool, 0644); MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event"); #define TRACKPAD2_2021_BT_VERSION 0x110 +#define TRACKPAD_2024_BT_VERSION 0x314 #define TRACKPAD_REPORT_ID 0x28 #define TRACKPAD2_USB_REPORT_ID 0x02 @@ -227,7 +228,9 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda touch_minor = tdata[4]; state = tdata[7] & TOUCH_STATE_MASK; down = state != TOUCH_STATE_NONE; - } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { + } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 || + input->id.product == + USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) { id = tdata[8] & 0xf; x = (tdata[1] << 27 | tdata[0] << 19) >> 19; y = -((tdata[3] << 30 | tdata[2] << 22 | tdata[1] << 14) >> 19); @@ -259,8 +262,9 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda /* If requested, emulate a scroll wheel by detecting small * vertical touch motions. */ - if (emulate_scroll_wheel && (input->id.product != - USB_DEVICE_ID_APPLE_MAGICTRACKPAD2)) { + if (emulate_scroll_wheel && + input->id.product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 && + input->id.product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) { unsigned long now = jiffies; int step_x = msc->touches[id].scroll_x - x; int step_y = msc->touches[id].scroll_y - y; @@ -359,7 +363,9 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); - if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) + if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 || + input->id.product == + USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) input_report_abs(input, ABS_MT_PRESSURE, pressure); if (report_undeciphered) { @@ -367,7 +373,9 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) input_event(input, EV_MSC, MSC_RAW, tdata[7]); else if (input->id.product != - USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) + USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 && + input->id.product != + USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) input_event(input, EV_MSC, MSC_RAW, tdata[8]); } } @@ -493,7 +501,9 @@ static int magicmouse_raw_event(struct hid_device *hdev, magicmouse_emit_buttons(msc, clicks & 3); input_report_rel(input, REL_X, x); input_report_rel(input, REL_Y, y); - } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { + } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 || + input->id.product == + USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) { input_mt_sync_frame(input); input_report_key(input, BTN_MOUSE, clicks & 1); } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ @@ -545,7 +555,9 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd __set_bit(REL_WHEEL_HI_RES, input->relbit); __set_bit(REL_HWHEEL_HI_RES, input->relbit); } - } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { + } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 || + input->id.product == + USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) { /* If the trackpad has been connected to a Mac, the name is * automatically personalized, e.g., "José Expósito's Trackpad". * When connected through Bluetooth, the personalized name is @@ -556,9 +568,12 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd */ if (hdev->vendor == BT_VENDOR_ID_APPLE) { if (input->id.version == TRACKPAD2_2021_BT_VERSION) + input->name = "Apple Inc. Magic Trackpad 2021"; + else if (input->id.version == TRACKPAD_2024_BT_VERSION) { + input->name = "Apple Inc. Magic Trackpad USB-C"; + } else { input->name = "Apple Inc. Magic Trackpad"; - else - input->name = "Apple Inc. Magic Trackpad 2"; + } } else { /* USB_VENDOR_ID_APPLE */ input->name = hdev->name; } @@ -621,7 +636,9 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd MOUSE_RES_X); input_abs_set_res(input, ABS_MT_POSITION_Y, MOUSE_RES_Y); - } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { + } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 || + input->id.product == + USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) { input_set_abs_params(input, ABS_MT_PRESSURE, 0, 253, 0, 0); input_set_abs_params(input, ABS_PRESSURE, 0, 253, 0, 0); input_set_abs_params(input, ABS_MT_ORIENTATION, -3, 4, 0, 0); @@ -660,7 +677,8 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd input_set_events_per_packet(input, 60); if (report_undeciphered && - input->id.product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { + input->id.product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 && + input->id.product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) { __set_bit(EV_MSC, input->evbit); __set_bit(MSC_RAW, input->mscbit); } @@ -685,7 +703,9 @@ static int magicmouse_input_mapping(struct hid_device *hdev, /* Magic Trackpad does not give relative data after switching to MT */ if ((hi->input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD || - hi->input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) && + hi->input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 || + hi->input->id.product == + USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) && field->flags & HID_MAIN_ITEM_RELATIVE) return -1; @@ -721,7 +741,8 @@ static int magicmouse_enable_multitouch(struct hid_device *hdev) int ret; int feature_size; - if (hdev->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { + if (hdev->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 || + hdev->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) { if (hdev->vendor == BT_VENDOR_ID_APPLE) { feature_size = sizeof(feature_mt_trackpad2_bt); feature = feature_mt_trackpad2_bt; @@ -766,7 +787,8 @@ static int magicmouse_fetch_battery(struct hid_device *hdev) if (!hdev->battery || hdev->vendor != USB_VENDOR_ID_APPLE || (hdev->product != USB_DEVICE_ID_APPLE_MAGICMOUSE2 && - hdev->product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2)) + hdev->product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 && + hdev->product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC)) return -1; report_enum = &hdev->report_enum[hdev->battery_report_type]; @@ -835,7 +857,9 @@ static int magicmouse_probe(struct hid_device *hdev, if (id->vendor == USB_VENDOR_ID_APPLE && (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2 || - (id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 && hdev->type != HID_TYPE_USBMOUSE))) + ((id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 || + id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) && + hdev->type != HID_TYPE_USBMOUSE))) return 0; if (!msc->input) { @@ -850,7 +874,8 @@ static int magicmouse_probe(struct hid_device *hdev, else if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) report = hid_register_report(hdev, HID_INPUT_REPORT, MOUSE2_REPORT_ID, 0); - else if (id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { + else if (id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 || + id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) { if (id->vendor == BT_VENDOR_ID_APPLE) report = hid_register_report(hdev, HID_INPUT_REPORT, TRACKPAD2_BT_REPORT_ID, 0); @@ -907,8 +932,8 @@ static void magicmouse_remove(struct hid_device *hdev) hid_hw_stop(hdev); } -static __u8 *magicmouse_report_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) +static const __u8 *magicmouse_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) { /* * Change the usage from: @@ -920,7 +945,8 @@ static __u8 *magicmouse_report_fixup(struct hid_device *hdev, __u8 *rdesc, */ if (hdev->vendor == USB_VENDOR_ID_APPLE && (hdev->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2 || - hdev->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) && + hdev->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 || + hdev->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) && *rsize == 83 && rdesc[46] == 0x84 && rdesc[58] == 0x85) { hid_info(hdev, "fixing up magicmouse battery report descriptor\n"); @@ -951,6 +977,10 @@ static const struct hid_device_id magic_mice[] = { USB_DEVICE_ID_APPLE_MAGICTRACKPAD2), .driver_data = 0 }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD2), .driver_data = 0 }, + { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC), .driver_data = 0 }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC), .driver_data = 0 }, { } }; MODULE_DEVICE_TABLE(hid, magic_mice); @@ -968,4 +998,5 @@ static struct hid_driver magicmouse_driver = { }; module_hid_driver(magicmouse_driver); +MODULE_DESCRIPTION("Apple \"Magic\" Wireless Mouse driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-maltron.c b/drivers/hid/hid-maltron.c index dcd6db6a646e..f0aad1ba2e6d 100644 --- a/drivers/hid/hid-maltron.c +++ b/drivers/hid/hid-maltron.c @@ -22,7 +22,7 @@ #include "hid-ids.h" /* The original buggy USB descriptor */ -static u8 maltron_rdesc_o[] = { +static const u8 maltron_rdesc_o[] = { 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */ 0x09, 0x80, /* Usage (Sys Control) */ 0xA1, 0x01, /* Collection (Application) */ @@ -79,7 +79,7 @@ static u8 maltron_rdesc_o[] = { }; /* The patched descriptor, allowing media key events to be accepted as valid */ -static u8 maltron_rdesc[] = { +static const u8 maltron_rdesc[] = { 0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */ 0x09, 0x80, /* Usage (Sys Control) */ 0xA1, 0x01, /* Collection (Application) */ @@ -137,8 +137,8 @@ static u8 maltron_rdesc[] = { 0xC0 /* End Collection */ }; -static __u8 *maltron_report_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) +static const __u8 *maltron_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) { if (*rsize == sizeof(maltron_rdesc_o) && !memcmp(maltron_rdesc_o, rdesc, sizeof(maltron_rdesc_o))) { @@ -162,4 +162,5 @@ static struct hid_driver maltron_driver = { }; module_hid_driver(maltron_driver); +MODULE_DESCRIPTION("HID driver for Maltron L90"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-mcp2221.c b/drivers/hid/hid-mcp2221.c index da5ea5a23b08..0f93c22a479f 100644 --- a/drivers/hid/hid-mcp2221.c +++ b/drivers/hid/hid-mcp2221.c @@ -1048,7 +1048,7 @@ static int mcp_iio_channels(struct mcp2221 *mcp) break; default: continue; - }; + } chan->type = IIO_VOLTAGE; chan->indexed = 1; diff --git a/drivers/hid/hid-megaworld.c b/drivers/hid/hid-megaworld.c index 599657863cb9..0476d7d16e7f 100644 --- a/drivers/hid/hid-megaworld.c +++ b/drivers/hid/hid-megaworld.c @@ -122,4 +122,5 @@ static struct hid_driver mwctrl_driver = { }; module_hid_driver(mwctrl_driver); +MODULE_DESCRIPTION("Vibration support for Mega World controllers"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-mf.c b/drivers/hid/hid-mf.c index 92d7ecd41a78..49a4052a1496 100644 --- a/drivers/hid/hid-mf.c +++ b/drivers/hid/hid-mf.c @@ -166,4 +166,5 @@ static struct hid_driver mf_driver = { }; module_hid_driver(mf_driver); +MODULE_DESCRIPTION("Force feedback support for Mayflash game controller adapters."); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index 9345e2bfd56e..18ac21c0bcb2 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -56,7 +56,7 @@ struct xb1s_ff_report { __u8 loop_count; } __packed; -static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { struct ms_data *ms = hid_get_drvdata(hdev); @@ -475,4 +475,5 @@ static struct hid_driver ms_driver = { }; module_hid_driver(ms_driver); +MODULE_DESCRIPTION("HID driver for some microsoft \"special\" devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-monterey.c b/drivers/hid/hid-monterey.c index c63f9f1e61b8..3089be990afe 100644 --- a/drivers/hid/hid-monterey.c +++ b/drivers/hid/hid-monterey.c @@ -18,7 +18,7 @@ #include "hid-ids.h" -static __u8 *mr_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *mr_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { if (*rsize >= 31 && rdesc[29] == 0x05 && rdesc[30] == 0x09) { @@ -62,4 +62,5 @@ static struct hid_driver mr_driver = { }; module_hid_driver(mr_driver); +MODULE_DESCRIPTION("HID driver for some monterey \"special\" devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 04a014cd2a2f..e50887a6d22c 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -31,6 +31,7 @@ * [1] https://gitlab.freedesktop.org/libevdev/hid-tools */ +#include <linux/bits.h> #include <linux/device.h> #include <linux/hid.h> #include <linux/module.h> @@ -83,6 +84,13 @@ enum latency_mode { HID_LATENCY_HIGH = 1, }; +enum report_mode { + TOUCHPAD_REPORT_NONE = 0, + TOUCHPAD_REPORT_BUTTONS = BIT(0), + TOUCHPAD_REPORT_CONTACTS = BIT(1), + TOUCHPAD_REPORT_ALL = TOUCHPAD_REPORT_BUTTONS | TOUCHPAD_REPORT_CONTACTS, +}; + #define MT_IO_FLAGS_RUNNING 0 #define MT_IO_FLAGS_ACTIVE_SLOTS 1 #define MT_IO_FLAGS_PENDING_SLOTS 2 @@ -212,6 +220,7 @@ static void mt_post_parse(struct mt_device *td, struct mt_application *app); #define MT_CLS_GOOGLE 0x0111 #define MT_CLS_RAZER_BLADE_STEALTH 0x0112 #define MT_CLS_SMART_TECH 0x0113 +#define MT_CLS_SIS 0x0457 #define MT_DEFAULT_MAXCONTACT 10 #define MT_MAX_MAXCONTACT 250 @@ -396,6 +405,11 @@ static const struct mt_class mt_classes[] = { MT_QUIRK_CONTACT_CNT_ACCURATE | MT_QUIRK_SEPARATE_APP_REPORT, }, + { .name = MT_CLS_SIS, + .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP | + MT_QUIRK_ALWAYS_VALID | + MT_QUIRK_CONTACT_CNT_ACCURATE, + }, { } }; @@ -1441,6 +1455,30 @@ static int mt_event(struct hid_device *hid, struct hid_field *field, return 0; } +static const __u8 *mt_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *size) +{ + if (hdev->vendor == I2C_VENDOR_ID_GOODIX && + (hdev->product == I2C_DEVICE_ID_GOODIX_01E8 || + hdev->product == I2C_DEVICE_ID_GOODIX_01E9)) { + if (rdesc[607] == 0x15) { + rdesc[607] = 0x25; + dev_info( + &hdev->dev, + "GT7868Q report descriptor fixup is applied.\n"); + } else { + dev_info( + &hdev->dev, + "The byte is not expected for fixing the report descriptor. \ +It's possible that the touchpad firmware is not suitable for applying the fix. \ +got: %x\n", + rdesc[607]); + } + } + + return rdesc; +} + static void mt_report(struct hid_device *hid, struct hid_report *report) { struct mt_device *td = hid_get_drvdata(hid); @@ -1462,8 +1500,7 @@ static bool mt_need_to_apply_feature(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, enum latency_mode latency, - bool surface_switch, - bool button_switch, + enum report_mode report_mode, bool *inputmode_found) { struct mt_device *td = hid_get_drvdata(hdev); @@ -1518,11 +1555,11 @@ static bool mt_need_to_apply_feature(struct hid_device *hdev, return true; case HID_DG_SURFACESWITCH: - field->value[index] = surface_switch; + field->value[index] = !!(report_mode & TOUCHPAD_REPORT_CONTACTS); return true; case HID_DG_BUTTONSWITCH: - field->value[index] = button_switch; + field->value[index] = !!(report_mode & TOUCHPAD_REPORT_BUTTONS); return true; } @@ -1530,7 +1567,7 @@ static bool mt_need_to_apply_feature(struct hid_device *hdev, } static void mt_set_modes(struct hid_device *hdev, enum latency_mode latency, - bool surface_switch, bool button_switch) + enum report_mode report_mode) { struct hid_report_enum *rep_enum; struct hid_report *rep; @@ -1555,8 +1592,7 @@ static void mt_set_modes(struct hid_device *hdev, enum latency_mode latency, rep->field[i], usage, latency, - surface_switch, - button_switch, + report_mode, &inputmode_found)) update_report = true; } @@ -1643,9 +1679,12 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi) break; } - if (suffix) + if (suffix) { hi->input->name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "%s %s", hdev->name, suffix); + if (!hi->input->name) + return -ENOMEM; + } return 0; } @@ -1787,6 +1826,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) if (mtclass->quirks & MT_QUIRK_FIX_CONST_CONTACT_ID) mt_fix_const_fields(hdev, HID_DG_CONTACTID); + if (hdev->vendor == USB_VENDOR_ID_SIS_TOUCH) + hdev->quirks |= HID_QUIRK_NOGET; + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) return ret; @@ -1796,7 +1838,7 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) dev_warn(&hdev->dev, "Cannot allocate sysfs group for %s\n", hdev->name); - mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true); + mt_set_modes(hdev, HID_LATENCY_NORMAL, TOUCHPAD_REPORT_ALL); return 0; } @@ -1808,9 +1850,9 @@ static int mt_suspend(struct hid_device *hdev, pm_message_t state) /* High latency is desirable for power savings during S3/S0ix */ if ((td->mtclass.quirks & MT_QUIRK_DISABLE_WAKEUP) || !hid_hw_may_wakeup(hdev)) - mt_set_modes(hdev, HID_LATENCY_HIGH, false, false); + mt_set_modes(hdev, HID_LATENCY_HIGH, TOUCHPAD_REPORT_NONE); else - mt_set_modes(hdev, HID_LATENCY_HIGH, true, true); + mt_set_modes(hdev, HID_LATENCY_HIGH, TOUCHPAD_REPORT_ALL); return 0; } @@ -1818,7 +1860,7 @@ static int mt_suspend(struct hid_device *hdev, pm_message_t state) static int mt_reset_resume(struct hid_device *hdev) { mt_release_contacts(hdev); - mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true); + mt_set_modes(hdev, HID_LATENCY_NORMAL, TOUCHPAD_REPORT_ALL); return 0; } @@ -1830,7 +1872,7 @@ static int mt_resume(struct hid_device *hdev) hid_hw_idle(hdev, 0, 0, HID_REQ_SET_IDLE); - mt_set_modes(hdev, HID_LATENCY_NORMAL, true, true); + mt_set_modes(hdev, HID_LATENCY_NORMAL, TOUCHPAD_REPORT_ALL); return 0; } @@ -1992,6 +2034,10 @@ static const struct hid_device_id mt_devices[] = { HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8, USB_VENDOR_ID_ELAN, 0x3148) }, + { .driver_data = MT_CLS_WIN_8_FORCE_MULTI_INPUT_NSMU, + HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8, + USB_VENDOR_ID_ELAN, 0x32ae) }, + /* Elitegroup panel */ { .driver_data = MT_CLS_SERIAL, MT_USB_DEVICE(USB_VENDOR_ID_ELITEGROUP, @@ -2035,6 +2081,14 @@ static const struct hid_device_id mt_devices[] = { MT_BT_DEVICE(USB_VENDOR_ID_FRUCTEL, USB_DEVICE_ID_GAMETEL_MT_MODE) }, + /* Goodix GT7868Q devices */ + { .driver_data = MT_CLS_WIN_8_FORCE_MULTI_INPUT_NSMU, + HID_DEVICE(BUS_I2C, HID_GROUP_ANY, I2C_VENDOR_ID_GOODIX, + I2C_DEVICE_ID_GOODIX_01E8) }, + { .driver_data = MT_CLS_WIN_8_FORCE_MULTI_INPUT_NSMU, + HID_DEVICE(BUS_I2C, HID_GROUP_ANY, I2C_VENDOR_ID_GOODIX, + I2C_DEVICE_ID_GOODIX_01E9) }, + /* GoodTouch panels */ { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_GOODTOUCH, @@ -2050,6 +2104,11 @@ static const struct hid_device_id mt_devices[] = { HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8, 0x347d, 0x7853) }, + /* HONOR MagicBook Art 14 touchpad */ + { .driver_data = MT_CLS_VTL, + HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8, + 0x35cc, 0x0104) }, + /* Ilitek dual touch panel */ { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_ILITEK, @@ -2081,6 +2140,22 @@ static const struct hid_device_id mt_devices[] = { USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X12_TAB) }, + /* Lenovo X12 TAB Gen 2 */ + { .driver_data = MT_CLS_WIN_8_FORCE_MULTI_INPUT_NSMU, + HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8, + USB_VENDOR_ID_LENOVO, + USB_DEVICE_ID_LENOVO_X12_TAB2) }, + + /* Logitech devices */ + { .driver_data = MT_CLS_NSMU, + HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_MULTITOUCH_WIN_8, + USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_LOGITECH_CASA_TOUCHPAD) }, + { .driver_data = MT_CLS_WIN_8_FORCE_MULTI_INPUT_NSMU, + HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8, + USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_LOGITECH_BOLT_RECEIVER) }, + /* MosArt panels */ { .driver_data = MT_CLS_CONFIDENCE_MINUS_ONE, MT_USB_DEVICE(USB_VENDOR_ID_ASUS, @@ -2237,6 +2312,16 @@ static const struct hid_device_id mt_devices[] = { HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8, USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_WHISKERS) }, + /* sis */ + { .driver_data = MT_CLS_SIS, + HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_SIS_TOUCH, + HID_ANY_ID) }, + + /* Hantick */ + { .driver_data = MT_CLS_NSMU, + HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8, + I2C_VENDOR_ID_HANTICK, I2C_PRODUCT_ID_HANTICK_5288) }, + /* Generic MT device */ { HID_DEVICE(HID_BUS_ANY, HID_GROUP_MULTITOUCH, HID_ANY_ID, HID_ANY_ID) }, @@ -2264,6 +2349,7 @@ static struct hid_driver mt_driver = { .feature_mapping = mt_feature_mapping, .usage_table = mt_grabbed_usages, .event = mt_event, + .report_fixup = mt_report_fixup, .report = mt_report, .suspend = pm_ptr(mt_suspend), .reset_resume = pm_ptr(mt_reset_resume), diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c index 80e0f23c1c33..11ac246176ae 100644 --- a/drivers/hid/hid-nintendo.c +++ b/drivers/hid/hid-nintendo.c @@ -29,11 +29,12 @@ */ #include "hid-ids.h" -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/hid.h> +#include <linux/idr.h> #include <linux/input.h> #include <linux/jiffies.h> #include <linux/leds.h> @@ -455,14 +456,13 @@ static const struct joycon_ctlr_button_mapping snescon_button_mappings[] = { { /* sentinel */ }, }; -/* - * "A", "B", and "C" are mapped positionally, rather than by label (e.g., "A" - * gets assigned to BTN_EAST instead of BTN_A). - */ static const struct joycon_ctlr_button_mapping gencon_button_mappings[] = { - { BTN_SOUTH, JC_BTN_A, }, - { BTN_EAST, JC_BTN_B, }, - { BTN_WEST, JC_BTN_R, }, + { BTN_A, JC_BTN_A, }, + { BTN_B, JC_BTN_B, }, + { BTN_C, JC_BTN_R, }, + { BTN_X, JC_BTN_X, }, /* MD/GEN 6B Only */ + { BTN_Y, JC_BTN_Y, }, /* MD/GEN 6B Only */ + { BTN_Z, JC_BTN_L, }, /* MD/GEN 6B Only */ { BTN_SELECT, JC_BTN_ZR, }, { BTN_START, JC_BTN_PLUS, }, { BTN_MODE, JC_BTN_HOME, }, @@ -470,9 +470,6 @@ static const struct joycon_ctlr_button_mapping gencon_button_mappings[] = { { /* sentinel */ }, }; -/* - * N64's C buttons get assigned to d-pad directions and registered as buttons. - */ static const struct joycon_ctlr_button_mapping n64con_button_mappings[] = { { BTN_A, JC_BTN_A, }, { BTN_B, JC_BTN_B, }, @@ -569,6 +566,7 @@ static const enum led_brightness joycon_player_led_patterns[JC_NUM_LED_PATTERNS] struct joycon_ctlr { struct hid_device *hdev; struct input_dev *input; + u32 player_id; struct led_classdev leds[JC_NUM_LEDS]; /* player leds */ struct led_classdev home_led; enum joycon_ctlr_state ctlr_state; @@ -656,7 +654,6 @@ struct joycon_ctlr { (ctlr->ctlr_type == JOYCON_CTLR_TYPE_JCR || \ ctlr->ctlr_type == JOYCON_CTLR_TYPE_PRO) - /* * Controller device helpers * @@ -667,40 +664,11 @@ struct joycon_ctlr { * These helpers are most useful early during the HID probe or in conjunction * with the capability helpers below. */ -static inline bool joycon_device_is_procon(struct joycon_ctlr *ctlr) -{ - return ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_PROCON; -} - static inline bool joycon_device_is_chrggrip(struct joycon_ctlr *ctlr) { return ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_CHRGGRIP; } -static inline bool joycon_device_is_snescon(struct joycon_ctlr *ctlr) -{ - return ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_SNESCON; -} - -static inline bool joycon_device_is_gencon(struct joycon_ctlr *ctlr) -{ - return ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_GENCON; -} - -static inline bool joycon_device_is_n64con(struct joycon_ctlr *ctlr) -{ - return ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_N64CON; -} - -static inline bool joycon_device_has_usb(struct joycon_ctlr *ctlr) -{ - return joycon_device_is_procon(ctlr) || - joycon_device_is_chrggrip(ctlr) || - joycon_device_is_snescon(ctlr) || - joycon_device_is_gencon(ctlr) || - joycon_device_is_n64con(ctlr); -} - /* * Controller type helpers * @@ -2261,7 +2229,8 @@ static int joycon_home_led_brightness_set(struct led_classdev *led, return ret; } -static DEFINE_SPINLOCK(joycon_input_num_spinlock); +static DEFINE_IDA(nintendo_player_id_allocator); + static int joycon_leds_create(struct joycon_ctlr *ctlr) { struct hid_device *hdev = ctlr->hdev; @@ -2272,20 +2241,19 @@ static int joycon_leds_create(struct joycon_ctlr *ctlr) char *name; int ret; int i; - unsigned long flags; int player_led_pattern; - static int input_num; - - /* - * Set the player leds based on controller number - * Because there is no standard concept of "player number", the pattern - * number will simply increase by 1 every time a controller is connected. - */ - spin_lock_irqsave(&joycon_input_num_spinlock, flags); - player_led_pattern = input_num++ % JC_NUM_LED_PATTERNS; - spin_unlock_irqrestore(&joycon_input_num_spinlock, flags); /* configure the player LEDs */ + ctlr->player_id = U32_MAX; + ret = ida_alloc(&nintendo_player_id_allocator, GFP_KERNEL); + if (ret < 0) { + hid_warn(hdev, "Failed to allocate player ID, skipping; ret=%d\n", ret); + goto home_led; + } + ctlr->player_id = ret; + player_led_pattern = ret % JC_NUM_LED_PATTERNS; + hid_info(ctlr->hdev, "assigned player %d led pattern", player_led_pattern + 1); + for (i = 0; i < JC_NUM_LEDS; i++) { name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s:%s", d_name, @@ -2501,8 +2469,11 @@ static int joycon_init(struct hid_device *hdev) /* set baudrate for improved latency */ ret = joycon_send_usb(ctlr, JC_USB_CMD_BAUDRATE_3M, HZ); if (ret) { - hid_err(hdev, "Failed to set baudrate; ret=%d\n", ret); - goto out_unlock; + /* + * We can function with the default baudrate. + * Provide a warning, and continue on. + */ + hid_warn(hdev, "Failed to set baudrate (ret=%d), continuing anyway\n", ret); } /* handshake */ ret = joycon_send_usb(ctlr, JC_USB_CMD_HANDSHAKE, HZ); @@ -2729,13 +2700,13 @@ static int nintendo_hid_probe(struct hid_device *hdev, ret = joycon_power_supply_create(ctlr); if (ret) { hid_err(hdev, "Failed to create power_supply; ret=%d\n", ret); - goto err_close; + goto err_ida; } ret = joycon_input_create(ctlr); if (ret) { hid_err(hdev, "Failed to create input device; ret=%d\n", ret); - goto err_close; + goto err_ida; } ctlr->ctlr_state = JOYCON_CTLR_STATE_READ; @@ -2743,6 +2714,8 @@ static int nintendo_hid_probe(struct hid_device *hdev, hid_dbg(hdev, "probe - success\n"); return 0; +err_ida: + ida_free(&nintendo_player_id_allocator, ctlr->player_id); err_close: hid_hw_close(hdev); err_stop: @@ -2767,6 +2740,7 @@ static void nintendo_hid_remove(struct hid_device *hdev) spin_unlock_irqrestore(&ctlr->lock, flags); destroy_workqueue(ctlr->rumble_queue); + ida_free(&nintendo_player_id_allocator, ctlr->player_id); hid_hw_close(hdev); hid_hw_stop(hdev); @@ -2824,7 +2798,19 @@ static struct hid_driver nintendo_hid_driver = { .resume = nintendo_hid_resume, #endif }; -module_hid_driver(nintendo_hid_driver); +static int __init nintendo_init(void) +{ + return hid_register_driver(&nintendo_hid_driver); +} + +static void __exit nintendo_exit(void) +{ + hid_unregister_driver(&nintendo_hid_driver); + ida_destroy(&nintendo_player_id_allocator); +} + +module_init(nintendo_init); +module_exit(nintendo_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ryan McClelland <rymcclel@gmail.com>"); diff --git a/drivers/hid/hid-nti.c b/drivers/hid/hid-nti.c index 1952e9ca5f45..03f7dd491228 100644 --- a/drivers/hid/hid-nti.c +++ b/drivers/hid/hid-nti.c @@ -29,7 +29,7 @@ MODULE_DESCRIPTION("HID driver for Network Technologies USB-SUN keyboard adapter /* * NTI Sun keyboard adapter has wrong logical maximum in report descriptor */ -static __u8 *nti_usbsun_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *nti_usbsun_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { if (*rsize >= 60 && rdesc[53] == 0x65 && rdesc[59] == 0x65) { diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index b5d26f03fe6b..2738ce947434 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -1029,4 +1029,5 @@ static struct hid_driver ntrig_driver = { }; module_hid_driver(ntrig_driver); +MODULE_DESCRIPTION("HID driver for N-Trig touchscreens"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-nvidia-shield.c b/drivers/hid/hid-nvidia-shield.c index 58b15750dbb0..ff9078ad1961 100644 --- a/drivers/hid/hid-nvidia-shield.c +++ b/drivers/hid/hid-nvidia-shield.c @@ -283,7 +283,9 @@ static struct input_dev *shield_haptics_create( return haptics; input_set_capability(haptics, EV_FF, FF_RUMBLE); - input_ff_create_memless(haptics, NULL, play_effect); + ret = input_ff_create_memless(haptics, NULL, play_effect); + if (ret) + goto err; ret = input_register_device(haptics); if (ret) diff --git a/drivers/hid/hid-ortek.c b/drivers/hid/hid-ortek.c index 9a4770d79c64..f27297269a7f 100644 --- a/drivers/hid/hid-ortek.c +++ b/drivers/hid/hid-ortek.c @@ -22,7 +22,7 @@ #include "hid-ids.h" -static __u8 *ortek_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *ortek_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { if (*rsize >= 56 && rdesc[54] == 0x25 && rdesc[55] == 0x01) { @@ -51,4 +51,5 @@ static struct hid_driver ortek_driver = { }; module_hid_driver(ortek_driver); +MODULE_DESCRIPTION("HID driver for Ortek PKB-1700/WKB-2000/Skycable wireless keyboard and mouse trackpad"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-petalynx.c b/drivers/hid/hid-petalynx.c index ea0af9f7ad90..1a986f077ce1 100644 --- a/drivers/hid/hid-petalynx.c +++ b/drivers/hid/hid-petalynx.c @@ -19,7 +19,7 @@ #include "hid-ids.h" /* Petalynx Maxter Remote has maximum for consumer page set too low */ -static __u8 *pl_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *pl_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { if (*rsize >= 62 && rdesc[39] == 0x2a && rdesc[40] == 0xf5 && @@ -102,4 +102,5 @@ static struct hid_driver pl_driver = { }; module_hid_driver(pl_driver); +MODULE_DESCRIPTION("HID driver for some petalynx \"special\" devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-picolcd_backlight.c b/drivers/hid/hid-picolcd_backlight.c index 5bd2a8c4bbd6..4b43b64537a3 100644 --- a/drivers/hid/hid-picolcd_backlight.c +++ b/drivers/hid/hid-picolcd_backlight.c @@ -9,7 +9,6 @@ #include <linux/hid.h> -#include <linux/fb.h> #include <linux/backlight.h> #include "hid-picolcd.h" @@ -32,22 +31,17 @@ static int picolcd_set_brightness(struct backlight_device *bdev) data->lcd_brightness = bdev->props.brightness & 0x0ff; data->lcd_power = bdev->props.power; spin_lock_irqsave(&data->lock, flags); - hid_set_field(report->field[0], 0, data->lcd_power == FB_BLANK_UNBLANK ? data->lcd_brightness : 0); + hid_set_field(report->field[0], 0, + data->lcd_power == BACKLIGHT_POWER_ON ? data->lcd_brightness : 0); if (!(data->status & PICOLCD_FAILED)) hid_hw_request(data->hdev, report, HID_REQ_SET_REPORT); spin_unlock_irqrestore(&data->lock, flags); return 0; } -static int picolcd_check_bl_fb(struct backlight_device *bdev, struct fb_info *fb) -{ - return fb && fb == picolcd_fbinfo((struct picolcd_data *)bl_get_data(bdev)); -} - static const struct backlight_ops picolcd_blops = { .update_status = picolcd_set_brightness, .get_brightness = picolcd_get_brightness, - .check_fb = picolcd_check_bl_fb, }; int picolcd_init_backlight(struct picolcd_data *data, struct hid_report *report) @@ -101,7 +95,7 @@ void picolcd_suspend_backlight(struct picolcd_data *data) if (!data->backlight) return; - data->backlight->props.power = FB_BLANK_POWERDOWN; + data->backlight->props.power = BACKLIGHT_POWER_OFF; picolcd_set_brightness(data->backlight); data->lcd_power = data->backlight->props.power = bl_power; } diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c index bbda231a7ce3..297103be3381 100644 --- a/drivers/hid/hid-picolcd_core.c +++ b/drivers/hid/hid-picolcd_core.c @@ -256,9 +256,9 @@ static ssize_t picolcd_operation_mode_show(struct device *dev, struct picolcd_data *data = dev_get_drvdata(dev); if (data->status & PICOLCD_BOOTLOADER) - return snprintf(buf, PAGE_SIZE, "[bootloader] lcd\n"); + return sysfs_emit(buf, "[bootloader] lcd\n"); else - return snprintf(buf, PAGE_SIZE, "bootloader [lcd]\n"); + return sysfs_emit(buf, "bootloader [lcd]\n"); } static ssize_t picolcd_operation_mode_store(struct device *dev, @@ -301,7 +301,7 @@ static ssize_t picolcd_operation_mode_delay_show(struct device *dev, { struct picolcd_data *data = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%hu\n", data->opmode_delay); + return sysfs_emit(buf, "%hu\n", data->opmode_delay); } static ssize_t picolcd_operation_mode_delay_store(struct device *dev, @@ -474,11 +474,6 @@ static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data) if (error) goto err; - /* Set up the framebuffer device */ - error = picolcd_init_framebuffer(data); - if (error) - goto err; - /* Setup lcd class device */ error = picolcd_init_lcd(data, picolcd_out_report(REPORT_CONTRAST, hdev)); if (error) @@ -489,6 +484,11 @@ static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data) if (error) goto err; + /* Set up the framebuffer device */ + error = picolcd_init_framebuffer(data); + if (error) + goto err; + /* Setup the LED class devices */ error = picolcd_init_leds(data, picolcd_out_report(REPORT_LED_STATE, hdev)); if (error) @@ -502,9 +502,9 @@ static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data) return 0; err: picolcd_exit_leds(data); + picolcd_exit_framebuffer(data); picolcd_exit_backlight(data); picolcd_exit_lcd(data); - picolcd_exit_framebuffer(data); picolcd_exit_cir(data); picolcd_exit_keys(data); return error; @@ -623,9 +623,9 @@ static void picolcd_remove(struct hid_device *hdev) /* Cleanup LED */ picolcd_exit_leds(data); /* Clean up the framebuffer */ + picolcd_exit_framebuffer(data); picolcd_exit_backlight(data); picolcd_exit_lcd(data); - picolcd_exit_framebuffer(data); /* Cleanup input */ picolcd_exit_cir(data); picolcd_exit_keys(data); diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c index d7dddd99d325..8c28e982e09d 100644 --- a/drivers/hid/hid-picolcd_fb.c +++ b/drivers/hid/hid-picolcd_fb.c @@ -296,7 +296,7 @@ static void picolcd_fb_destroy(struct fb_info *info) /* make sure no work is deferred */ fb_deferred_io_cleanup(info); - /* No thridparty should ever unregister our framebuffer! */ + /* No thirdparty should ever unregister our framebuffer! */ WARN_ON(fbdata->picolcd != NULL); vfree((u8 *)info->fix.smem_start); @@ -421,12 +421,10 @@ static ssize_t picolcd_fb_update_rate_show(struct device *dev, size_t ret = 0; for (i = 1; i <= PICOLCDFB_UPDATE_RATE_LIMIT; i++) - if (ret >= PAGE_SIZE) - break; - else if (i == fb_update_rate) - ret += scnprintf(buf+ret, PAGE_SIZE-ret, "[%u] ", i); + if (i == fb_update_rate) + ret += sysfs_emit_at(buf, ret, "[%u] ", i); else - ret += scnprintf(buf+ret, PAGE_SIZE-ret, "%u ", i); + ret += sysfs_emit_at(buf, ret, "%u ", i); if (ret > 0) buf[min(ret, (size_t)PAGE_SIZE)-1] = '\n'; return ret; @@ -493,6 +491,16 @@ int picolcd_init_framebuffer(struct picolcd_data *data) info->fix = picolcdfb_fix; info->fix.smem_len = PICOLCDFB_SIZE*8; +#ifdef CONFIG_FB_BACKLIGHT +#ifdef CONFIG_HID_PICOLCD_BACKLIGHT + info->bl_dev = data->backlight; +#endif +#endif + +#ifdef CONFIG_HID_PICOLCD_LCD + info->lcd_dev = data->lcd; +#endif + fbdata = info->par; spin_lock_init(&fbdata->lock); fbdata->picolcd = data; diff --git a/drivers/hid/hid-picolcd_lcd.c b/drivers/hid/hid-picolcd_lcd.c index 0c4b76de8ae5..318f19eac0e7 100644 --- a/drivers/hid/hid-picolcd_lcd.c +++ b/drivers/hid/hid-picolcd_lcd.c @@ -41,15 +41,9 @@ static int picolcd_set_contrast(struct lcd_device *ldev, int contrast) return 0; } -static int picolcd_check_lcd_fb(struct lcd_device *ldev, struct fb_info *fb) -{ - return fb && fb == picolcd_fbinfo((struct picolcd_data *)lcd_get_data(ldev)); -} - -static struct lcd_ops picolcd_lcdops = { +static const struct lcd_ops picolcd_lcdops = { .get_contrast = picolcd_get_contrast, .set_contrast = picolcd_set_contrast, - .check_fb = picolcd_check_lcd_fb, }; int picolcd_init_lcd(struct picolcd_data *data, struct hid_report *report) diff --git a/drivers/hid/hid-pl.c b/drivers/hid/hid-pl.c index 93fb07ec3180..3c8827081dea 100644 --- a/drivers/hid/hid-pl.c +++ b/drivers/hid/hid-pl.c @@ -219,4 +219,5 @@ static struct hid_driver pl_driver = { }; module_hid_driver(pl_driver); +MODULE_DESCRIPTION("Force feedback support for PantherLord/GreenAsia based devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-plantronics.c b/drivers/hid/hid-plantronics.c index 3d414ae194ac..25cfd964dc25 100644 --- a/drivers/hid/hid-plantronics.c +++ b/drivers/hid/hid-plantronics.c @@ -38,8 +38,10 @@ (usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) #define PLT_QUIRK_DOUBLE_VOLUME_KEYS BIT(0) +#define PLT_QUIRK_FOLLOWED_OPPOSITE_VOLUME_KEYS BIT(1) #define PLT_DOUBLE_KEY_TIMEOUT 5 /* ms */ +#define PLT_FOLLOWED_OPPOSITE_KEY_TIMEOUT 220 /* ms */ struct plt_drv_data { unsigned long device_type; @@ -137,6 +139,21 @@ static int plantronics_event(struct hid_device *hdev, struct hid_field *field, drv_data->last_volume_key_ts = cur_ts; } + if (drv_data->quirks & PLT_QUIRK_FOLLOWED_OPPOSITE_VOLUME_KEYS) { + unsigned long prev_ts, cur_ts; + + /* Usages are filtered in plantronics_usages. */ + + if (!value) /* Handle key presses only. */ + return 0; + + prev_ts = drv_data->last_volume_key_ts; + cur_ts = jiffies; + if (jiffies_to_msecs(cur_ts - prev_ts) <= PLT_FOLLOWED_OPPOSITE_KEY_TIMEOUT) + return 1; /* Ignore the followed opposite volume key. */ + + drv_data->last_volume_key_ts = cur_ts; + } return 0; } @@ -210,6 +227,12 @@ static const struct hid_device_id plantronics_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS, USB_DEVICE_ID_PLANTRONICS_BLACKWIRE_3225_SERIES), .driver_data = PLT_QUIRK_DOUBLE_VOLUME_KEYS }, + { HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS, + USB_DEVICE_ID_PLANTRONICS_BLACKWIRE_3325_SERIES), + .driver_data = PLT_QUIRK_FOLLOWED_OPPOSITE_VOLUME_KEYS }, + { HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS, + USB_DEVICE_ID_PLANTRONICS_ENCOREPRO_500_SERIES), + .driver_data = PLT_QUIRK_FOLLOWED_OPPOSITE_VOLUME_KEYS }, { HID_USB_DEVICE(USB_VENDOR_ID_PLANTRONICS, HID_ANY_ID) }, { } }; diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index 8ac8f7b8e317..1468fb11e39d 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -15,7 +15,7 @@ #include <linux/led-class-multicolor.h> #include <linux/module.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "hid-ids.h" @@ -27,6 +27,11 @@ static DEFINE_IDA(ps_player_id_allocator); #define HID_PLAYSTATION_VERSION_PATCH 0x8000 +enum PS_TYPE { + PS_TYPE_PS4_DUALSHOCK4, + PS_TYPE_PS5_DUALSENSE, +}; + /* Base class for playstation devices. */ struct ps_device { struct list_head list; @@ -287,6 +292,8 @@ struct dualsense_output_report { #define DS4_INPUT_REPORT_USB 0x01 #define DS4_INPUT_REPORT_USB_SIZE 64 +#define DS4_INPUT_REPORT_BT_MINIMAL 0x01 +#define DS4_INPUT_REPORT_BT_MINIMAL_SIZE 10 #define DS4_INPUT_REPORT_BT 0x11 #define DS4_INPUT_REPORT_BT_SIZE 78 #define DS4_OUTPUT_REPORT_USB 0x05 @@ -1778,8 +1785,10 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) int retries; buf = kzalloc(DS4_FEATURE_REPORT_CALIBRATION_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; + if (!buf) { + ret = -ENOMEM; + goto transfer_failed; + } /* We should normally receive the feature report data we asked * for, but hidraw applications such as Steam can issue feature @@ -1796,26 +1805,30 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) continue; } - hid_err(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret); + hid_warn(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret); ret = -EILSEQ; - goto err_free; + goto transfer_failed; } else { break; } } } else { /* Bluetooth */ buf = kzalloc(DS4_FEATURE_REPORT_CALIBRATION_BT_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; + if (!buf) { + ret = -ENOMEM; + goto transfer_failed; + } ret = ps_get_report(hdev, DS4_FEATURE_REPORT_CALIBRATION_BT, buf, DS4_FEATURE_REPORT_CALIBRATION_BT_SIZE, true); + if (ret) { - hid_err(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret); - goto err_free; + hid_warn(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret); + goto transfer_failed; } } + /* Transfer succeeded - parse the calibration data received. */ gyro_pitch_bias = get_unaligned_le16(&buf[1]); gyro_yaw_bias = get_unaligned_le16(&buf[3]); gyro_roll_bias = get_unaligned_le16(&buf[5]); @@ -1844,6 +1857,9 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) acc_z_plus = get_unaligned_le16(&buf[31]); acc_z_minus = get_unaligned_le16(&buf[33]); + /* Done parsing the buffer, so let's free it. */ + kfree(buf); + /* * Set gyroscope calibration and normalization parameters. * Data values will be normalized to 1/DS4_GYRO_RES_PER_DEG_S degree/s. @@ -1868,21 +1884,6 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) abs(gyro_roll_minus - gyro_roll_bias); /* - * Sanity check gyro calibration data. This is needed to prevent crashes - * during report handling of virtual, clone or broken devices not implementing - * calibration data properly. - */ - for (i = 0; i < ARRAY_SIZE(ds4->gyro_calib_data); i++) { - if (ds4->gyro_calib_data[i].sens_denom == 0) { - hid_warn(hdev, "Invalid gyro calibration data for axis (%d), disabling calibration.", - ds4->gyro_calib_data[i].abs_code); - ds4->gyro_calib_data[i].bias = 0; - ds4->gyro_calib_data[i].sens_numer = DS4_GYRO_RANGE; - ds4->gyro_calib_data[i].sens_denom = S16_MAX; - } - } - - /* * Set accelerometer calibration and normalization parameters. * Data values will be normalized to 1/DS4_ACC_RES_PER_G g. */ @@ -1904,6 +1905,23 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) ds4->accel_calib_data[2].sens_numer = 2*DS4_ACC_RES_PER_G; ds4->accel_calib_data[2].sens_denom = range_2g; +transfer_failed: + /* + * Sanity check gyro calibration data. This is needed to prevent crashes + * during report handling of virtual, clone or broken devices not implementing + * calibration data properly. + */ + for (i = 0; i < ARRAY_SIZE(ds4->gyro_calib_data); i++) { + if (ds4->gyro_calib_data[i].sens_denom == 0) { + ds4->gyro_calib_data[i].abs_code = ABS_RX + i; + hid_warn(hdev, "Invalid gyro calibration data for axis (%d), disabling calibration.", + ds4->gyro_calib_data[i].abs_code); + ds4->gyro_calib_data[i].bias = 0; + ds4->gyro_calib_data[i].sens_numer = DS4_GYRO_RANGE; + ds4->gyro_calib_data[i].sens_denom = S16_MAX; + } + } + /* * Sanity check accelerometer calibration data. This is needed to prevent crashes * during report handling of virtual, clone or broken devices not implementing calibration @@ -1911,6 +1929,7 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) */ for (i = 0; i < ARRAY_SIZE(ds4->accel_calib_data); i++) { if (ds4->accel_calib_data[i].sens_denom == 0) { + ds4->accel_calib_data[i].abs_code = ABS_X + i; hid_warn(hdev, "Invalid accelerometer calibration data for axis (%d), disabling calibration.", ds4->accel_calib_data[i].abs_code); ds4->accel_calib_data[i].bias = 0; @@ -1919,8 +1938,6 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) } } -err_free: - kfree(buf); return ret; } @@ -2037,8 +2054,9 @@ static int dualshock4_led_set_blink(struct led_classdev *led, unsigned long *del dualshock4_schedule_work(ds4); - *delay_on = ds4->lightbar_blink_on; - *delay_off = ds4->lightbar_blink_off; + /* Report scaled values back to LED subsystem */ + *delay_on = ds4->lightbar_blink_on * 10; + *delay_off = ds4->lightbar_blink_off * 10; return 0; } @@ -2065,6 +2083,13 @@ static int dualshock4_led_set_brightness(struct led_classdev *led, enum led_brig break; case 3: ds4->lightbar_enabled = !!value; + + /* brightness = 0 also cancels blinking in Linux. */ + if (!ds4->lightbar_enabled) { + ds4->lightbar_blink_off = 0; + ds4->lightbar_blink_on = 0; + ds4->update_lightbar_blink = true; + } } ds4->update_lightbar = true; @@ -2118,6 +2143,26 @@ static void dualshock4_output_worker(struct work_struct *work) spin_lock_irqsave(&ds4->base.lock, flags); + /* + * Some 3rd party gamepads expect updates to rumble and lightbar + * together, and setting one may cancel the other. + * + * Let's maximise compatibility by always sending rumble and lightbar + * updates together, even when only one has been scheduled, resulting + * in: + * + * ds4->valid_flag0 >= 0x03 + * + * Hopefully this will maximise compatibility with third-party pads. + * + * Any further update bits, such as 0x04 for lightbar blinking, will + * be or'd on top of this like before. + */ + if (ds4->update_rumble || ds4->update_lightbar) { + ds4->update_rumble = true; /* 0x01 */ + ds4->update_lightbar = true; /* 0x02 */ + } + if (ds4->update_rumble) { /* Select classic rumble style haptics and enable it. */ common->valid_flag0 |= DS4_OUTPUT_VALID_FLAG0_MOTOR; @@ -2182,6 +2227,7 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report * int battery_status, i, j; uint16_t sensor_timestamp; unsigned long flags; + bool is_minimal = false; /* * DualShock4 in USB uses the full HID report for reportID 1, but @@ -2209,6 +2255,18 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report * ds4_report = &bt->common; num_touch_reports = bt->num_touch_reports; touch_reports = bt->touch_reports; + } else if (hdev->bus == BUS_BLUETOOTH && + report->id == DS4_INPUT_REPORT_BT_MINIMAL && + size == DS4_INPUT_REPORT_BT_MINIMAL_SIZE) { + /* Some third-party pads never switch to the full 0x11 report. + * The short 0x01 report is 10 bytes long: + * u8 report_id == 0x01 + * u8 first_bytes_of_full_report[9] + * So let's reuse the full report parser, and stop it after + * parsing the buttons. + */ + ds4_report = (struct dualshock4_input_report_common *)&data[1]; + is_minimal = true; } else { hid_err(hdev, "Unhandled reportID=%d\n", report->id); return -1; @@ -2242,6 +2300,9 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report * input_report_key(ds4->gamepad, BTN_MODE, ds4_report->buttons[2] & DS_BUTTONS2_PS_HOME); input_sync(ds4->gamepad); + if (is_minimal) + return 0; + /* Parse and calibrate gyroscope data. */ for (i = 0; i < ARRAY_SIZE(ds4_report->gyro); i++) { int raw_data = (short)le16_to_cpu(ds4_report->gyro[i]); @@ -2550,8 +2611,8 @@ static struct ps_device *dualshock4_create(struct hid_device *hdev) ret = dualshock4_get_firmware_info(ds4); if (ret) { - hid_err(hdev, "Failed to get firmware info from DualShock4\n"); - return ERR_PTR(ret); + hid_warn(hdev, "Failed to get firmware info from DualShock4\n"); + hid_warn(hdev, "HW/FW version data in sysfs will be invalid.\n"); } ret = ps_devices_list_add(ps_dev); @@ -2560,8 +2621,8 @@ static struct ps_device *dualshock4_create(struct hid_device *hdev) ret = dualshock4_get_calibration_data(ds4); if (ret) { - hid_err(hdev, "Failed to get calibration data from DualShock4\n"); - goto err; + hid_warn(hdev, "Failed to get calibration data from DualShock4\n"); + hid_warn(hdev, "Gyroscope and accelerometer will be inaccurate.\n"); } ds4->gamepad = ps_gamepad_create(hdev, dualshock4_play_effect); @@ -2655,17 +2716,14 @@ static int ps_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_stop; } - if (hdev->product == USB_DEVICE_ID_SONY_PS4_CONTROLLER || - hdev->product == USB_DEVICE_ID_SONY_PS4_CONTROLLER_2 || - hdev->product == USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE) { + if (id->driver_data == PS_TYPE_PS4_DUALSHOCK4) { dev = dualshock4_create(hdev); if (IS_ERR(dev)) { hid_err(hdev, "Failed to create dualshock4.\n"); ret = PTR_ERR(dev); goto err_close; } - } else if (hdev->product == USB_DEVICE_ID_SONY_PS5_CONTROLLER || - hdev->product == USB_DEVICE_ID_SONY_PS5_CONTROLLER_2) { + } else if (id->driver_data == PS_TYPE_PS5_DUALSENSE) { dev = dualsense_create(hdev); if (IS_ERR(dev)) { hid_err(hdev, "Failed to create dualsense.\n"); @@ -2699,16 +2757,26 @@ static void ps_remove(struct hid_device *hdev) static const struct hid_device_id ps_devices[] = { /* Sony DualShock 4 controllers for PS4 */ - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) }, - { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) }, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2) }, - { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2) }, - { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER), + .driver_data = PS_TYPE_PS4_DUALSHOCK4 }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER), + .driver_data = PS_TYPE_PS4_DUALSHOCK4 }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2), + .driver_data = PS_TYPE_PS4_DUALSHOCK4 }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2), + .driver_data = PS_TYPE_PS4_DUALSHOCK4 }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE), + .driver_data = PS_TYPE_PS4_DUALSHOCK4 }, + /* Sony DualSense controllers for PS5 */ - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER) }, - { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER) }, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER_2) }, - { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER_2) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER), + .driver_data = PS_TYPE_PS5_DUALSENSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER), + .driver_data = PS_TYPE_PS5_DUALSENSE }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER_2), + .driver_data = PS_TYPE_PS5_DUALSENSE }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER_2), + .driver_data = PS_TYPE_PS5_DUALSENSE }, { } }; MODULE_DEVICE_TABLE(hid, ps_devices); diff --git a/drivers/hid/hid-primax.c b/drivers/hid/hid-primax.c index 1e6413d07cae..e44d79dff8de 100644 --- a/drivers/hid/hid-primax.c +++ b/drivers/hid/hid-primax.c @@ -70,4 +70,5 @@ static struct hid_driver px_driver = { module_hid_driver(px_driver); MODULE_AUTHOR("Terry Lambert <tlambert@google.com>"); +MODULE_DESCRIPTION("HID driver for primax and similar keyboards with in-band modifiers"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-prodikeys.c b/drivers/hid/hid-prodikeys.c index a593ed62c969..3d08c190a935 100644 --- a/drivers/hid/hid-prodikeys.c +++ b/drivers/hid/hid-prodikeys.c @@ -728,7 +728,7 @@ static int pcmidi_snd_terminate(struct pcmidi_snd *pm) /* * PC-MIDI report descriptor for report id is wrong. */ -static __u8 *pk_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *pk_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { if (*rsize == 178 && @@ -862,4 +862,5 @@ static struct hid_driver pk_driver = { }; module_hid_driver(pk_driver); +MODULE_DESCRIPTION("HID driver for the Prodikeys PC-MIDI Keyboard"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-pxrc.c b/drivers/hid/hid-pxrc.c index b0e517f9cde7..71fe0c06ddcd 100644 --- a/drivers/hid/hid-pxrc.c +++ b/drivers/hid/hid-pxrc.c @@ -17,7 +17,7 @@ struct pxrc_priv { bool alternate; }; -static __u8 pxrc_rdesc_fixed[] = { +static const __u8 pxrc_rdesc_fixed[] = { 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 0x09, 0x04, // Usage (Joystick) 0xA1, 0x01, // Collection (Application) @@ -42,8 +42,8 @@ static __u8 pxrc_rdesc_fixed[] = { 0xC0, // End Collection }; -static __u8 *pxrc_report_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) +static const __u8 *pxrc_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) { hid_info(hdev, "fixing up PXRC report descriptor\n"); *rsize = sizeof(pxrc_rdesc_fixed); diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index e0bbf0c6345d..5d7a418ccdbe 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -891,6 +891,7 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_DPAD) }, #endif { HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) }, + { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_HP_5MP_CAMERA_5473) }, { } }; diff --git a/drivers/hid/hid-razer.c b/drivers/hid/hid-razer.c index 740df148b0ef..7f48258c61f7 100644 --- a/drivers/hid/hid-razer.c +++ b/drivers/hid/hid-razer.c @@ -122,4 +122,5 @@ static struct hid_driver razer_driver = { module_hid_driver(razer_driver); MODULE_AUTHOR("Jelle van der Waa <jvanderwaa@redhat.com>"); +MODULE_DESCRIPTION("HID driver for gaming keys on Razer Blackwidow gaming keyboards"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-redragon.c b/drivers/hid/hid-redragon.c index 73c9d4c4fa34..20d28ed75c1e 100644 --- a/drivers/hid/hid-redragon.c +++ b/drivers/hid/hid-redragon.c @@ -33,7 +33,7 @@ * key codes are generated. */ -static __u8 *redragon_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *redragon_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { if (*rsize >= 102 && rdesc[100] == 0x81 && rdesc[101] == 0x00) { @@ -59,4 +59,5 @@ static struct hid_driver redragon_driver = { module_hid_driver(redragon_driver); +MODULE_DESCRIPTION("HID driver for Redragon keyboards"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-retrode.c b/drivers/hid/hid-retrode.c index 6a08e25aa296..7997627fdccc 100644 --- a/drivers/hid/hid-retrode.c +++ b/drivers/hid/hid-retrode.c @@ -94,4 +94,5 @@ static struct hid_driver retrode_driver = { module_hid_driver(retrode_driver); +MODULE_DESCRIPTION("HID driver for Retrode 2 controller adapter and plug-in extensions"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-roccat-arvo.c b/drivers/hid/hid-roccat-arvo.c index d55aaabab1ed..3048297569c5 100644 --- a/drivers/hid/hid-roccat-arvo.c +++ b/drivers/hid/hid-roccat-arvo.c @@ -224,24 +224,24 @@ static ssize_t arvo_sysfs_read(struct file *fp, } static ssize_t arvo_sysfs_write_button(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) + struct kobject *kobj, const struct bin_attribute *attr, + char *buf, loff_t off, size_t count) { return arvo_sysfs_write(fp, kobj, buf, off, count, sizeof(struct arvo_button), ARVO_COMMAND_BUTTON); } -static BIN_ATTR(button, 0220, NULL, arvo_sysfs_write_button, - sizeof(struct arvo_button)); +static const BIN_ATTR(button, 0220, NULL, arvo_sysfs_write_button, + sizeof(struct arvo_button)); static ssize_t arvo_sysfs_read_info(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) + struct kobject *kobj, const struct bin_attribute *attr, + char *buf, loff_t off, size_t count) { return arvo_sysfs_read(fp, kobj, buf, off, count, sizeof(struct arvo_info), ARVO_COMMAND_INFO); } -static BIN_ATTR(info, 0440, arvo_sysfs_read_info, NULL, - sizeof(struct arvo_info)); +static const BIN_ATTR(info, 0440, arvo_sysfs_read_info, NULL, + sizeof(struct arvo_info)); static struct attribute *arvo_attrs[] = { &dev_attr_mode_key.attr, @@ -250,7 +250,7 @@ static struct attribute *arvo_attrs[] = { NULL, }; -static struct bin_attribute *arvo_bin_attributes[] = { +static const struct bin_attribute *const arvo_bin_attributes[] = { &bin_attr_button, &bin_attr_info, NULL, @@ -258,7 +258,7 @@ static struct bin_attribute *arvo_bin_attributes[] = { static const struct attribute_group arvo_group = { .attrs = arvo_attrs, - .bin_attrs = arvo_bin_attributes, + .bin_attrs_new = arvo_bin_attributes, }; static const struct attribute_group *arvo_groups[] = { diff --git a/drivers/hid/hid-roccat-common.h b/drivers/hid/hid-roccat-common.h index 839ddfd931f0..0f9a2db04df9 100644 --- a/drivers/hid/hid-roccat-common.h +++ b/drivers/hid/hid-roccat-common.h @@ -46,8 +46,8 @@ ssize_t roccat_common2_sysfs_write(struct file *fp, struct kobject *kobj, #define ROCCAT_COMMON2_SYSFS_W(thingy, COMMAND, SIZE) \ static ssize_t roccat_common2_sysfs_write_ ## thingy(struct file *fp, \ - struct kobject *kobj, struct bin_attribute *attr, char *buf, \ - loff_t off, size_t count) \ + struct kobject *kobj, const struct bin_attribute *attr, \ + char *buf, loff_t off, size_t count) \ { \ return roccat_common2_sysfs_write(fp, kobj, buf, off, count, \ SIZE, COMMAND); \ @@ -55,8 +55,8 @@ static ssize_t roccat_common2_sysfs_write_ ## thingy(struct file *fp, \ #define ROCCAT_COMMON2_SYSFS_R(thingy, COMMAND, SIZE) \ static ssize_t roccat_common2_sysfs_read_ ## thingy(struct file *fp, \ - struct kobject *kobj, struct bin_attribute *attr, char *buf, \ - loff_t off, size_t count) \ + struct kobject *kobj, const struct bin_attribute *attr, \ + char *buf, loff_t off, size_t count) \ { \ return roccat_common2_sysfs_read(fp, kobj, buf, off, count, \ SIZE, COMMAND); \ @@ -68,27 +68,27 @@ ROCCAT_COMMON2_SYSFS_R(thingy, COMMAND, SIZE) #define ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(thingy, COMMAND, SIZE) \ ROCCAT_COMMON2_SYSFS_RW(thingy, COMMAND, SIZE); \ -static struct bin_attribute bin_attr_ ## thingy = { \ +static const struct bin_attribute bin_attr_ ## thingy = { \ .attr = { .name = #thingy, .mode = 0660 }, \ .size = SIZE, \ - .read = roccat_common2_sysfs_read_ ## thingy, \ - .write = roccat_common2_sysfs_write_ ## thingy \ + .read_new = roccat_common2_sysfs_read_ ## thingy, \ + .write_new = roccat_common2_sysfs_write_ ## thingy \ } #define ROCCAT_COMMON2_BIN_ATTRIBUTE_R(thingy, COMMAND, SIZE) \ ROCCAT_COMMON2_SYSFS_R(thingy, COMMAND, SIZE); \ -static struct bin_attribute bin_attr_ ## thingy = { \ +static const struct bin_attribute bin_attr_ ## thingy = { \ .attr = { .name = #thingy, .mode = 0440 }, \ .size = SIZE, \ - .read = roccat_common2_sysfs_read_ ## thingy, \ + .read_new = roccat_common2_sysfs_read_ ## thingy, \ } #define ROCCAT_COMMON2_BIN_ATTRIBUTE_W(thingy, COMMAND, SIZE) \ ROCCAT_COMMON2_SYSFS_W(thingy, COMMAND, SIZE); \ -static struct bin_attribute bin_attr_ ## thingy = { \ +static const struct bin_attribute bin_attr_ ## thingy = { \ .attr = { .name = #thingy, .mode = 0220 }, \ .size = SIZE, \ - .write = roccat_common2_sysfs_write_ ## thingy \ + .write_new = roccat_common2_sysfs_write_ ## thingy \ } #endif diff --git a/drivers/hid/hid-roccat-isku.c b/drivers/hid/hid-roccat-isku.c index 458060403397..65a84bfcc2f8 100644 --- a/drivers/hid/hid-roccat-isku.c +++ b/drivers/hid/hid-roccat-isku.c @@ -61,7 +61,7 @@ static ssize_t isku_sysfs_show_actual_profile(struct device *dev, { struct isku_device *isku = hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); - return snprintf(buf, PAGE_SIZE, "%d\n", isku->actual_profile); + return sysfs_emit(buf, "%d\n", isku->actual_profile); } static ssize_t isku_sysfs_set_actual_profile(struct device *dev, @@ -156,7 +156,7 @@ static ssize_t isku_sysfs_write(struct file *fp, struct kobject *kobj, #define ISKU_SYSFS_W(thingy, THINGY) \ static ssize_t isku_sysfs_write_ ## thingy(struct file *fp, struct kobject *kobj, \ - struct bin_attribute *attr, char *buf, \ + const struct bin_attribute *attr, char *buf, \ loff_t off, size_t count) \ { \ return isku_sysfs_write(fp, kobj, buf, off, count, \ @@ -165,7 +165,7 @@ static ssize_t isku_sysfs_write_ ## thingy(struct file *fp, struct kobject *kobj #define ISKU_SYSFS_R(thingy, THINGY) \ static ssize_t isku_sysfs_read_ ## thingy(struct file *fp, struct kobject *kobj, \ - struct bin_attribute *attr, char *buf, \ + const struct bin_attribute *attr, char *buf, \ loff_t off, size_t count) \ { \ return isku_sysfs_read(fp, kobj, buf, off, count, \ @@ -178,27 +178,27 @@ ISKU_SYSFS_W(thingy, THINGY) #define ISKU_BIN_ATTR_RW(thingy, THINGY) \ ISKU_SYSFS_RW(thingy, THINGY); \ -static struct bin_attribute bin_attr_##thingy = { \ +static const struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0660 }, \ .size = ISKU_SIZE_ ## THINGY, \ - .read = isku_sysfs_read_ ## thingy, \ - .write = isku_sysfs_write_ ## thingy \ + .read_new = isku_sysfs_read_ ## thingy, \ + .write_new = isku_sysfs_write_ ## thingy \ } #define ISKU_BIN_ATTR_R(thingy, THINGY) \ ISKU_SYSFS_R(thingy, THINGY); \ -static struct bin_attribute bin_attr_##thingy = { \ +static const struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0440 }, \ .size = ISKU_SIZE_ ## THINGY, \ - .read = isku_sysfs_read_ ## thingy, \ + .read_new = isku_sysfs_read_ ## thingy, \ } #define ISKU_BIN_ATTR_W(thingy, THINGY) \ ISKU_SYSFS_W(thingy, THINGY); \ -static struct bin_attribute bin_attr_##thingy = { \ +static const struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0220 }, \ .size = ISKU_SIZE_ ## THINGY, \ - .write = isku_sysfs_write_ ## thingy \ + .write_new = isku_sysfs_write_ ## thingy \ } ISKU_BIN_ATTR_RW(macro, MACRO); @@ -217,7 +217,7 @@ ISKU_BIN_ATTR_W(control, CONTROL); ISKU_BIN_ATTR_W(reset, RESET); ISKU_BIN_ATTR_R(info, INFO); -static struct bin_attribute *isku_bin_attributes[] = { +static const struct bin_attribute *const isku_bin_attributes[] = { &bin_attr_macro, &bin_attr_keys_function, &bin_attr_keys_easyzone, @@ -238,7 +238,7 @@ static struct bin_attribute *isku_bin_attributes[] = { static const struct attribute_group isku_group = { .attrs = isku_attrs, - .bin_attrs = isku_bin_attributes, + .bin_attrs_new = isku_bin_attributes, }; static const struct attribute_group *isku_groups[] = { diff --git a/drivers/hid/hid-roccat-kone.c b/drivers/hid/hid-roccat-kone.c index 00a1abc7e839..b3c0242e5a37 100644 --- a/drivers/hid/hid-roccat-kone.c +++ b/drivers/hid/hid-roccat-kone.c @@ -261,7 +261,7 @@ static int kone_get_firmware_version(struct usb_device *usb_dev, int *result) } static ssize_t kone_sysfs_read_settings(struct file *fp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj)->parent->parent; struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev)); @@ -285,7 +285,7 @@ static ssize_t kone_sysfs_read_settings(struct file *fp, struct kobject *kobj, * case of error the old data is still valid */ static ssize_t kone_sysfs_write_settings(struct file *fp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj)->parent->parent; struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev)); @@ -327,11 +327,11 @@ unlock: return sizeof(struct kone_settings); } -static BIN_ATTR(settings, 0660, kone_sysfs_read_settings, - kone_sysfs_write_settings, sizeof(struct kone_settings)); +static const BIN_ATTR(settings, 0660, kone_sysfs_read_settings, + kone_sysfs_write_settings, sizeof(struct kone_settings)); static ssize_t kone_sysfs_read_profilex(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, + struct kobject *kobj, const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj)->parent->parent; struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev)); @@ -351,7 +351,7 @@ static ssize_t kone_sysfs_read_profilex(struct file *fp, /* Writes data only if different to stored data */ static ssize_t kone_sysfs_write_profilex(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, + struct kobject *kobj, const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj)->parent->parent; struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev)); @@ -382,11 +382,11 @@ static ssize_t kone_sysfs_write_profilex(struct file *fp, return sizeof(struct kone_profile); } #define PROFILE_ATTR(number) \ -static struct bin_attribute bin_attr_profile##number = { \ +static const struct bin_attribute bin_attr_profile##number = { \ .attr = { .name = "profile" #number, .mode = 0660 }, \ .size = sizeof(struct kone_profile), \ - .read = kone_sysfs_read_profilex, \ - .write = kone_sysfs_write_profilex, \ + .read_new = kone_sysfs_read_profilex, \ + .write_new = kone_sysfs_write_profilex, \ .private = &profile_numbers[number-1], \ } PROFILE_ATTR(1); @@ -400,7 +400,7 @@ static ssize_t kone_sysfs_show_actual_profile(struct device *dev, { struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); - return snprintf(buf, PAGE_SIZE, "%d\n", kone->actual_profile); + return sysfs_emit(buf, "%d\n", kone->actual_profile); } static DEVICE_ATTR(actual_profile, 0440, kone_sysfs_show_actual_profile, NULL); @@ -409,7 +409,7 @@ static ssize_t kone_sysfs_show_actual_dpi(struct device *dev, { struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); - return snprintf(buf, PAGE_SIZE, "%d\n", kone->actual_dpi); + return sysfs_emit(buf, "%d\n", kone->actual_dpi); } static DEVICE_ATTR(actual_dpi, 0440, kone_sysfs_show_actual_dpi, NULL); @@ -432,7 +432,7 @@ static ssize_t kone_sysfs_show_weight(struct device *dev, if (retval) return retval; - return snprintf(buf, PAGE_SIZE, "%d\n", weight); + return sysfs_emit(buf, "%d\n", weight); } static DEVICE_ATTR(weight, 0440, kone_sysfs_show_weight, NULL); @@ -441,7 +441,7 @@ static ssize_t kone_sysfs_show_firmware_version(struct device *dev, { struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); - return snprintf(buf, PAGE_SIZE, "%d\n", kone->firmware_version); + return sysfs_emit(buf, "%d\n", kone->firmware_version); } static DEVICE_ATTR(firmware_version, 0440, kone_sysfs_show_firmware_version, NULL); @@ -451,7 +451,7 @@ static ssize_t kone_sysfs_show_tcu(struct device *dev, { struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); - return snprintf(buf, PAGE_SIZE, "%d\n", kone->settings.tcu); + return sysfs_emit(buf, "%d\n", kone->settings.tcu); } static int kone_tcu_command(struct usb_device *usb_dev, int number) @@ -553,7 +553,7 @@ static ssize_t kone_sysfs_show_startup_profile(struct device *dev, { struct kone_device *kone = hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); - return snprintf(buf, PAGE_SIZE, "%d\n", kone->settings.startup_profile); + return sysfs_emit(buf, "%d\n", kone->settings.startup_profile); } static ssize_t kone_sysfs_set_startup_profile(struct device *dev, @@ -634,7 +634,7 @@ static struct attribute *kone_attrs[] = { NULL, }; -static struct bin_attribute *kone_bin_attributes[] = { +static const struct bin_attribute *const kone_bin_attributes[] = { &bin_attr_settings, &bin_attr_profile1, &bin_attr_profile2, @@ -646,7 +646,7 @@ static struct bin_attribute *kone_bin_attributes[] = { static const struct attribute_group kone_group = { .attrs = kone_attrs, - .bin_attrs = kone_bin_attributes, + .bin_attrs_new = kone_bin_attributes, }; static const struct attribute_group *kone_groups[] = { diff --git a/drivers/hid/hid-roccat-koneplus.c b/drivers/hid/hid-roccat-koneplus.c index 22b895436a7c..5d8a5ce88b4c 100644 --- a/drivers/hid/hid-roccat-koneplus.c +++ b/drivers/hid/hid-roccat-koneplus.c @@ -128,8 +128,8 @@ static ssize_t koneplus_sysfs_write(struct file *fp, struct kobject *kobj, #define KONEPLUS_SYSFS_W(thingy, THINGY) \ static ssize_t koneplus_sysfs_write_ ## thingy(struct file *fp, \ - struct kobject *kobj, struct bin_attribute *attr, char *buf, \ - loff_t off, size_t count) \ + struct kobject *kobj, const struct bin_attribute *attr, \ + char *buf, loff_t off, size_t count) \ { \ return koneplus_sysfs_write(fp, kobj, buf, off, count, \ KONEPLUS_SIZE_ ## THINGY, KONEPLUS_COMMAND_ ## THINGY); \ @@ -137,8 +137,8 @@ static ssize_t koneplus_sysfs_write_ ## thingy(struct file *fp, \ #define KONEPLUS_SYSFS_R(thingy, THINGY) \ static ssize_t koneplus_sysfs_read_ ## thingy(struct file *fp, \ - struct kobject *kobj, struct bin_attribute *attr, char *buf, \ - loff_t off, size_t count) \ + struct kobject *kobj, const struct bin_attribute *attr, \ + char *buf, loff_t off, size_t count) \ { \ return koneplus_sysfs_read(fp, kobj, buf, off, count, \ KONEPLUS_SIZE_ ## THINGY, KONEPLUS_COMMAND_ ## THINGY); \ @@ -150,27 +150,27 @@ KONEPLUS_SYSFS_R(thingy, THINGY) #define KONEPLUS_BIN_ATTRIBUTE_RW(thingy, THINGY) \ KONEPLUS_SYSFS_RW(thingy, THINGY); \ -static struct bin_attribute bin_attr_##thingy = { \ +static const struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0660 }, \ .size = KONEPLUS_SIZE_ ## THINGY, \ - .read = koneplus_sysfs_read_ ## thingy, \ - .write = koneplus_sysfs_write_ ## thingy \ + .read_new = koneplus_sysfs_read_ ## thingy, \ + .write_new = koneplus_sysfs_write_ ## thingy \ } #define KONEPLUS_BIN_ATTRIBUTE_R(thingy, THINGY) \ KONEPLUS_SYSFS_R(thingy, THINGY); \ -static struct bin_attribute bin_attr_##thingy = { \ +static const struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0440 }, \ .size = KONEPLUS_SIZE_ ## THINGY, \ - .read = koneplus_sysfs_read_ ## thingy, \ + .read_new = koneplus_sysfs_read_ ## thingy, \ } #define KONEPLUS_BIN_ATTRIBUTE_W(thingy, THINGY) \ KONEPLUS_SYSFS_W(thingy, THINGY); \ -static struct bin_attribute bin_attr_##thingy = { \ +static const struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0220 }, \ .size = KONEPLUS_SIZE_ ## THINGY, \ - .write = koneplus_sysfs_write_ ## thingy \ + .write_new = koneplus_sysfs_write_ ## thingy \ } KONEPLUS_BIN_ATTRIBUTE_W(control, CONTROL); KONEPLUS_BIN_ATTRIBUTE_W(talk, TALK); @@ -183,8 +183,8 @@ KONEPLUS_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS); KONEPLUS_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS); static ssize_t koneplus_sysfs_read_profilex_settings(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) + struct kobject *kobj, const struct bin_attribute *attr, + char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj)->parent->parent; struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); @@ -201,8 +201,8 @@ static ssize_t koneplus_sysfs_read_profilex_settings(struct file *fp, } static ssize_t koneplus_sysfs_read_profilex_buttons(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) + struct kobject *kobj, const struct bin_attribute *attr, + char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj)->parent->parent; struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); @@ -219,16 +219,16 @@ static ssize_t koneplus_sysfs_read_profilex_buttons(struct file *fp, } #define PROFILE_ATTR(number) \ -static struct bin_attribute bin_attr_profile##number##_settings = { \ +static const struct bin_attribute bin_attr_profile##number##_settings = { \ .attr = { .name = "profile" #number "_settings", .mode = 0440 }, \ .size = KONEPLUS_SIZE_PROFILE_SETTINGS, \ - .read = koneplus_sysfs_read_profilex_settings, \ + .read_new = koneplus_sysfs_read_profilex_settings, \ .private = &profile_numbers[number-1], \ }; \ -static struct bin_attribute bin_attr_profile##number##_buttons = { \ +static const struct bin_attribute bin_attr_profile##number##_buttons = { \ .attr = { .name = "profile" #number "_buttons", .mode = 0440 }, \ .size = KONEPLUS_SIZE_PROFILE_BUTTONS, \ - .read = koneplus_sysfs_read_profilex_buttons, \ + .read_new = koneplus_sysfs_read_profilex_buttons, \ .private = &profile_numbers[number-1], \ }; PROFILE_ATTR(1); @@ -242,7 +242,7 @@ static ssize_t koneplus_sysfs_show_actual_profile(struct device *dev, { struct koneplus_device *koneplus = hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); - return snprintf(buf, PAGE_SIZE, "%d\n", koneplus->actual_profile); + return sysfs_emit(buf, "%d\n", koneplus->actual_profile); } static ssize_t koneplus_sysfs_set_actual_profile(struct device *dev, @@ -309,7 +309,7 @@ static ssize_t koneplus_sysfs_show_firmware_version(struct device *dev, &info, KONEPLUS_SIZE_INFO); mutex_unlock(&koneplus->koneplus_lock); - return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version); + return sysfs_emit(buf, "%d\n", info.firmware_version); } static DEVICE_ATTR(firmware_version, 0440, koneplus_sysfs_show_firmware_version, NULL); @@ -321,7 +321,7 @@ static struct attribute *koneplus_attrs[] = { NULL, }; -static struct bin_attribute *koneplus_bin_attributes[] = { +static const struct bin_attribute *const koneplus_bin_attributes[] = { &bin_attr_control, &bin_attr_talk, &bin_attr_macro, @@ -346,7 +346,7 @@ static struct bin_attribute *koneplus_bin_attributes[] = { static const struct attribute_group koneplus_group = { .attrs = koneplus_attrs, - .bin_attrs = koneplus_bin_attributes, + .bin_attrs_new = koneplus_bin_attributes, }; static const struct attribute_group *koneplus_groups[] = { diff --git a/drivers/hid/hid-roccat-konepure.c b/drivers/hid/hid-roccat-konepure.c index beca8aef8bbb..7fb705789d4e 100644 --- a/drivers/hid/hid-roccat-konepure.c +++ b/drivers/hid/hid-roccat-konepure.c @@ -47,7 +47,7 @@ ROCCAT_COMMON2_BIN_ATTRIBUTE_R(tcu_image, 0x0c, 0x0404); ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(sensor, 0x0f, 0x06); ROCCAT_COMMON2_BIN_ATTRIBUTE_W(talk, 0x10, 0x10); -static struct bin_attribute *konepure_bin_attrs[] = { +static const struct bin_attribute *const konepure_bin_attrs[] = { &bin_attr_actual_profile, &bin_attr_control, &bin_attr_info, @@ -62,7 +62,7 @@ static struct bin_attribute *konepure_bin_attrs[] = { }; static const struct attribute_group konepure_group = { - .bin_attrs = konepure_bin_attrs, + .bin_attrs_new = konepure_bin_attrs, }; static const struct attribute_group *konepure_groups[] = { diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c index 86af538c10d6..e31e4a2e62d5 100644 --- a/drivers/hid/hid-roccat-kovaplus.c +++ b/drivers/hid/hid-roccat-kovaplus.c @@ -171,8 +171,8 @@ static ssize_t kovaplus_sysfs_write(struct file *fp, struct kobject *kobj, #define KOVAPLUS_SYSFS_W(thingy, THINGY) \ static ssize_t kovaplus_sysfs_write_ ## thingy(struct file *fp, \ - struct kobject *kobj, struct bin_attribute *attr, char *buf, \ - loff_t off, size_t count) \ + struct kobject *kobj, const struct bin_attribute *attr, \ + char *buf, loff_t off, size_t count) \ { \ return kovaplus_sysfs_write(fp, kobj, buf, off, count, \ KOVAPLUS_SIZE_ ## THINGY, KOVAPLUS_COMMAND_ ## THINGY); \ @@ -180,8 +180,8 @@ static ssize_t kovaplus_sysfs_write_ ## thingy(struct file *fp, \ #define KOVAPLUS_SYSFS_R(thingy, THINGY) \ static ssize_t kovaplus_sysfs_read_ ## thingy(struct file *fp, \ - struct kobject *kobj, struct bin_attribute *attr, char *buf, \ - loff_t off, size_t count) \ + struct kobject *kobj, const struct bin_attribute *attr, \ + char *buf, loff_t off, size_t count) \ { \ return kovaplus_sysfs_read(fp, kobj, buf, off, count, \ KOVAPLUS_SIZE_ ## THINGY, KOVAPLUS_COMMAND_ ## THINGY); \ @@ -193,19 +193,19 @@ KOVAPLUS_SYSFS_R(thingy, THINGY) #define KOVAPLUS_BIN_ATTRIBUTE_RW(thingy, THINGY) \ KOVAPLUS_SYSFS_RW(thingy, THINGY); \ -static struct bin_attribute bin_attr_##thingy = { \ +static const struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0660 }, \ .size = KOVAPLUS_SIZE_ ## THINGY, \ - .read = kovaplus_sysfs_read_ ## thingy, \ - .write = kovaplus_sysfs_write_ ## thingy \ + .read_new = kovaplus_sysfs_read_ ## thingy, \ + .write_new = kovaplus_sysfs_write_ ## thingy \ } #define KOVAPLUS_BIN_ATTRIBUTE_W(thingy, THINGY) \ KOVAPLUS_SYSFS_W(thingy, THINGY); \ -static struct bin_attribute bin_attr_##thingy = { \ +static const struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0220 }, \ .size = KOVAPLUS_SIZE_ ## THINGY, \ - .write = kovaplus_sysfs_write_ ## thingy \ + .write_new = kovaplus_sysfs_write_ ## thingy \ } KOVAPLUS_BIN_ATTRIBUTE_W(control, CONTROL); KOVAPLUS_BIN_ATTRIBUTE_RW(info, INFO); @@ -213,8 +213,8 @@ KOVAPLUS_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS); KOVAPLUS_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS); static ssize_t kovaplus_sysfs_read_profilex_settings(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) + struct kobject *kobj, const struct bin_attribute *attr, + char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj)->parent->parent; struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); @@ -231,8 +231,8 @@ static ssize_t kovaplus_sysfs_read_profilex_settings(struct file *fp, } static ssize_t kovaplus_sysfs_read_profilex_buttons(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) + struct kobject *kobj, const struct bin_attribute *attr, + char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj)->parent->parent; struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); @@ -249,16 +249,16 @@ static ssize_t kovaplus_sysfs_read_profilex_buttons(struct file *fp, } #define PROFILE_ATTR(number) \ -static struct bin_attribute bin_attr_profile##number##_settings = { \ +static const struct bin_attribute bin_attr_profile##number##_settings = { \ .attr = { .name = "profile" #number "_settings", .mode = 0440 }, \ .size = KOVAPLUS_SIZE_PROFILE_SETTINGS, \ - .read = kovaplus_sysfs_read_profilex_settings, \ + .read_new = kovaplus_sysfs_read_profilex_settings, \ .private = &profile_numbers[number-1], \ }; \ -static struct bin_attribute bin_attr_profile##number##_buttons = { \ +static const struct bin_attribute bin_attr_profile##number##_buttons = { \ .attr = { .name = "profile" #number "_buttons", .mode = 0440 }, \ .size = KOVAPLUS_SIZE_PROFILE_BUTTONS, \ - .read = kovaplus_sysfs_read_profilex_buttons, \ + .read_new = kovaplus_sysfs_read_profilex_buttons, \ .private = &profile_numbers[number-1], \ }; PROFILE_ATTR(1); @@ -272,7 +272,7 @@ static ssize_t kovaplus_sysfs_show_actual_profile(struct device *dev, { struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); - return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_profile); + return sysfs_emit(buf, "%d\n", kovaplus->actual_profile); } static ssize_t kovaplus_sysfs_set_actual_profile(struct device *dev, @@ -325,7 +325,7 @@ static ssize_t kovaplus_sysfs_show_actual_cpi(struct device *dev, { struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); - return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_cpi); + return sysfs_emit(buf, "%d\n", kovaplus->actual_cpi); } static DEVICE_ATTR(actual_cpi, 0440, kovaplus_sysfs_show_actual_cpi, NULL); @@ -334,7 +334,7 @@ static ssize_t kovaplus_sysfs_show_actual_sensitivity_x(struct device *dev, { struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); - return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_x_sensitivity); + return sysfs_emit(buf, "%d\n", kovaplus->actual_x_sensitivity); } static DEVICE_ATTR(actual_sensitivity_x, 0440, kovaplus_sysfs_show_actual_sensitivity_x, NULL); @@ -344,7 +344,7 @@ static ssize_t kovaplus_sysfs_show_actual_sensitivity_y(struct device *dev, { struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); - return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_y_sensitivity); + return sysfs_emit(buf, "%d\n", kovaplus->actual_y_sensitivity); } static DEVICE_ATTR(actual_sensitivity_y, 0440, kovaplus_sysfs_show_actual_sensitivity_y, NULL); @@ -365,7 +365,7 @@ static ssize_t kovaplus_sysfs_show_firmware_version(struct device *dev, &info, KOVAPLUS_SIZE_INFO); mutex_unlock(&kovaplus->kovaplus_lock); - return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version); + return sysfs_emit(buf, "%d\n", info.firmware_version); } static DEVICE_ATTR(firmware_version, 0440, kovaplus_sysfs_show_firmware_version, NULL); @@ -379,7 +379,7 @@ static struct attribute *kovaplus_attrs[] = { NULL, }; -static struct bin_attribute *kovaplus_bin_attributes[] = { +static const struct bin_attribute *const kovaplus_bin_attributes[] = { &bin_attr_control, &bin_attr_info, &bin_attr_profile_settings, @@ -399,7 +399,7 @@ static struct bin_attribute *kovaplus_bin_attributes[] = { static const struct attribute_group kovaplus_group = { .attrs = kovaplus_attrs, - .bin_attrs = kovaplus_bin_attributes, + .bin_attrs_new = kovaplus_bin_attributes, }; static const struct attribute_group *kovaplus_groups[] = { diff --git a/drivers/hid/hid-roccat-lua.c b/drivers/hid/hid-roccat-lua.c index d5ddf0d68346..023ec64b4b0e 100644 --- a/drivers/hid/hid-roccat-lua.c +++ b/drivers/hid/hid-roccat-lua.c @@ -66,7 +66,7 @@ static ssize_t lua_sysfs_write(struct file *fp, struct kobject *kobj, #define LUA_SYSFS_W(thingy, THINGY) \ static ssize_t lua_sysfs_write_ ## thingy(struct file *fp, \ - struct kobject *kobj, struct bin_attribute *attr, \ + struct kobject *kobj, const struct bin_attribute *attr, \ char *buf, loff_t off, size_t count) \ { \ return lua_sysfs_write(fp, kobj, buf, off, count, \ @@ -75,7 +75,7 @@ static ssize_t lua_sysfs_write_ ## thingy(struct file *fp, \ #define LUA_SYSFS_R(thingy, THINGY) \ static ssize_t lua_sysfs_read_ ## thingy(struct file *fp, \ - struct kobject *kobj, struct bin_attribute *attr, \ + struct kobject *kobj, const struct bin_attribute *attr, \ char *buf, loff_t off, size_t count) \ { \ return lua_sysfs_read(fp, kobj, buf, off, count, \ @@ -85,11 +85,11 @@ static ssize_t lua_sysfs_read_ ## thingy(struct file *fp, \ #define LUA_BIN_ATTRIBUTE_RW(thingy, THINGY) \ LUA_SYSFS_W(thingy, THINGY) \ LUA_SYSFS_R(thingy, THINGY) \ -static struct bin_attribute lua_ ## thingy ## _attr = { \ +static const struct bin_attribute lua_ ## thingy ## _attr = { \ .attr = { .name = #thingy, .mode = 0660 }, \ .size = LUA_SIZE_ ## THINGY, \ - .read = lua_sysfs_read_ ## thingy, \ - .write = lua_sysfs_write_ ## thingy \ + .read_new = lua_sysfs_read_ ## thingy, \ + .write_new = lua_sysfs_write_ ## thingy \ }; LUA_BIN_ATTRIBUTE_RW(control, CONTROL) diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c index 5663b9cd9c69..2b53fbfbb897 100644 --- a/drivers/hid/hid-roccat-pyra.c +++ b/drivers/hid/hid-roccat-pyra.c @@ -129,8 +129,8 @@ static ssize_t pyra_sysfs_write(struct file *fp, struct kobject *kobj, #define PYRA_SYSFS_W(thingy, THINGY) \ static ssize_t pyra_sysfs_write_ ## thingy(struct file *fp, \ - struct kobject *kobj, struct bin_attribute *attr, char *buf, \ - loff_t off, size_t count) \ + struct kobject *kobj, const struct bin_attribute *attr, \ + char *buf, loff_t off, size_t count) \ { \ return pyra_sysfs_write(fp, kobj, buf, off, count, \ PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \ @@ -138,8 +138,8 @@ static ssize_t pyra_sysfs_write_ ## thingy(struct file *fp, \ #define PYRA_SYSFS_R(thingy, THINGY) \ static ssize_t pyra_sysfs_read_ ## thingy(struct file *fp, \ - struct kobject *kobj, struct bin_attribute *attr, char *buf, \ - loff_t off, size_t count) \ + struct kobject *kobj, const struct bin_attribute *attr, \ + char *buf, loff_t off, size_t count) \ { \ return pyra_sysfs_read(fp, kobj, buf, off, count, \ PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \ @@ -151,27 +151,27 @@ PYRA_SYSFS_R(thingy, THINGY) #define PYRA_BIN_ATTRIBUTE_RW(thingy, THINGY) \ PYRA_SYSFS_RW(thingy, THINGY); \ -static struct bin_attribute bin_attr_##thingy = { \ +static const struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0660 }, \ .size = PYRA_SIZE_ ## THINGY, \ - .read = pyra_sysfs_read_ ## thingy, \ - .write = pyra_sysfs_write_ ## thingy \ + .read_new = pyra_sysfs_read_ ## thingy, \ + .write_new = pyra_sysfs_write_ ## thingy \ } #define PYRA_BIN_ATTRIBUTE_R(thingy, THINGY) \ PYRA_SYSFS_R(thingy, THINGY); \ -static struct bin_attribute bin_attr_##thingy = { \ +static const struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0440 }, \ - .size = PYRA_SIZE_ ## THINGY, \ - .read = pyra_sysfs_read_ ## thingy, \ + .size_new = PYRA_SIZE_ ## THINGY, \ + .read_new = pyra_sysfs_read_ ## thingy, \ } #define PYRA_BIN_ATTRIBUTE_W(thingy, THINGY) \ PYRA_SYSFS_W(thingy, THINGY); \ -static struct bin_attribute bin_attr_##thingy = { \ +static const struct bin_attribute bin_attr_##thingy = { \ .attr = { .name = #thingy, .mode = 0220 }, \ .size = PYRA_SIZE_ ## THINGY, \ - .write = pyra_sysfs_write_ ## thingy \ + .write_new = pyra_sysfs_write_ ## thingy \ } PYRA_BIN_ATTRIBUTE_W(control, CONTROL); @@ -180,8 +180,8 @@ PYRA_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS); PYRA_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS); static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) + struct kobject *kobj, const struct bin_attribute *attr, + char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj)->parent->parent; struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); @@ -198,8 +198,8 @@ static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp, } static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) + struct kobject *kobj, const struct bin_attribute *attr, + char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj)->parent->parent; struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); @@ -216,16 +216,16 @@ static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp, } #define PROFILE_ATTR(number) \ -static struct bin_attribute bin_attr_profile##number##_settings = { \ +static const struct bin_attribute bin_attr_profile##number##_settings = { \ .attr = { .name = "profile" #number "_settings", .mode = 0440 }, \ .size = PYRA_SIZE_PROFILE_SETTINGS, \ - .read = pyra_sysfs_read_profilex_settings, \ + .read_new = pyra_sysfs_read_profilex_settings, \ .private = &profile_numbers[number-1], \ }; \ -static struct bin_attribute bin_attr_profile##number##_buttons = { \ +static const struct bin_attribute bin_attr_profile##number##_buttons = { \ .attr = { .name = "profile" #number "_buttons", .mode = 0440 }, \ .size = PYRA_SIZE_PROFILE_BUTTONS, \ - .read = pyra_sysfs_read_profilex_buttons, \ + .read_new = pyra_sysfs_read_profilex_buttons, \ .private = &profile_numbers[number-1], \ }; PROFILE_ATTR(1); @@ -235,8 +235,8 @@ PROFILE_ATTR(4); PROFILE_ATTR(5); static ssize_t pyra_sysfs_write_settings(struct file *fp, - struct kobject *kobj, struct bin_attribute *attr, char *buf, - loff_t off, size_t count) + struct kobject *kobj, const struct bin_attribute *attr, + char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj)->parent->parent; struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev)); @@ -273,7 +273,7 @@ static ssize_t pyra_sysfs_write_settings(struct file *fp, } PYRA_SYSFS_R(settings, SETTINGS); -static struct bin_attribute bin_attr_settings = +static const struct bin_attribute bin_attr_settings = __BIN_ATTR(settings, (S_IWUSR | S_IRUGO), pyra_sysfs_read_settings, pyra_sysfs_write_settings, PYRA_SIZE_SETTINGS); @@ -283,7 +283,7 @@ static ssize_t pyra_sysfs_show_actual_cpi(struct device *dev, { struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); - return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_cpi); + return sysfs_emit(buf, "%d\n", pyra->actual_cpi); } static DEVICE_ATTR(actual_cpi, 0440, pyra_sysfs_show_actual_cpi, NULL); @@ -300,7 +300,7 @@ static ssize_t pyra_sysfs_show_actual_profile(struct device *dev, &settings, PYRA_SIZE_SETTINGS); mutex_unlock(&pyra->pyra_lock); - return snprintf(buf, PAGE_SIZE, "%d\n", settings.startup_profile); + return sysfs_emit(buf, "%d\n", settings.startup_profile); } static DEVICE_ATTR(actual_profile, 0440, pyra_sysfs_show_actual_profile, NULL); static DEVICE_ATTR(startup_profile, 0440, pyra_sysfs_show_actual_profile, NULL); @@ -321,7 +321,7 @@ static ssize_t pyra_sysfs_show_firmware_version(struct device *dev, &info, PYRA_SIZE_INFO); mutex_unlock(&pyra->pyra_lock); - return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version); + return sysfs_emit(buf, "%d\n", info.firmware_version); } static DEVICE_ATTR(firmware_version, 0440, pyra_sysfs_show_firmware_version, NULL); @@ -334,7 +334,7 @@ static struct attribute *pyra_attrs[] = { NULL, }; -static struct bin_attribute *pyra_bin_attributes[] = { +static const struct bin_attribute *const pyra_bin_attributes[] = { &bin_attr_control, &bin_attr_info, &bin_attr_profile_settings, @@ -355,7 +355,7 @@ static struct bin_attribute *pyra_bin_attributes[] = { static const struct attribute_group pyra_group = { .attrs = pyra_attrs, - .bin_attrs = pyra_bin_attributes, + .bin_attrs_new = pyra_bin_attributes, }; static const struct attribute_group *pyra_groups[] = { diff --git a/drivers/hid/hid-roccat-ryos.c b/drivers/hid/hid-roccat-ryos.c index 57714a4525e2..902dac1e714e 100644 --- a/drivers/hid/hid-roccat-ryos.c +++ b/drivers/hid/hid-roccat-ryos.c @@ -47,7 +47,7 @@ ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(stored_lights, 0x17, 0x0566); ROCCAT_COMMON2_BIN_ATTRIBUTE_W(custom_lights, 0x18, 0x14); ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(light_macro, 0x19, 0x07d2); -static struct bin_attribute *ryos_bin_attrs[] = { +static const struct bin_attribute *const ryos_bin_attrs[] = { &bin_attr_control, &bin_attr_profile, &bin_attr_keys_primary, @@ -70,7 +70,7 @@ static struct bin_attribute *ryos_bin_attrs[] = { }; static const struct attribute_group ryos_group = { - .bin_attrs = ryos_bin_attrs, + .bin_attrs_new = ryos_bin_attrs, }; static const struct attribute_group *ryos_groups[] = { diff --git a/drivers/hid/hid-roccat-savu.c b/drivers/hid/hid-roccat-savu.c index 2baa47a0efc5..7399b8ffb5c7 100644 --- a/drivers/hid/hid-roccat-savu.c +++ b/drivers/hid/hid-roccat-savu.c @@ -30,7 +30,7 @@ ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(macro, 0x8, 0x0823); ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(info, 0x9, 0x08); ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(sensor, 0xc, 0x04); -static struct bin_attribute *savu_bin_attrs[] = { +static const struct bin_attribute *const savu_bin_attrs[] = { &bin_attr_control, &bin_attr_profile, &bin_attr_general, @@ -42,7 +42,7 @@ static struct bin_attribute *savu_bin_attrs[] = { }; static const struct attribute_group savu_group = { - .bin_attrs = savu_bin_attrs, + .bin_attrs_new = savu_bin_attrs, }; static const struct attribute_group *savu_groups[] = { diff --git a/drivers/hid/hid-saitek.c b/drivers/hid/hid-saitek.c index b84e975977c4..6fe7c087c594 100644 --- a/drivers/hid/hid-saitek.c +++ b/drivers/hid/hid-saitek.c @@ -66,7 +66,7 @@ static int saitek_probe(struct hid_device *hdev, return 0; } -static __u8 *saitek_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *saitek_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { struct saitek_sc *ssc = hid_get_drvdata(hdev); @@ -204,4 +204,5 @@ static struct hid_driver saitek_driver = { }; module_hid_driver(saitek_driver); +MODULE_DESCRIPTION("HID driver for Saitek devices."); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-samsung.c b/drivers/hid/hid-samsung.c index 08fb25b8459a..f3908a9e04e6 100644 --- a/drivers/hid/hid-samsung.c +++ b/drivers/hid/hid-samsung.c @@ -469,7 +469,7 @@ static int samsung_universal_kbd_input_mapping(struct hid_device *hdev, return 1; } -static __u8 *samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { if (hdev->product == USB_DEVICE_ID_SAMSUNG_IR_REMOTE && hid_is_usb(hdev)) @@ -561,4 +561,5 @@ static struct hid_driver samsung_driver = { }; module_hid_driver(samsung_driver); +MODULE_DESCRIPTION("HID driver for some samsung \"special\" devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-semitek.c b/drivers/hid/hid-semitek.c index ba6607d5e051..4fbec5fd87ce 100644 --- a/drivers/hid/hid-semitek.c +++ b/drivers/hid/hid-semitek.c @@ -11,8 +11,8 @@ #include "hid-ids.h" -static __u8 *semitek_report_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) +static const __u8 *semitek_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) { /* In the report descriptor for interface 2, fix the incorrect description of report ID 0x04 (the report contains a @@ -37,4 +37,5 @@ static struct hid_driver semitek_driver = { }; module_hid_driver(semitek_driver); +MODULE_DESCRIPTION("HID driver for Semitek keyboards"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-sensor-custom.c b/drivers/hid/hid-sensor-custom.c index d85398721659..761760668f6d 100644 --- a/drivers/hid/hid-sensor-custom.c +++ b/drivers/hid/hid-sensor-custom.c @@ -155,7 +155,7 @@ static ssize_t enable_sensor_show(struct device *dev, { struct hid_sensor_custom *sensor_inst = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", sensor_inst->enable); + return sysfs_emit(buf, "%d\n", sensor_inst->enable); } static int set_power_report_state(struct hid_sensor_custom *sensor_inst, @@ -372,14 +372,13 @@ static ssize_t show_value(struct device *dev, struct device_attribute *attr, sizeof(struct hid_custom_usage_desc), usage_id_cmp); if (usage_desc) - return snprintf(buf, PAGE_SIZE, "%s\n", - usage_desc->desc); + return sysfs_emit(buf, "%s\n", usage_desc->desc); else - return sprintf(buf, "not-specified\n"); + return sysfs_emit(buf, "not-specified\n"); } else return -EINVAL; - return sprintf(buf, "%d\n", value); + return sysfs_emit(buf, "%d\n", value); } static ssize_t store_value(struct device *dev, struct device_attribute *attr, @@ -733,7 +732,7 @@ static int hid_sensor_custom_dev_if_add(struct hid_sensor_custom *sensor_inst) sensor_inst->custom_dev.minor = MISC_DYNAMIC_MINOR; sensor_inst->custom_dev.name = dev_name(&sensor_inst->pdev->dev); - sensor_inst->custom_dev.fops = &hid_sensor_custom_fops, + sensor_inst->custom_dev.fops = &hid_sensor_custom_fops; ret = misc_register(&sensor_inst->custom_dev); if (ret) { kfifo_free(&sensor_inst->data_fifo); @@ -947,7 +946,7 @@ hid_sensor_register_platform_device(struct platform_device *pdev, memcpy(real_usage, match->luid, 4); - /* usage id are all lowcase */ + /* usage id are all lowercase */ for (c = real_usage; *c != '\0'; c++) *c = tolower(*c); @@ -1032,14 +1031,14 @@ err_remove_callback: return ret; } -static int hid_sensor_custom_remove(struct platform_device *pdev) +static void hid_sensor_custom_remove(struct platform_device *pdev) { struct hid_sensor_custom *sensor_inst = platform_get_drvdata(pdev); struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; if (sensor_inst->custom_pdev) { platform_device_unregister(sensor_inst->custom_pdev); - return 0; + return; } hid_sensor_custom_dev_if_remove(sensor_inst); @@ -1047,8 +1046,6 @@ static int hid_sensor_custom_remove(struct platform_device *pdev) sysfs_remove_group(&sensor_inst->pdev->dev.kobj, &enable_sensor_attr_group); sensor_hub_remove_callback(hsdev, hsdev->usage); - - return 0; } static const struct platform_device_id hid_sensor_custom_ids[] = { diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index 26e93a331a51..4c94c03cb573 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -580,7 +580,7 @@ void sensor_hub_device_close(struct hid_sensor_hub_device *hsdev) } EXPORT_SYMBOL_GPL(sensor_hub_device_close); -static __u8 *sensor_hub_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *sensor_hub_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { /* @@ -730,23 +730,30 @@ err_stop_hw: return ret; } +static int sensor_hub_finalize_pending_fn(struct device *dev, void *data) +{ + struct hid_sensor_hub_device *hsdev = dev->platform_data; + + if (hsdev->pending.status) + complete(&hsdev->pending.ready); + + return 0; +} + static void sensor_hub_remove(struct hid_device *hdev) { struct sensor_hub_data *data = hid_get_drvdata(hdev); unsigned long flags; - int i; hid_dbg(hdev, " hardware removed\n"); hid_hw_close(hdev); hid_hw_stop(hdev); + spin_lock_irqsave(&data->lock, flags); - for (i = 0; i < data->hid_sensor_client_cnt; ++i) { - struct hid_sensor_hub_device *hsdev = - data->hid_sensor_hub_client_devs[i].platform_data; - if (hsdev->pending.status) - complete(&hsdev->pending.ready); - } + device_for_each_child(&hdev->dev, NULL, + sensor_hub_finalize_pending_fn); spin_unlock_irqrestore(&data->lock, flags); + mfd_remove_devices(&hdev->dev); mutex_destroy(&data->mutex); } diff --git a/drivers/hid/hid-sigmamicro.c b/drivers/hid/hid-sigmamicro.c index 2e7058ac0e9d..c87276d7ba0d 100644 --- a/drivers/hid/hid-sigmamicro.c +++ b/drivers/hid/hid-sigmamicro.c @@ -99,8 +99,8 @@ static const __u8 sm_0059_rdesc[] = { 0xc0, /* End Collection 166 */ }; -static __u8 *sm_report_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) +static const __u8 *sm_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) { if (*rsize == sizeof(sm_0059_rdesc) && !memcmp(sm_0059_rdesc, rdesc, *rsize)) { diff --git a/drivers/hid/hid-sjoy.c b/drivers/hid/hid-sjoy.c index 49971be7c3ff..d3a777f52a3f 100644 --- a/drivers/hid/hid-sjoy.c +++ b/drivers/hid/hid-sjoy.c @@ -168,6 +168,7 @@ static struct hid_driver sjoy_driver = { }; module_hid_driver(sjoy_driver); +MODULE_DESCRIPTION("Force feedback support for SmartJoy PLUS PS2->USB adapter"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jussi Kivilinna"); diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index ebc0aa4e4345..5258b45684e8 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -40,7 +40,7 @@ #include <linux/crc32.h> #include <linux/usb.h> #include <linux/timer.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "hid-ids.h" @@ -99,7 +99,7 @@ static const char ghl_ps4_magic_data[] = { }; /* PS/3 Motion controller */ -static u8 motion_rdesc[] = { +static const u8 motion_rdesc[] = { 0x05, 0x01, /* Usage Page (Desktop), */ 0x09, 0x04, /* Usage (Joystick), */ 0xA1, 0x01, /* Collection (Application), */ @@ -195,7 +195,7 @@ static u8 motion_rdesc[] = { 0xC0 /* End Collection */ }; -static u8 ps3remote_rdesc[] = { +static const u8 ps3remote_rdesc[] = { 0x05, 0x01, /* GUsagePage Generic Desktop */ 0x09, 0x05, /* LUsage 0x05 [Game Pad] */ 0xA1, 0x01, /* MCollection Application (mouse, keyboard) */ @@ -599,15 +599,15 @@ static int guitar_mapping(struct hid_device *hdev, struct hid_input *hi, return 0; } -static u8 *motion_fixup(struct hid_device *hdev, u8 *rdesc, - unsigned int *rsize) +static const u8 *motion_fixup(struct hid_device *hdev, u8 *rdesc, + unsigned int *rsize) { *rsize = sizeof(motion_rdesc); return motion_rdesc; } -static u8 *ps3remote_fixup(struct hid_device *hdev, u8 *rdesc, - unsigned int *rsize) +static const u8 *ps3remote_fixup(struct hid_device *hdev, u8 *rdesc, + unsigned int *rsize) { *rsize = sizeof(ps3remote_rdesc); return ps3remote_rdesc; @@ -743,7 +743,7 @@ static int sixaxis_mapping(struct hid_device *hdev, struct hid_input *hi, return -1; } -static u8 *sony_report_fixup(struct hid_device *hdev, u8 *rdesc, +static const u8 *sony_report_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *rsize) { struct sony_sc *sc = hid_get_drvdata(hdev); @@ -1379,7 +1379,8 @@ static int sony_leds_init(struct sony_sc *sc) u8 max_brightness[MAX_LEDS] = { [0 ... (MAX_LEDS - 1)] = 1 }; u8 use_hw_blink[MAX_LEDS] = { 0 }; - BUG_ON(!(sc->quirks & SONY_LED_SUPPORT)); + if (WARN_ON(!(sc->quirks & SONY_LED_SUPPORT))) + return -EINVAL; if (sc->quirks & BUZZ_CONTROLLER) { sc->led_count = 4; @@ -1844,8 +1845,7 @@ static int sony_set_device_id(struct sony_sc *sc) * All others are set to -1. */ if (sc->quirks & SIXAXIS_CONTROLLER) { - ret = ida_simple_get(&sony_device_id_allocator, 0, 0, - GFP_KERNEL); + ret = ida_alloc(&sony_device_id_allocator, GFP_KERNEL); if (ret < 0) { sc->device_id = -1; return ret; @@ -1861,7 +1861,7 @@ static int sony_set_device_id(struct sony_sc *sc) static void sony_release_device_id(struct sony_sc *sc) { if (sc->device_id >= 0) { - ida_simple_remove(&sony_device_id_allocator, sc->device_id); + ida_free(&sony_device_id_allocator, sc->device_id); sc->device_id = -1; } } @@ -2016,8 +2016,6 @@ static int sony_input_configured(struct hid_device *hdev, } else if (sc->quirks & MOTION_CONTROLLER) { sony_init_output_report(sc, motion_send_output_report); - } else { - ret = 0; } if (sc->quirks & SONY_LED_SUPPORT) { @@ -2311,4 +2309,5 @@ static void __exit sony_exit(void) module_init(sony_init); module_exit(sony_exit); +MODULE_DESCRIPTION("HID driver for Sony / PS2 / PS3 / PS4 BD devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-speedlink.c b/drivers/hid/hid-speedlink.c index 9e75f1aae0ca..22ee078c42c6 100644 --- a/drivers/hid/hid-speedlink.c +++ b/drivers/hid/hid-speedlink.c @@ -75,4 +75,5 @@ static struct hid_driver speedlink_driver = { }; module_hid_driver(speedlink_driver); +MODULE_DESCRIPTION("HID driver for Speedlink Vicious and Divine Cezanne (USB mouse)"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c index b08a5ab58528..c9e65e9088b3 100644 --- a/drivers/hid/hid-steam.c +++ b/drivers/hid/hid-steam.c @@ -45,6 +45,7 @@ #include <linux/power_supply.h> #include "hid-ids.h" +MODULE_DESCRIPTION("HID driver for Valve Steam Controller"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Rodrigo Rivas Costa <rodrigorivascosta@gmail.com>"); @@ -66,6 +67,14 @@ static LIST_HEAD(steam_devices); #define STEAM_DECK_TRIGGER_RESOLUTION 5461 /* Joystick runs are about 5 mm and 32768 units */ #define STEAM_DECK_JOYSTICK_RESOLUTION 6553 +/* Accelerometer has 16 bit resolution and a range of +/- 2g */ +#define STEAM_DECK_ACCEL_RES_PER_G 16384 +#define STEAM_DECK_ACCEL_RANGE 32768 +#define STEAM_DECK_ACCEL_FUZZ 32 +/* Gyroscope has 16 bit resolution and a range of +/- 2000 dps */ +#define STEAM_DECK_GYRO_RES_PER_DPS 16 +#define STEAM_DECK_GYRO_RANGE 32768 +#define STEAM_DECK_GYRO_FUZZ 1 #define STEAM_PAD_FUZZ 256 @@ -244,7 +253,7 @@ enum ID_CONTROLLER_DECK_STATE = 9 }; -/* String attribute idenitifiers */ +/* String attribute identifiers */ enum { ATTRIB_STR_BOARD_SERIAL, ATTRIB_STR_UNIT_SERIAL, @@ -288,6 +297,7 @@ struct steam_device { struct mutex report_mutex; unsigned long client_opened; struct input_dev __rcu *input; + struct input_dev __rcu *sensors; unsigned long quirks; struct work_struct work_connect; bool connected; @@ -302,6 +312,8 @@ struct steam_device { struct work_struct rumble_work; u16 rumble_left; u16 rumble_right; + unsigned int sensor_timestamp_us; + struct work_struct unregister_work; }; static int steam_recv_report(struct steam_device *steam, @@ -825,6 +837,74 @@ input_register_fail: return ret; } +static int steam_sensors_register(struct steam_device *steam) +{ + struct hid_device *hdev = steam->hdev; + struct input_dev *sensors; + int ret; + + if (!(steam->quirks & STEAM_QUIRK_DECK)) + return 0; + + rcu_read_lock(); + sensors = rcu_dereference(steam->sensors); + rcu_read_unlock(); + if (sensors) { + dbg_hid("%s: already connected\n", __func__); + return 0; + } + + sensors = input_allocate_device(); + if (!sensors) + return -ENOMEM; + + input_set_drvdata(sensors, steam); + sensors->dev.parent = &hdev->dev; + + sensors->name = "Steam Deck Motion Sensors"; + sensors->phys = hdev->phys; + sensors->uniq = steam->serial_no; + sensors->id.bustype = hdev->bus; + sensors->id.vendor = hdev->vendor; + sensors->id.product = hdev->product; + sensors->id.version = hdev->version; + + __set_bit(INPUT_PROP_ACCELEROMETER, sensors->propbit); + __set_bit(EV_MSC, sensors->evbit); + __set_bit(MSC_TIMESTAMP, sensors->mscbit); + + input_set_abs_params(sensors, ABS_X, -STEAM_DECK_ACCEL_RANGE, + STEAM_DECK_ACCEL_RANGE, STEAM_DECK_ACCEL_FUZZ, 0); + input_set_abs_params(sensors, ABS_Y, -STEAM_DECK_ACCEL_RANGE, + STEAM_DECK_ACCEL_RANGE, STEAM_DECK_ACCEL_FUZZ, 0); + input_set_abs_params(sensors, ABS_Z, -STEAM_DECK_ACCEL_RANGE, + STEAM_DECK_ACCEL_RANGE, STEAM_DECK_ACCEL_FUZZ, 0); + input_abs_set_res(sensors, ABS_X, STEAM_DECK_ACCEL_RES_PER_G); + input_abs_set_res(sensors, ABS_Y, STEAM_DECK_ACCEL_RES_PER_G); + input_abs_set_res(sensors, ABS_Z, STEAM_DECK_ACCEL_RES_PER_G); + + input_set_abs_params(sensors, ABS_RX, -STEAM_DECK_GYRO_RANGE, + STEAM_DECK_GYRO_RANGE, STEAM_DECK_GYRO_FUZZ, 0); + input_set_abs_params(sensors, ABS_RY, -STEAM_DECK_GYRO_RANGE, + STEAM_DECK_GYRO_RANGE, STEAM_DECK_GYRO_FUZZ, 0); + input_set_abs_params(sensors, ABS_RZ, -STEAM_DECK_GYRO_RANGE, + STEAM_DECK_GYRO_RANGE, STEAM_DECK_GYRO_FUZZ, 0); + input_abs_set_res(sensors, ABS_RX, STEAM_DECK_GYRO_RES_PER_DPS); + input_abs_set_res(sensors, ABS_RY, STEAM_DECK_GYRO_RES_PER_DPS); + input_abs_set_res(sensors, ABS_RZ, STEAM_DECK_GYRO_RES_PER_DPS); + + ret = input_register_device(sensors); + if (ret) + goto sensors_register_fail; + + rcu_assign_pointer(steam->sensors, sensors); + return 0; + +sensors_register_fail: + input_free_device(sensors); + return ret; +} + static void steam_input_unregister(struct steam_device *steam) { struct input_dev *input; @@ -838,6 +918,24 @@ static void steam_input_unregister(struct steam_device *steam) input_unregister_device(input); } +static void steam_sensors_unregister(struct steam_device *steam) +{ + struct input_dev *sensors; + + if (!(steam->quirks & STEAM_QUIRK_DECK)) + return; + + rcu_read_lock(); + sensors = rcu_dereference(steam->sensors); + rcu_read_unlock(); + + if (!sensors) + return; + RCU_INIT_POINTER(steam->sensors, NULL); + synchronize_rcu(); + input_unregister_device(sensors); +} + static void steam_battery_unregister(struct steam_device *steam) { struct power_supply *battery; @@ -890,18 +988,28 @@ static int steam_register(struct steam_device *steam) spin_lock_irqsave(&steam->lock, flags); client_opened = steam->client_opened; spin_unlock_irqrestore(&steam->lock, flags); + if (!client_opened) { steam_set_lizard_mode(steam, lizard_mode); ret = steam_input_register(steam); - } else - ret = 0; + if (ret != 0) + goto steam_register_input_fail; + ret = steam_sensors_register(steam); + if (ret != 0) + goto steam_register_sensors_fail; + } + return 0; +steam_register_sensors_fail: + steam_input_unregister(steam); +steam_register_input_fail: return ret; } static void steam_unregister(struct steam_device *steam) { steam_battery_unregister(steam); + steam_sensors_unregister(steam); steam_input_unregister(steam); if (steam->serial_no[0]) { hid_info(steam->hdev, "Steam Controller '%s' disconnected", @@ -943,10 +1051,10 @@ static void steam_mode_switch_cb(struct work_struct *work) struct steam_device, mode_switch); unsigned long flags; bool client_opened; - steam->gamepad_mode = !steam->gamepad_mode; if (!lizard_mode) return; + steam->gamepad_mode = !steam->gamepad_mode; if (steam->gamepad_mode) steam_set_lizard_mode(steam, false); else { @@ -965,6 +1073,31 @@ static void steam_mode_switch_cb(struct work_struct *work) } } +static void steam_work_unregister_cb(struct work_struct *work) +{ + struct steam_device *steam = container_of(work, struct steam_device, + unregister_work); + unsigned long flags; + bool connected; + bool opened; + + spin_lock_irqsave(&steam->lock, flags); + opened = steam->client_opened; + connected = steam->connected; + spin_unlock_irqrestore(&steam->lock, flags); + + if (connected) { + if (opened) { + steam_sensors_unregister(steam); + steam_input_unregister(steam); + } else { + steam_set_lizard_mode(steam, lizard_mode); + steam_input_register(steam); + steam_sensors_register(steam); + } + } +} + static bool steam_is_valve_interface(struct hid_device *hdev) { struct hid_report_enum *rep_enum; @@ -1010,7 +1143,7 @@ static int steam_client_ll_open(struct hid_device *hdev) steam->client_opened++; spin_unlock_irqrestore(&steam->lock, flags); - steam_input_unregister(steam); + schedule_work(&steam->unregister_work); return 0; } @@ -1027,10 +1160,7 @@ static void steam_client_ll_close(struct hid_device *hdev) connected = steam->connected && !steam->client_opened; spin_unlock_irqrestore(&steam->lock, flags); - if (connected) { - steam_set_lizard_mode(steam, lizard_mode); - steam_input_register(steam); - } + schedule_work(&steam->unregister_work); } static int steam_client_ll_raw_request(struct hid_device *hdev, @@ -1121,6 +1251,8 @@ static int steam_probe(struct hid_device *hdev, INIT_DELAYED_WORK(&steam->mode_switch, steam_mode_switch_cb); INIT_LIST_HEAD(&steam->list); INIT_WORK(&steam->rumble_work, steam_haptic_rumble_cb); + steam->sensor_timestamp_us = 0; + INIT_WORK(&steam->unregister_work, steam_work_unregister_cb); /* * With the real steam controller interface, do not connect hidraw. @@ -1158,7 +1290,7 @@ static int steam_probe(struct hid_device *hdev, steam->client_hdev = steam_create_client_hid(hdev); if (IS_ERR(steam->client_hdev)) { ret = PTR_ERR(steam->client_hdev); - goto err_stream_unregister; + goto err_steam_unregister; } steam->client_hdev->driver_data = steam; @@ -1170,7 +1302,7 @@ static int steam_probe(struct hid_device *hdev, err_destroy: hid_destroy_device(steam->client_hdev); -err_stream_unregister: +err_steam_unregister: if (steam->connected) steam_unregister(steam); err_hw_close: @@ -1181,6 +1313,7 @@ err_cancel_work: cancel_work_sync(&steam->work_connect); cancel_delayed_work_sync(&steam->mode_switch); cancel_work_sync(&steam->rumble_work); + cancel_work_sync(&steam->unregister_work); return ret; } @@ -1196,6 +1329,8 @@ static void steam_remove(struct hid_device *hdev) cancel_delayed_work_sync(&steam->mode_switch); cancel_work_sync(&steam->work_connect); + cancel_work_sync(&steam->rumble_work); + cancel_work_sync(&steam->unregister_work); hid_destroy_device(steam->client_hdev); steam->client_hdev = NULL; steam->client_opened = 0; @@ -1380,12 +1515,12 @@ static void steam_do_input_event(struct steam_device *steam, * 18-19 | s16 | ABS_HAT0Y | left-pad Y value * 20-21 | s16 | ABS_HAT1X | right-pad X value * 22-23 | s16 | ABS_HAT1Y | right-pad Y value - * 24-25 | s16 | -- | accelerometer X value - * 26-27 | s16 | -- | accelerometer Y value - * 28-29 | s16 | -- | accelerometer Z value - * 30-31 | s16 | -- | gyro X value - * 32-33 | s16 | -- | gyro Y value - * 34-35 | s16 | -- | gyro Z value + * 24-25 | s16 | IMU ABS_X | accelerometer X value + * 26-27 | s16 | IMU ABS_Z | accelerometer Y value + * 28-29 | s16 | IMU ABS_Y | accelerometer Z value + * 30-31 | s16 | IMU ABS_RX | gyro X value + * 32-33 | s16 | IMU ABS_RZ | gyro Y value + * 34-35 | s16 | IMU ABS_RY | gyro Z value * 36-37 | s16 | -- | quaternion W value * 38-39 | s16 | -- | quaternion X value * 40-41 | s16 | -- | quaternion Y value @@ -1482,13 +1617,13 @@ static void steam_do_deck_input_event(struct steam_device *steam, if (!(b9 & BIT(6)) && steam->did_mode_switch) { steam->did_mode_switch = false; - cancel_delayed_work_sync(&steam->mode_switch); + cancel_delayed_work(&steam->mode_switch); } else if (!steam->client_opened && (b9 & BIT(6)) && !steam->did_mode_switch) { steam->did_mode_switch = true; schedule_delayed_work(&steam->mode_switch, 45 * HZ / 100); } - if (!steam->gamepad_mode) + if (!steam->gamepad_mode && lizard_mode) return; lpad_touched = b10 & BIT(3); @@ -1546,6 +1681,32 @@ static void steam_do_deck_input_event(struct steam_device *steam, input_sync(input); } +static void steam_do_deck_sensors_event(struct steam_device *steam, + struct input_dev *sensors, u8 *data) +{ + /* + * The deck input report is received every 4 ms on average, + * with a jitter of +/- 4 ms even though the USB descriptor claims + * that it uses 1 kHz. + * Since the HID report does not include a sensor timestamp, + * use a fixed increment here. + */ + steam->sensor_timestamp_us += 4000; + + if (!steam->gamepad_mode && lizard_mode) + return; + + input_event(sensors, EV_MSC, MSC_TIMESTAMP, steam->sensor_timestamp_us); + input_report_abs(sensors, ABS_X, steam_le16(data + 24)); + input_report_abs(sensors, ABS_Z, -steam_le16(data + 26)); + input_report_abs(sensors, ABS_Y, steam_le16(data + 28)); + input_report_abs(sensors, ABS_RX, steam_le16(data + 30)); + input_report_abs(sensors, ABS_RZ, -steam_le16(data + 32)); + input_report_abs(sensors, ABS_RY, steam_le16(data + 34)); + + input_sync(sensors); +} + /* * The size for this message payload is 11. * The known values are: @@ -1583,6 +1744,7 @@ static int steam_raw_event(struct hid_device *hdev, { struct steam_device *steam = hid_get_drvdata(hdev); struct input_dev *input; + struct input_dev *sensors; struct power_supply *battery; if (!steam) @@ -1628,6 +1790,9 @@ static int steam_raw_event(struct hid_device *hdev, input = rcu_dereference(steam->input); if (likely(input)) steam_do_deck_input_event(steam, input, data); + sensors = rcu_dereference(steam->sensors); + if (likely(sensors)) + steam_do_deck_sensors_event(steam, sensors, data); rcu_read_unlock(); break; case ID_CONTROLLER_WIRELESS: diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index b3edadf42d6d..d4bd7848b8c6 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -19,6 +19,7 @@ #define STEELSERIES_SRWS1 BIT(0) #define STEELSERIES_ARCTIS_1 BIT(1) +#define STEELSERIES_ARCTIS_9 BIT(2) struct steelseries_device { struct hid_device *hdev; @@ -32,6 +33,7 @@ struct steelseries_device { struct power_supply *battery; uint8_t battery_capacity; bool headset_connected; + bool battery_charging; }; #if IS_BUILTIN(CONFIG_LEDS_CLASS) || \ @@ -51,7 +53,7 @@ struct steelseries_srws1_data { * appear in the 'Generic Desktop' usage. */ -static __u8 steelseries_srws1_rdesc_fixed[] = { +static const __u8 steelseries_srws1_rdesc_fixed[] = { 0x05, 0x01, /* Usage Page (Desktop) */ 0x09, 0x08, /* Usage (MultiAxis), Changed */ 0xA1, 0x01, /* Collection (Application), */ @@ -368,32 +370,35 @@ static void steelseries_srws1_remove(struct hid_device *hdev) hid_hw_stop(hdev); kfree(drv_data); - return; } #endif #define STEELSERIES_HEADSET_BATTERY_TIMEOUT_MS 3000 #define ARCTIS_1_BATTERY_RESPONSE_LEN 8 +#define ARCTIS_9_BATTERY_RESPONSE_LEN 64 static const char arctis_1_battery_request[] = { 0x06, 0x12 }; +static const char arctis_9_battery_request[] = { 0x00, 0x20 }; -static int steelseries_headset_arctis_1_fetch_battery(struct hid_device *hdev) +static int steelseries_headset_request_battery(struct hid_device *hdev, + const char *request, size_t len) { u8 *write_buf; int ret; /* Request battery information */ - write_buf = kmemdup(arctis_1_battery_request, sizeof(arctis_1_battery_request), GFP_KERNEL); + write_buf = kmemdup(request, len, GFP_KERNEL); if (!write_buf) return -ENOMEM; - ret = hid_hw_raw_request(hdev, arctis_1_battery_request[0], - write_buf, sizeof(arctis_1_battery_request), + hid_dbg(hdev, "Sending battery request report"); + ret = hid_hw_raw_request(hdev, request[0], write_buf, len, HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); - if (ret < (int)sizeof(arctis_1_battery_request)) { + if (ret < (int)len) { hid_err(hdev, "hid_hw_raw_request() failed with %d\n", ret); ret = -ENODATA; } + kfree(write_buf); return ret; } @@ -404,13 +409,26 @@ static void steelseries_headset_fetch_battery(struct hid_device *hdev) int ret = 0; if (sd->quirks & STEELSERIES_ARCTIS_1) - ret = steelseries_headset_arctis_1_fetch_battery(hdev); + ret = steelseries_headset_request_battery(hdev, + arctis_1_battery_request, sizeof(arctis_1_battery_request)); + else if (sd->quirks & STEELSERIES_ARCTIS_9) + ret = steelseries_headset_request_battery(hdev, + arctis_9_battery_request, sizeof(arctis_9_battery_request)); if (ret < 0) hid_dbg(hdev, "Battery query failed (err: %d)\n", ret); } +static int battery_capacity_to_level(int capacity) +{ + if (capacity >= 50) + return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; + if (capacity >= 20) + return POWER_SUPPLY_CAPACITY_LEVEL_LOW; + return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; +} + static void steelseries_headset_battery_timer_tick(struct work_struct *work) { struct steelseries_device *sd = container_of(work, @@ -420,6 +438,9 @@ static void steelseries_headset_battery_timer_tick(struct work_struct *work) steelseries_headset_fetch_battery(hdev); } +#define STEELSERIES_PREFIX "SteelSeries " +#define STEELSERIES_PREFIX_LEN strlen(STEELSERIES_PREFIX) + static int steelseries_headset_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -428,13 +449,24 @@ static int steelseries_headset_battery_get_property(struct power_supply *psy, int ret = 0; switch (psp) { + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = sd->hdev->name; + while (!strncmp(val->strval, STEELSERIES_PREFIX, STEELSERIES_PREFIX_LEN)) + val->strval += STEELSERIES_PREFIX_LEN; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = "SteelSeries"; + break; case POWER_SUPPLY_PROP_PRESENT: val->intval = 1; break; case POWER_SUPPLY_PROP_STATUS: - val->intval = sd->headset_connected ? - POWER_SUPPLY_STATUS_DISCHARGING : - POWER_SUPPLY_STATUS_UNKNOWN; + if (sd->headset_connected) { + val->intval = sd->battery_charging ? + POWER_SUPPLY_STATUS_CHARGING : + POWER_SUPPLY_STATUS_DISCHARGING; + } else + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; break; case POWER_SUPPLY_PROP_SCOPE: val->intval = POWER_SUPPLY_SCOPE_DEVICE; @@ -442,6 +474,9 @@ static int steelseries_headset_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CAPACITY: val->intval = sd->battery_capacity; break; + case POWER_SUPPLY_PROP_CAPACITY_LEVEL: + val->intval = battery_capacity_to_level(sd->battery_capacity); + break; default: ret = -EINVAL; break; @@ -465,10 +500,13 @@ steelseries_headset_set_wireless_status(struct hid_device *hdev, } static enum power_supply_property steelseries_headset_battery_props[] = { + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_SCOPE, POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, }; static int steelseries_headset_battery_register(struct steelseries_device *sd) @@ -492,6 +530,7 @@ static int steelseries_headset_battery_register(struct steelseries_device *sd) /* avoid the warning of 0% battery while waiting for the first info */ steelseries_headset_set_wireless_status(sd->hdev, false); sd->battery_capacity = 100; + sd->battery_charging = false; sd->battery = devm_power_supply_register(&sd->hdev->dev, &sd->battery_desc, &battery_cfg); @@ -507,9 +546,22 @@ static int steelseries_headset_battery_register(struct steelseries_device *sd) INIT_DELAYED_WORK(&sd->battery_work, steelseries_headset_battery_timer_tick); steelseries_headset_fetch_battery(sd->hdev); + if (sd->quirks & STEELSERIES_ARCTIS_9) { + /* The first fetch_battery request can remain unanswered in some cases */ + schedule_delayed_work(&sd->battery_work, + msecs_to_jiffies(STEELSERIES_HEADSET_BATTERY_TIMEOUT_MS)); + } + return 0; } +static bool steelseries_is_vendor_usage_page(struct hid_device *hdev, uint8_t usage_page) +{ + return hdev->rdesc[0] == 0x06 && + hdev->rdesc[1] == usage_page && + hdev->rdesc[2] == 0xff; +} + static int steelseries_probe(struct hid_device *hdev, const struct hid_device_id *id) { struct steelseries_device *sd; @@ -535,12 +587,20 @@ static int steelseries_probe(struct hid_device *hdev, const struct hid_device_id if (ret) return ret; + if (sd->quirks & STEELSERIES_ARCTIS_9 && + !steelseries_is_vendor_usage_page(hdev, 0xc0)) + return -ENODEV; + spin_lock_init(&sd->lock); ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) return ret; + ret = hid_hw_open(hdev); + if (ret) + return ret; + if (steelseries_headset_battery_register(sd) < 0) hid_err(sd->hdev, "Failed to register battery for headset\n"); @@ -567,11 +627,12 @@ static void steelseries_remove(struct hid_device *hdev) cancel_delayed_work_sync(&sd->battery_work); + hid_hw_close(hdev); hid_hw_stop(hdev); } -static __u8 *steelseries_srws1_report_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) +static const __u8 *steelseries_srws1_report_fixup(struct hid_device *hdev, + __u8 *rdesc, unsigned int *rsize) { if (hdev->vendor != USB_VENDOR_ID_STEELSERIES || hdev->product != USB_DEVICE_ID_STEELSERIES_SRWS1) @@ -580,12 +641,21 @@ static __u8 *steelseries_srws1_report_fixup(struct hid_device *hdev, __u8 *rdesc if (*rsize >= 115 && rdesc[11] == 0x02 && rdesc[13] == 0xc8 && rdesc[29] == 0xbb && rdesc[40] == 0xc5) { hid_info(hdev, "Fixing up Steelseries SRW-S1 report descriptor\n"); - rdesc = steelseries_srws1_rdesc_fixed; *rsize = sizeof(steelseries_srws1_rdesc_fixed); + return steelseries_srws1_rdesc_fixed; } return rdesc; } +static uint8_t steelseries_headset_map_capacity(uint8_t capacity, uint8_t min_in, uint8_t max_in) +{ + if (capacity >= max_in) + return 100; + if (capacity <= min_in) + return 0; + return (capacity - min_in) * 100 / (max_in - min_in); +} + static int steelseries_headset_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *read_buf, int size) @@ -593,6 +663,7 @@ static int steelseries_headset_raw_event(struct hid_device *hdev, struct steelseries_device *sd = hid_get_drvdata(hdev); int capacity = sd->battery_capacity; bool connected = sd->headset_connected; + bool charging = sd->battery_charging; unsigned long flags; /* Not a headset */ @@ -603,8 +674,11 @@ static int steelseries_headset_raw_event(struct hid_device *hdev, hid_dbg(sd->hdev, "Parsing raw event for Arctis 1 headset (%*ph)\n", size, read_buf); if (size < ARCTIS_1_BATTERY_RESPONSE_LEN || - memcmp (read_buf, arctis_1_battery_request, sizeof(arctis_1_battery_request))) + memcmp(read_buf, arctis_1_battery_request, sizeof(arctis_1_battery_request))) { + if (!delayed_work_pending(&sd->battery_work)) + goto request_battery; return 0; + } if (read_buf[2] == 0x01) { connected = false; capacity = 100; @@ -614,6 +688,34 @@ static int steelseries_headset_raw_event(struct hid_device *hdev, } } + if (sd->quirks & STEELSERIES_ARCTIS_9) { + hid_dbg(sd->hdev, + "Parsing raw event for Arctis 9 headset (%*ph)\n", size, read_buf); + if (size < ARCTIS_9_BATTERY_RESPONSE_LEN) { + if (!delayed_work_pending(&sd->battery_work)) + goto request_battery; + return 0; + } + + if (read_buf[0] == 0xaa && read_buf[1] == 0x01) { + connected = true; + charging = read_buf[4] == 0x01; + + /* + * Found no official documentation about min and max. + * Values defined by testing. + */ + capacity = steelseries_headset_map_capacity(read_buf[3], 0x68, 0x9d); + } else { + /* + * Device is off and sends the last known status read_buf[1] == 0x03 or + * there is no known status of the device read_buf[0] == 0x55 + */ + connected = false; + charging = false; + } + } + if (connected != sd->headset_connected) { hid_dbg(sd->hdev, "Connected status changed from %sconnected to %sconnected\n", @@ -631,6 +733,16 @@ static int steelseries_headset_raw_event(struct hid_device *hdev, power_supply_changed(sd->battery); } + if (charging != sd->battery_charging) { + hid_dbg(sd->hdev, + "Battery charging status changed from %scharging to %scharging\n", + sd->battery_charging ? "" : "not ", + charging ? "" : "not "); + sd->battery_charging = charging; + power_supply_changed(sd->battery); + } + +request_battery: spin_lock_irqsave(&sd->lock, flags); if (!sd->removed) schedule_delayed_work(&sd->battery_work, @@ -648,6 +760,10 @@ static const struct hid_device_id steelseries_devices[] = { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, 0x12b6), .driver_data = STEELSERIES_ARCTIS_1 }, + { /* SteelSeries Arctis 9 Wireless for XBox */ + HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, 0x12c2), + .driver_data = STEELSERIES_ARCTIS_9 }, + { } }; MODULE_DEVICE_TABLE(hid, steelseries_devices); @@ -662,6 +778,8 @@ static struct hid_driver steelseries_driver = { }; module_hid_driver(steelseries_driver); +MODULE_DESCRIPTION("HID driver for Steelseries devices"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>"); MODULE_AUTHOR("Simon Wood <simon@mungewell.org>"); +MODULE_AUTHOR("Christian Mayer <git@mayer-bgk.de>"); diff --git a/drivers/hid/hid-sunplus.c b/drivers/hid/hid-sunplus.c index aa2855c2ed4e..64e4cff8ca1d 100644 --- a/drivers/hid/hid-sunplus.c +++ b/drivers/hid/hid-sunplus.c @@ -18,7 +18,7 @@ #include "hid-ids.h" -static __u8 *sp_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *sp_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { if (*rsize >= 112 && rdesc[104] == 0x26 && rdesc[105] == 0x80 && @@ -62,4 +62,5 @@ static struct hid_driver sp_driver = { }; module_hid_driver(sp_driver); +MODULE_DESCRIPTION("HID driver for some sunplus \"special\" devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-thrustmaster.c b/drivers/hid/hid-thrustmaster.c index cf1679b0d4fb..3b81468a1df2 100644 --- a/drivers/hid/hid-thrustmaster.c +++ b/drivers/hid/hid-thrustmaster.c @@ -170,6 +170,14 @@ static void thrustmaster_interrupts(struct hid_device *hdev) ep = &usbif->cur_altsetting->endpoint[1]; b_ep = ep->desc.bEndpointAddress; + /* Are the expected endpoints present? */ + u8 ep_addr[2] = {b_ep, 0}; + + if (!usb_check_int_endpoints(usbif, ep_addr)) { + hid_err(hdev, "Unexpected non-int endpoint\n"); + return; + } + for (i = 0; i < ARRAY_SIZE(setup_arr); ++i) { memcpy(send_buf, setup_arr[i], setup_arr_sizes[i]); diff --git a/drivers/hid/hid-tivo.c b/drivers/hid/hid-tivo.c index 68eb08b63945..827bf67abeb9 100644 --- a/drivers/hid/hid-tivo.c +++ b/drivers/hid/hid-tivo.c @@ -73,5 +73,6 @@ static struct hid_driver tivo_driver = { }; module_hid_driver(tivo_driver); +MODULE_DESCRIPTION("HID driver for TiVo Slide Bluetooth remote"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>"); diff --git a/drivers/hid/hid-tmff.c b/drivers/hid/hid-tmff.c index 4040cd98dafe..fcd859aa3a8c 100644 --- a/drivers/hid/hid-tmff.c +++ b/drivers/hid/hid-tmff.c @@ -265,4 +265,5 @@ static struct hid_driver tm_driver = { }; module_hid_driver(tm_driver); +MODULE_DESCRIPTION("Force feedback support for various HID compliant devices by ThrustMaster"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-topre.c b/drivers/hid/hid-topre.c index d1d5ca310ead..ccedf8721722 100644 --- a/drivers/hid/hid-topre.c +++ b/drivers/hid/hid-topre.c @@ -21,14 +21,19 @@ MODULE_LICENSE("GPL"); * events it's actually sending. It claims to send array events but is instead * sending variable events. */ -static __u8 *topre_report_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) +static const __u8 *topre_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) { if (*rsize >= 119 && rdesc[69] == 0x29 && rdesc[70] == 0xe7 && rdesc[71] == 0x81 && rdesc[72] == 0x00) { hid_info(hdev, "fixing up Topre REALFORCE keyboard report descriptor\n"); rdesc[72] = 0x02; + } else if (*rsize >= 106 && rdesc[28] == 0x29 && rdesc[29] == 0xe7 && + rdesc[30] == 0x81 && rdesc[31] == 0x00) { + hid_info(hdev, + "fixing up Topre REALFORCE keyboard report descriptor\n"); + rdesc[31] = 0x02; } return rdesc; } @@ -38,6 +43,8 @@ static const struct hid_device_id topre_id_table[] = { USB_DEVICE_ID_TOPRE_REALFORCE_R2_108) }, { HID_USB_DEVICE(USB_VENDOR_ID_TOPRE, USB_DEVICE_ID_TOPRE_REALFORCE_R2_87) }, + { HID_USB_DEVICE(USB_VENDOR_ID_TOPRE, + USB_DEVICE_ID_TOPRE_REALFORCE_R3S_87) }, { } }; MODULE_DEVICE_TABLE(hid, topre_id_table); diff --git a/drivers/hid/hid-topseed.c b/drivers/hid/hid-topseed.c index 2125327b8de1..645e36cd83a6 100644 --- a/drivers/hid/hid-topseed.c +++ b/drivers/hid/hid-topseed.c @@ -78,4 +78,5 @@ static struct hid_driver ts_driver = { }; module_hid_driver(ts_driver); +MODULE_DESCRIPTION("HID driver for TopSeed Cyberlink remote"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-twinhan.c b/drivers/hid/hid-twinhan.c index 14af794146c0..0ef5194085b2 100644 --- a/drivers/hid/hid-twinhan.c +++ b/drivers/hid/hid-twinhan.c @@ -131,4 +131,5 @@ static struct hid_driver twinhan_driver = { }; module_hid_driver(twinhan_driver); +MODULE_DESCRIPTION("HID driver for TwinHan IR remote control"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c index ad74cbc9a0aa..d8008933c052 100644 --- a/drivers/hid/hid-uclogic-core.c +++ b/drivers/hid/hid-uclogic-core.c @@ -50,14 +50,14 @@ static void uclogic_inrange_timeout(struct timer_list *t) input_sync(input); } -static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); if (drvdata->desc_ptr != NULL) { - rdesc = drvdata->desc_ptr; *rsize = drvdata->desc_size; + return drvdata->desc_ptr; } return rdesc; } @@ -567,7 +567,9 @@ module_hid_driver(uclogic_driver); MODULE_AUTHOR("Martin Rusko"); MODULE_AUTHOR("Nikolai Kondrashov"); +MODULE_DESCRIPTION("HID driver for UC-Logic devices not fully compliant with HID standard"); MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("HID driver for UC-Logic devices not fully compliant with HID standard"); #ifdef CONFIG_HID_KUNIT_TEST #include "hid-uclogic-core-test.c" diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c index 9859dad36495..a6044996abf2 100644 --- a/drivers/hid/hid-uclogic-params.c +++ b/drivers/hid/hid-uclogic-params.c @@ -19,7 +19,7 @@ #include "hid-ids.h" #include <linux/ctype.h> #include <linux/string.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> /** * uclogic_params_pen_inrange_to_str() - Convert a pen in-range reporting type @@ -681,7 +681,7 @@ void uclogic_params_cleanup(struct uclogic_params *params) * -ENOMEM, if failed to allocate memory. */ int uclogic_params_get_desc(const struct uclogic_params *params, - __u8 **pdesc, + const __u8 **pdesc, unsigned int *psize) { int rc = -ENOMEM; @@ -769,7 +769,7 @@ static void uclogic_params_init_invalid(struct uclogic_params *params) static int uclogic_params_init_with_opt_desc(struct uclogic_params *params, struct hid_device *hdev, unsigned int orig_desc_size, - __u8 *desc_ptr, + const __u8 *desc_ptr, unsigned int desc_size) { __u8 *desc_copy_ptr = NULL; @@ -842,7 +842,7 @@ static int uclogic_params_huion_init(struct uclogic_params *params, __u8 *params_ptr = NULL; size_t params_len = 0; /* Parameters string descriptor of a model with touch ring (HS610) */ - const __u8 touch_ring_model_params_buf[] = { + static const __u8 touch_ring_model_params_buf[] = { 0x13, 0x03, 0x70, 0xC6, 0x00, 0x06, 0x7C, 0x00, 0xFF, 0x1F, 0xD8, 0x13, 0x03, 0x0D, 0x10, 0x01, 0x04, 0x3C, 0x3E @@ -884,6 +884,9 @@ static int uclogic_params_huion_init(struct uclogic_params *params, goto cleanup; } + /* The firmware is used in userspace as unique identifier */ + strscpy(hdev->uniq, ver_ptr, sizeof(hdev->uniq)); + /* If this is a transition firmware */ if (strcmp(ver_ptr, transition_ver) == 0) { hid_dbg(hdev, diff --git a/drivers/hid/hid-uclogic-params.h b/drivers/hid/hid-uclogic-params.h index d6ffadb2f601..35ff062d09b5 100644 --- a/drivers/hid/hid-uclogic-params.h +++ b/drivers/hid/hid-uclogic-params.h @@ -79,7 +79,7 @@ struct uclogic_params_pen { * Pointer to report descriptor part describing the pen inputs. * Allocated with kmalloc. NULL if the part is not specified. */ - __u8 *desc_ptr; + const __u8 *desc_ptr; /* * Size of the report descriptor. * Only valid, if "desc_ptr" is not NULL. @@ -118,7 +118,7 @@ struct uclogic_params_frame { * Pointer to report descriptor part describing the frame inputs. * Allocated with kmalloc. NULL if the part is not specified. */ - __u8 *desc_ptr; + const __u8 *desc_ptr; /* * Size of the report descriptor. * Only valid, if "desc_ptr" is not NULL. @@ -212,7 +212,7 @@ struct uclogic_params { * allocated with kmalloc. NULL if no common part is needed. * Only valid, if "invalid" is false. */ - __u8 *desc_ptr; + const __u8 *desc_ptr; /* * Size of the common part of the replacement report descriptor. * Only valid, if "desc_ptr" is valid and not NULL. @@ -239,7 +239,7 @@ struct uclogic_drvdata { /* Interface parameters */ struct uclogic_params params; /* Pointer to the replacement report descriptor. NULL if none. */ - __u8 *desc_ptr; + const __u8 *desc_ptr; /* * Size of the replacement report descriptor. * Only valid if desc_ptr is not NULL @@ -261,7 +261,7 @@ extern int uclogic_params_init(struct uclogic_params *params, /* Get a replacement report descriptor for a tablet's interface. */ extern int uclogic_params_get_desc(const struct uclogic_params *params, - __u8 **pdesc, + const __u8 **pdesc, unsigned int *psize); /* Free resources used by tablet interface's parameters */ diff --git a/drivers/hid/hid-uclogic-rdesc-test.c b/drivers/hid/hid-uclogic-rdesc-test.c index 90bf4e586e01..066df622b6f2 100644 --- a/drivers/hid/hid-uclogic-rdesc-test.c +++ b/drivers/hid/hid-uclogic-rdesc-test.c @@ -9,6 +9,8 @@ #include <kunit/test.h> #include "./hid-uclogic-rdesc.h" +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); + struct uclogic_template_case { const char *name; const __u8 *template; diff --git a/drivers/hid/hid-uclogic-rdesc.c b/drivers/hid/hid-uclogic-rdesc.c index b6dfdf6356a6..9b9cbc2aae36 100644 --- a/drivers/hid/hid-uclogic-rdesc.c +++ b/drivers/hid/hid-uclogic-rdesc.c @@ -16,10 +16,11 @@ #include "hid-uclogic-rdesc.h" #include <linux/slab.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> +#include <kunit/visibility.h> /* Fixed WP4030U report descriptor */ -__u8 uclogic_rdesc_wp4030u_fixed_arr[] = { +const __u8 uclogic_rdesc_wp4030u_fixed_arr[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x01, /* Usage (Digitizer), */ 0xA1, 0x01, /* Collection (Application), */ @@ -64,7 +65,7 @@ const size_t uclogic_rdesc_wp4030u_fixed_size = sizeof(uclogic_rdesc_wp4030u_fixed_arr); /* Fixed WP5540U report descriptor */ -__u8 uclogic_rdesc_wp5540u_fixed_arr[] = { +const __u8 uclogic_rdesc_wp5540u_fixed_arr[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x01, /* Usage (Digitizer), */ 0xA1, 0x01, /* Collection (Application), */ @@ -141,7 +142,7 @@ const size_t uclogic_rdesc_wp5540u_fixed_size = sizeof(uclogic_rdesc_wp5540u_fixed_arr); /* Fixed WP8060U report descriptor */ -__u8 uclogic_rdesc_wp8060u_fixed_arr[] = { +const __u8 uclogic_rdesc_wp8060u_fixed_arr[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x01, /* Usage (Digitizer), */ 0xA1, 0x01, /* Collection (Application), */ @@ -218,7 +219,7 @@ const size_t uclogic_rdesc_wp8060u_fixed_size = sizeof(uclogic_rdesc_wp8060u_fixed_arr); /* Fixed WP1062 report descriptor */ -__u8 uclogic_rdesc_wp1062_fixed_arr[] = { +const __u8 uclogic_rdesc_wp1062_fixed_arr[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x01, /* Usage (Digitizer), */ 0xA1, 0x01, /* Collection (Application), */ @@ -266,7 +267,7 @@ const size_t uclogic_rdesc_wp1062_fixed_size = sizeof(uclogic_rdesc_wp1062_fixed_arr); /* Fixed PF1209 report descriptor */ -__u8 uclogic_rdesc_pf1209_fixed_arr[] = { +const __u8 uclogic_rdesc_pf1209_fixed_arr[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x01, /* Usage (Digitizer), */ 0xA1, 0x01, /* Collection (Application), */ @@ -343,7 +344,7 @@ const size_t uclogic_rdesc_pf1209_fixed_size = sizeof(uclogic_rdesc_pf1209_fixed_arr); /* Fixed PID 0522 tablet report descriptor, interface 0 (stylus) */ -__u8 uclogic_rdesc_twhl850_fixed0_arr[] = { +const __u8 uclogic_rdesc_twhl850_fixed0_arr[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x01, /* Usage (Digitizer), */ 0xA1, 0x01, /* Collection (Application), */ @@ -389,7 +390,7 @@ const size_t uclogic_rdesc_twhl850_fixed0_size = sizeof(uclogic_rdesc_twhl850_fixed0_arr); /* Fixed PID 0522 tablet report descriptor, interface 1 (mouse) */ -__u8 uclogic_rdesc_twhl850_fixed1_arr[] = { +const __u8 uclogic_rdesc_twhl850_fixed1_arr[] = { 0x05, 0x01, /* Usage Page (Desktop), */ 0x09, 0x02, /* Usage (Mouse), */ 0xA1, 0x01, /* Collection (Application), */ @@ -429,7 +430,7 @@ const size_t uclogic_rdesc_twhl850_fixed1_size = sizeof(uclogic_rdesc_twhl850_fixed1_arr); /* Fixed PID 0522 tablet report descriptor, interface 2 (frame buttons) */ -__u8 uclogic_rdesc_twhl850_fixed2_arr[] = { +const __u8 uclogic_rdesc_twhl850_fixed2_arr[] = { 0x05, 0x01, /* Usage Page (Desktop), */ 0x09, 0x06, /* Usage (Keyboard), */ 0xA1, 0x01, /* Collection (Application), */ @@ -455,7 +456,7 @@ const size_t uclogic_rdesc_twhl850_fixed2_size = sizeof(uclogic_rdesc_twhl850_fixed2_arr); /* Fixed TWHA60 report descriptor, interface 0 (stylus) */ -__u8 uclogic_rdesc_twha60_fixed0_arr[] = { +const __u8 uclogic_rdesc_twha60_fixed0_arr[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x01, /* Usage (Digitizer), */ 0xA1, 0x01, /* Collection (Application), */ @@ -504,7 +505,7 @@ const size_t uclogic_rdesc_twha60_fixed0_size = sizeof(uclogic_rdesc_twha60_fixed0_arr); /* Fixed TWHA60 report descriptor, interface 1 (frame buttons) */ -__u8 uclogic_rdesc_twha60_fixed1_arr[] = { +const __u8 uclogic_rdesc_twha60_fixed1_arr[] = { 0x05, 0x01, /* Usage Page (Desktop), */ 0x09, 0x06, /* Usage (Keyboard), */ 0xA1, 0x01, /* Collection (Application), */ @@ -689,10 +690,10 @@ const size_t uclogic_rdesc_v2_pen_template_size = 0xA0, /* Collection (Physical), */ \ 0x05, 0x09, /* Usage Page (Button), */ \ 0x19, 0x01, /* Usage Minimum (01h), */ \ - 0x29, 0x03, /* Usage Maximum (03h), */ \ - 0x95, 0x03, /* Report Count (3), */ \ + 0x29, 0x0A, /* Usage Maximum (0Ah), */ \ + 0x95, 0x0A, /* Report Count (10), */ \ 0x81, 0x02, /* Input (Variable), */ \ - 0x95, ((_size) * 8 - 45), \ + 0x95, ((_size) * 8 - 52), \ /* Report Count (padding), */ \ 0x81, 0x01, /* Input (Constant), */ \ 0xC0, /* End Collection, */ \ @@ -789,7 +790,8 @@ const __u8 uclogic_rdesc_v2_frame_touch_strip_arr[] = { 0x95, 0x01, /* Report Count (1), */ 0x81, 0x02, /* Input (Variable), */ 0x05, 0x01, /* Usage Page (Desktop), */ - 0x09, 0x38, /* Usage (Wheel), */ + 0x09, 0x33, /* Usage (Rx), */ + 0x09, 0x34, /* Usage (Ry), */ 0x95, 0x01, /* Report Count (1), */ 0x15, 0x00, /* Logical Minimum (0), */ 0x25, 0x07, /* Logical Maximum (7), */ @@ -1242,3 +1244,4 @@ __u8 *uclogic_rdesc_template_apply(const __u8 *template_ptr, return rdesc_ptr; } +EXPORT_SYMBOL_IF_KUNIT(uclogic_rdesc_template_apply); diff --git a/drivers/hid/hid-uclogic-rdesc.h b/drivers/hid/hid-uclogic-rdesc.h index 906d068f50a9..3878a0e8c464 100644 --- a/drivers/hid/hid-uclogic-rdesc.h +++ b/drivers/hid/hid-uclogic-rdesc.h @@ -23,15 +23,15 @@ #define UCLOGIC_RDESC_WPXXXXU_ORIG_SIZE 212 /* Fixed WP4030U report descriptor */ -extern __u8 uclogic_rdesc_wp4030u_fixed_arr[]; +extern const __u8 uclogic_rdesc_wp4030u_fixed_arr[]; extern const size_t uclogic_rdesc_wp4030u_fixed_size; /* Fixed WP5540U report descriptor */ -extern __u8 uclogic_rdesc_wp5540u_fixed_arr[]; +extern const __u8 uclogic_rdesc_wp5540u_fixed_arr[]; extern const size_t uclogic_rdesc_wp5540u_fixed_size; /* Fixed WP8060U report descriptor */ -extern __u8 uclogic_rdesc_wp8060u_fixed_arr[]; +extern const __u8 uclogic_rdesc_wp8060u_fixed_arr[]; extern const size_t uclogic_rdesc_wp8060u_fixed_size; /* Size of the original descriptor of the new WP5540U tablet */ @@ -41,14 +41,14 @@ extern const size_t uclogic_rdesc_wp8060u_fixed_size; #define UCLOGIC_RDESC_WP1062_ORIG_SIZE 254 /* Fixed WP1062 report descriptor */ -extern __u8 uclogic_rdesc_wp1062_fixed_arr[]; +extern const __u8 uclogic_rdesc_wp1062_fixed_arr[]; extern const size_t uclogic_rdesc_wp1062_fixed_size; /* Size of the original descriptor of PF1209 tablet */ #define UCLOGIC_RDESC_PF1209_ORIG_SIZE 234 /* Fixed PF1209 report descriptor */ -extern __u8 uclogic_rdesc_pf1209_fixed_arr[]; +extern const __u8 uclogic_rdesc_pf1209_fixed_arr[]; extern const size_t uclogic_rdesc_pf1209_fixed_size; /* Size of the original descriptors of TWHL850 tablet */ @@ -57,15 +57,15 @@ extern const size_t uclogic_rdesc_pf1209_fixed_size; #define UCLOGIC_RDESC_TWHL850_ORIG2_SIZE 92 /* Fixed PID 0522 tablet report descriptor, interface 0 (stylus) */ -extern __u8 uclogic_rdesc_twhl850_fixed0_arr[]; +extern const __u8 uclogic_rdesc_twhl850_fixed0_arr[]; extern const size_t uclogic_rdesc_twhl850_fixed0_size; /* Fixed PID 0522 tablet report descriptor, interface 1 (mouse) */ -extern __u8 uclogic_rdesc_twhl850_fixed1_arr[]; +extern const __u8 uclogic_rdesc_twhl850_fixed1_arr[]; extern const size_t uclogic_rdesc_twhl850_fixed1_size; /* Fixed PID 0522 tablet report descriptor, interface 2 (frame buttons) */ -extern __u8 uclogic_rdesc_twhl850_fixed2_arr[]; +extern const __u8 uclogic_rdesc_twhl850_fixed2_arr[]; extern const size_t uclogic_rdesc_twhl850_fixed2_size; /* Size of the original descriptors of TWHA60 tablet */ @@ -73,11 +73,11 @@ extern const size_t uclogic_rdesc_twhl850_fixed2_size; #define UCLOGIC_RDESC_TWHA60_ORIG1_SIZE 139 /* Fixed TWHA60 report descriptor, interface 0 (stylus) */ -extern __u8 uclogic_rdesc_twha60_fixed0_arr[]; +extern const __u8 uclogic_rdesc_twha60_fixed0_arr[]; extern const size_t uclogic_rdesc_twha60_fixed0_size; /* Fixed TWHA60 report descriptor, interface 1 (frame buttons) */ -extern __u8 uclogic_rdesc_twha60_fixed1_arr[]; +extern const __u8 uclogic_rdesc_twha60_fixed1_arr[]; extern const size_t uclogic_rdesc_twha60_fixed1_size; /* Report descriptor template placeholder head */ diff --git a/drivers/hid/hid-viewsonic.c b/drivers/hid/hid-viewsonic.c index 8024b1d370e2..532bed88bdf8 100644 --- a/drivers/hid/hid-viewsonic.c +++ b/drivers/hid/hid-viewsonic.c @@ -22,7 +22,7 @@ #define PD1011_RDESC_ORIG_SIZE 408 /* Fixed report descriptor of PD1011 signature pad */ -static __u8 pd1011_rdesc_fixed[] = { +static const __u8 pd1011_rdesc_fixed[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x01, /* Usage (Digitizer), */ 0xA1, 0x01, /* Collection (Application), */ @@ -70,15 +70,15 @@ static __u8 pd1011_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -static __u8 *viewsonic_report_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) +static const __u8 *viewsonic_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) { switch (hdev->product) { case USB_DEVICE_ID_VIEWSONIC_PD1011: case USB_DEVICE_ID_SIGNOTEC_VIEWSONIC_PD1011: if (*rsize == PD1011_RDESC_ORIG_SIZE) { - rdesc = pd1011_rdesc_fixed; *rsize = sizeof(pd1011_rdesc_fixed); + return pd1011_rdesc_fixed; } break; } @@ -102,4 +102,5 @@ static struct hid_driver viewsonic_driver = { }; module_hid_driver(viewsonic_driver); +MODULE_DESCRIPTION("HID driver for ViewSonic devices not fully compliant with HID standard"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-vivaldi-common.c b/drivers/hid/hid-vivaldi-common.c index b0af2be94895..bf734055d4b6 100644 --- a/drivers/hid/hid-vivaldi-common.c +++ b/drivers/hid/hid-vivaldi-common.c @@ -138,4 +138,5 @@ const struct attribute_group *vivaldi_attribute_groups[] = { }; EXPORT_SYMBOL_GPL(vivaldi_attribute_groups); +MODULE_DESCRIPTION("Helpers for ChromeOS HID Vivaldi keyboards"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-vrc2.c b/drivers/hid/hid-vrc2.c index 80a2b7ef5e66..7dc41e92f488 100644 --- a/drivers/hid/hid-vrc2.c +++ b/drivers/hid/hid-vrc2.c @@ -16,7 +16,7 @@ #define USB_VENDOR_ID_VRC2 (0x07c0) #define USB_DEVICE_ID_VRC2 (0x1125) -static __u8 vrc2_rdesc_fixed[] = { +static const __u8 vrc2_rdesc_fixed[] = { 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 0x09, 0x04, // Usage (Joystick) 0xA1, 0x01, // Collection (Application) @@ -38,8 +38,8 @@ static __u8 vrc2_rdesc_fixed[] = { 0xC0, // End Collection }; -static __u8 *vrc2_report_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) +static const __u8 *vrc2_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) { hid_info(hdev, "fixing up VRC-2 report descriptor\n"); *rsize = sizeof(vrc2_rdesc_fixed); diff --git a/drivers/hid/hid-waltop.c b/drivers/hid/hid-waltop.c index bc355b1a5b30..be34be27d4d5 100644 --- a/drivers/hid/hid-waltop.c +++ b/drivers/hid/hid-waltop.c @@ -43,7 +43,7 @@ #define SLIM_TABLET_5_8_INCH_RDESC_ORIG_SIZE 222 /* Fixed Slim Tablet 5.8 inch descriptor */ -static __u8 slim_tablet_5_8_inch_rdesc_fixed[] = { +static const __u8 slim_tablet_5_8_inch_rdesc_fixed[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x02, /* Usage (Pen), */ 0xA1, 0x01, /* Collection (Application), */ @@ -94,7 +94,7 @@ static __u8 slim_tablet_5_8_inch_rdesc_fixed[] = { #define SLIM_TABLET_12_1_INCH_RDESC_ORIG_SIZE 269 /* Fixed Slim Tablet 12.1 inch descriptor */ -static __u8 slim_tablet_12_1_inch_rdesc_fixed[] = { +static const __u8 slim_tablet_12_1_inch_rdesc_fixed[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x02, /* Usage (Pen), */ 0xA1, 0x01, /* Collection (Application), */ @@ -145,7 +145,7 @@ static __u8 slim_tablet_12_1_inch_rdesc_fixed[] = { #define Q_PAD_RDESC_ORIG_SIZE 241 /* Fixed Q Pad descriptor */ -static __u8 q_pad_rdesc_fixed[] = { +static const __u8 q_pad_rdesc_fixed[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x02, /* Usage (Pen), */ 0xA1, 0x01, /* Collection (Application), */ @@ -198,7 +198,7 @@ static __u8 q_pad_rdesc_fixed[] = { /* * Fixed report descriptor for tablet with PID 0038. */ -static __u8 pid_0038_rdesc_fixed[] = { +static const __u8 pid_0038_rdesc_fixed[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x02, /* Usage (Pen), */ 0xA1, 0x01, /* Collection (Application), */ @@ -249,7 +249,7 @@ static __u8 pid_0038_rdesc_fixed[] = { #define MEDIA_TABLET_10_6_INCH_RDESC_ORIG_SIZE 300 /* Fixed Media Tablet 10.6 inch descriptor */ -static __u8 media_tablet_10_6_inch_rdesc_fixed[] = { +static const __u8 media_tablet_10_6_inch_rdesc_fixed[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x02, /* Usage (Pen), */ 0xA1, 0x01, /* Collection (Application), */ @@ -362,7 +362,7 @@ static __u8 media_tablet_10_6_inch_rdesc_fixed[] = { #define MEDIA_TABLET_14_1_INCH_RDESC_ORIG_SIZE 309 /* Fixed Media Tablet 14.1 inch descriptor */ -static __u8 media_tablet_14_1_inch_rdesc_fixed[] = { +static const __u8 media_tablet_14_1_inch_rdesc_fixed[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x02, /* Usage (Pen), */ 0xA1, 0x01, /* Collection (Application), */ @@ -473,7 +473,7 @@ static __u8 media_tablet_14_1_inch_rdesc_fixed[] = { #define SIRIUS_BATTERY_FREE_TABLET_RDESC_ORIG_SIZE 335 /* Fixed Sirius Battery Free Tablet descriptor */ -static __u8 sirius_battery_free_tablet_rdesc_fixed[] = { +static const __u8 sirius_battery_free_tablet_rdesc_fixed[] = { 0x05, 0x0D, /* Usage Page (Digitizer), */ 0x09, 0x02, /* Usage (Pen), */ 0xA1, 0x01, /* Collection (Application), */ @@ -599,50 +599,50 @@ static __u8 sirius_battery_free_tablet_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -static __u8 *waltop_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *waltop_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { switch (hdev->product) { case USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH: if (*rsize == SLIM_TABLET_5_8_INCH_RDESC_ORIG_SIZE) { - rdesc = slim_tablet_5_8_inch_rdesc_fixed; *rsize = sizeof(slim_tablet_5_8_inch_rdesc_fixed); + return slim_tablet_5_8_inch_rdesc_fixed; } break; case USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH: if (*rsize == SLIM_TABLET_12_1_INCH_RDESC_ORIG_SIZE) { - rdesc = slim_tablet_12_1_inch_rdesc_fixed; *rsize = sizeof(slim_tablet_12_1_inch_rdesc_fixed); + return slim_tablet_12_1_inch_rdesc_fixed; } break; case USB_DEVICE_ID_WALTOP_Q_PAD: if (*rsize == Q_PAD_RDESC_ORIG_SIZE) { - rdesc = q_pad_rdesc_fixed; *rsize = sizeof(q_pad_rdesc_fixed); + return q_pad_rdesc_fixed; } break; case USB_DEVICE_ID_WALTOP_PID_0038: if (*rsize == PID_0038_RDESC_ORIG_SIZE) { - rdesc = pid_0038_rdesc_fixed; *rsize = sizeof(pid_0038_rdesc_fixed); + return pid_0038_rdesc_fixed; } break; case USB_DEVICE_ID_WALTOP_MEDIA_TABLET_10_6_INCH: if (*rsize == MEDIA_TABLET_10_6_INCH_RDESC_ORIG_SIZE) { - rdesc = media_tablet_10_6_inch_rdesc_fixed; *rsize = sizeof(media_tablet_10_6_inch_rdesc_fixed); + return media_tablet_10_6_inch_rdesc_fixed; } break; case USB_DEVICE_ID_WALTOP_MEDIA_TABLET_14_1_INCH: if (*rsize == MEDIA_TABLET_14_1_INCH_RDESC_ORIG_SIZE) { - rdesc = media_tablet_14_1_inch_rdesc_fixed; *rsize = sizeof(media_tablet_14_1_inch_rdesc_fixed); + return media_tablet_14_1_inch_rdesc_fixed; } break; case USB_DEVICE_ID_WALTOP_SIRIUS_BATTERY_FREE_TABLET: if (*rsize == SIRIUS_BATTERY_FREE_TABLET_RDESC_ORIG_SIZE) { - rdesc = sirius_battery_free_tablet_rdesc_fixed; *rsize = sizeof(sirius_battery_free_tablet_rdesc_fixed); + return sirius_battery_free_tablet_rdesc_fixed; } break; } @@ -742,4 +742,5 @@ static struct hid_driver waltop_driver = { }; module_hid_driver(waltop_driver); +MODULE_DESCRIPTION("HID driver for Waltop devices not fully compliant with HID standard"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-winwing.c b/drivers/hid/hid-winwing.c new file mode 100644 index 000000000000..d4afbbd27807 --- /dev/null +++ b/drivers/hid/hid-winwing.c @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * HID driver for WinWing Orion 2 throttle + * + * Copyright (c) 2023 Ivan Gorinov + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/hidraw.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> + +#define MAX_REPORT 16 + +struct winwing_led { + struct led_classdev cdev; + struct hid_device *hdev; + int number; +}; + +struct winwing_led_info { + int number; + int max_brightness; + const char *led_name; +}; + +static const struct winwing_led_info led_info[3] = { + { 0, 255, "backlight" }, + { 1, 1, "a-a" }, + { 2, 1, "a-g" }, +}; + +struct winwing_drv_data { + struct hid_device *hdev; + __u8 *report_buf; + struct mutex lock; + unsigned int num_leds; + struct winwing_led leds[]; +}; + +static int winwing_led_write(struct led_classdev *cdev, + enum led_brightness br) +{ + struct winwing_led *led = (struct winwing_led *) cdev; + struct winwing_drv_data *data = hid_get_drvdata(led->hdev); + __u8 *buf = data->report_buf; + int ret; + + mutex_lock(&data->lock); + + buf[0] = 0x02; + buf[1] = 0x60; + buf[2] = 0xbe; + buf[3] = 0x00; + buf[4] = 0x00; + buf[5] = 0x03; + buf[6] = 0x49; + buf[7] = led->number; + buf[8] = br; + buf[9] = 0x00; + buf[10] = 0; + buf[11] = 0; + buf[12] = 0; + buf[13] = 0; + + ret = hid_hw_output_report(led->hdev, buf, 14); + + mutex_unlock(&data->lock); + + return ret; +} + +static int winwing_init_led(struct hid_device *hdev, + struct input_dev *input) +{ + struct winwing_drv_data *data; + struct winwing_led *led; + int ret; + int i; + + size_t data_size = struct_size(data, leds, 3); + + data = devm_kzalloc(&hdev->dev, data_size, GFP_KERNEL); + + if (!data) + return -ENOMEM; + + data->report_buf = devm_kmalloc(&hdev->dev, MAX_REPORT, GFP_KERNEL); + + if (!data->report_buf) + return -ENOMEM; + + for (i = 0; i < 3; i += 1) { + const struct winwing_led_info *info = &led_info[i]; + + led = &data->leds[i]; + led->hdev = hdev; + led->number = info->number; + led->cdev.max_brightness = info->max_brightness; + led->cdev.brightness_set_blocking = winwing_led_write; + led->cdev.flags = LED_HW_PLUGGABLE; + led->cdev.name = devm_kasprintf(&hdev->dev, GFP_KERNEL, + "%s::%s", + dev_name(&input->dev), + info->led_name); + if (!led->cdev.name) + return -ENOMEM; + + ret = devm_led_classdev_register(&hdev->dev, &led->cdev); + if (ret) + return ret; + } + + hid_set_drvdata(hdev, data); + + return ret; +} + +static int winwing_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int ret; + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "parse failed\n"); + return ret; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + hid_err(hdev, "hw start failed\n"); + return ret; + } + + return 0; +} + +static int winwing_input_configured(struct hid_device *hdev, + struct hid_input *hidinput) +{ + int ret; + + ret = winwing_init_led(hdev, hidinput->input); + + if (ret) + hid_err(hdev, "led init failed\n"); + + return ret; +} + +static const __u8 original_rdesc_buttons[] = { + 0x05, 0x09, 0x19, 0x01, 0x29, 0x6F, + 0x15, 0x00, 0x25, 0x01, 0x35, 0x00, + 0x45, 0x01, 0x75, 0x01, 0x95, 0x6F, + 0x81, 0x02, 0x75, 0x01, 0x95, 0x01, + 0x81, 0x01 +}; + +/* + * HID report descriptor shows 111 buttons, which exceeds maximum + * number of buttons (80) supported by Linux kernel HID subsystem. + * + * This module skips numbers 32-63, unused on some throttle grips. + */ + +static const __u8 *winwing_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + int sig_length = sizeof(original_rdesc_buttons); + int unused_button_numbers = 32; + + if (*rsize < 34) + return rdesc; + + if (memcmp(rdesc + 8, original_rdesc_buttons, sig_length) == 0) { + + /* Usage Maximum */ + rdesc[13] -= unused_button_numbers; + + /* Report Count for buttons */ + rdesc[25] -= unused_button_numbers; + + /* Report Count for padding [HID1_11, 6.2.2.9] */ + rdesc[31] += unused_button_numbers; + + hid_info(hdev, "winwing descriptor fixed\n"); + } + + return rdesc; +} + +static int winwing_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *raw_data, int size) +{ + if (size >= 15) { + /* Skip buttons 32 .. 63 */ + memmove(raw_data + 5, raw_data + 9, 6); + + /* Clear the padding */ + memset(raw_data + 11, 0, 4); + } + + return 0; +} + +static const struct hid_device_id winwing_devices[] = { + { HID_USB_DEVICE(0x4098, 0xbe62) }, /* TGRIP-18 */ + { HID_USB_DEVICE(0x4098, 0xbe68) }, /* TGRIP-16EX */ + {} +}; + +MODULE_DEVICE_TABLE(hid, winwing_devices); + +static struct hid_driver winwing_driver = { + .name = "winwing", + .id_table = winwing_devices, + .probe = winwing_probe, + .input_configured = winwing_input_configured, + .report_fixup = winwing_report_fixup, + .raw_event = winwing_raw_event, +}; +module_hid_driver(winwing_driver); + +MODULE_DESCRIPTION("HID driver for WinWing Orion 2 throttle"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-xiaomi.c b/drivers/hid/hid-xiaomi.c index a97a90afad33..ef6598550a40 100644 --- a/drivers/hid/hid-xiaomi.c +++ b/drivers/hid/hid-xiaomi.c @@ -14,7 +14,7 @@ /* Fixed Mi Silent Mouse report descriptor */ /* Button's Usage Maximum changed from 3 to 5 to make side buttons work */ #define MI_SILENT_MOUSE_ORIG_RDESC_LENGTH 87 -static __u8 mi_silent_mouse_rdesc_fixed[] = { +static const __u8 mi_silent_mouse_rdesc_fixed[] = { 0x05, 0x01, /* Usage Page (Desktop), */ 0x09, 0x02, /* Usage (Mouse), */ 0xA1, 0x01, /* Collection (Application), */ @@ -61,15 +61,15 @@ static __u8 mi_silent_mouse_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -static __u8 *xiaomi_report_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) +static const __u8 *xiaomi_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) { switch (hdev->product) { case USB_DEVICE_ID_MI_SILENT_MOUSE: if (*rsize == MI_SILENT_MOUSE_ORIG_RDESC_LENGTH) { hid_info(hdev, "fixing up Mi Silent Mouse report descriptor\n"); - rdesc = mi_silent_mouse_rdesc_fixed; *rsize = sizeof(mi_silent_mouse_rdesc_fixed); + return mi_silent_mouse_rdesc_fixed; } break; } diff --git a/drivers/hid/hid-xinmo.c b/drivers/hid/hid-xinmo.c index 5c2860a9d8c9..66b8bfb6e647 100644 --- a/drivers/hid/hid-xinmo.c +++ b/drivers/hid/hid-xinmo.c @@ -56,4 +56,5 @@ static struct hid_driver xinmo_driver = { }; module_hid_driver(xinmo_driver); +MODULE_DESCRIPTION("HID driver for Xin-Mo devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-zpff.c b/drivers/hid/hid-zpff.c index 3abaca045869..aacf7f137b18 100644 --- a/drivers/hid/hid-zpff.c +++ b/drivers/hid/hid-zpff.c @@ -138,4 +138,5 @@ static struct hid_driver zp_driver = { }; module_hid_driver(zp_driver); +MODULE_DESCRIPTION("Force feedback support for Zeroplus based devices"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-zydacron.c b/drivers/hid/hid-zydacron.c index 0d003caee113..3bdb26f45592 100644 --- a/drivers/hid/hid-zydacron.c +++ b/drivers/hid/hid-zydacron.c @@ -24,7 +24,7 @@ struct zc_device { * Zydacron remote control has an invalid HID report descriptor, * that needs fixing before we can parse it. */ -static __u8 *zc_report_fixup(struct hid_device *hdev, __u8 *rdesc, +static const __u8 *zc_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { if (*rsize >= 253 && @@ -205,4 +205,5 @@ static struct hid_driver zc_driver = { }; module_hid_driver(zc_driver); +MODULE_DESCRIPTION("HID driver for zydacron remote control"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 2bc762d31ac7..c887f48756f4 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -38,12 +38,20 @@ static const struct class hidraw_class = { static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES]; static DECLARE_RWSEM(minors_rwsem); +static inline bool hidraw_is_revoked(struct hidraw_list *list) +{ + return list->revoked; +} + static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { struct hidraw_list *list = file->private_data; int ret = 0, len; DECLARE_WAITQUEUE(wait, current); + if (hidraw_is_revoked(list)) + return -ENODEV; + mutex_lock(&list->read_mutex); while (ret == 0) { @@ -140,7 +148,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, if ((report_type == HID_OUTPUT_REPORT) && !(dev->quirks & HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP)) { - ret = hid_hw_output_report(dev, buf, count); + ret = __hid_hw_output_report(dev, buf, count, (u64)(long)file, false); /* * compatibility with old implementation of USB-HID and I2C-HID: * if the device does not support receiving output reports, @@ -150,8 +158,8 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, goto out_free; } - ret = hid_hw_raw_request(dev, buf[0], buf, count, report_type, - HID_REQ_SET_REPORT); + ret = __hid_hw_raw_request(dev, buf[0], buf, count, report_type, + HID_REQ_SET_REPORT, (u64)(long)file, false); out_free: kfree(buf); @@ -161,9 +169,13 @@ out: static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { + struct hidraw_list *list = file->private_data; ssize_t ret; down_read(&minors_rwsem); - ret = hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT); + if (hidraw_is_revoked(list)) + ret = -ENODEV; + else + ret = hidraw_send_report(file, buffer, count, HID_OUTPUT_REPORT); up_read(&minors_rwsem); return ret; } @@ -227,8 +239,8 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t goto out_free; } - ret = hid_hw_raw_request(dev, report_number, buf, count, report_type, - HID_REQ_GET_REPORT); + ret = __hid_hw_raw_request(dev, report_number, buf, count, report_type, + HID_REQ_GET_REPORT, (u64)(long)file, false); if (ret < 0) goto out_free; @@ -256,7 +268,7 @@ static __poll_t hidraw_poll(struct file *file, poll_table *wait) poll_wait(file, &list->hidraw->wait, wait); if (list->head != list->tail) mask |= EPOLLIN | EPOLLRDNORM; - if (!list->hidraw->exist) + if (!list->hidraw->exist || hidraw_is_revoked(list)) mask |= EPOLLERR | EPOLLHUP; return mask; } @@ -320,6 +332,9 @@ static int hidraw_fasync(int fd, struct file *file, int on) { struct hidraw_list *list = file->private_data; + if (hidraw_is_revoked(list)) + return -ENODEV; + return fasync_helper(fd, file, on, &list->fasync); } @@ -372,6 +387,13 @@ static int hidraw_release(struct inode * inode, struct file * file) return 0; } +static int hidraw_revoke(struct hidraw_list *list) +{ + list->revoked = true; + + return 0; +} + static long hidraw_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -379,11 +401,12 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, unsigned int minor = iminor(inode); long ret = 0; struct hidraw *dev; + struct hidraw_list *list = file->private_data; void __user *user_arg = (void __user*) arg; down_read(&minors_rwsem); dev = hidraw_table[minor]; - if (!dev || !dev->exist) { + if (!dev || !dev->exist || hidraw_is_revoked(list)) { ret = -ENODEV; goto out; } @@ -421,6 +444,14 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, ret = -EFAULT; break; } + case HIDIOCREVOKE: + { + if (user_arg) + ret = -EINVAL; + else + ret = hidraw_revoke(list); + break; + } default: { struct hid_device *hid = dev->hid; @@ -527,7 +558,7 @@ int hidraw_report_event(struct hid_device *hid, u8 *data, int len) list_for_each_entry(list, &dev->list, node) { int new_head = (list->head + 1) & (HIDRAW_BUFFER_SIZE - 1); - if (new_head == list->tail) + if (hidraw_is_revoked(list) || new_head == list->tail) continue; if (!(list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC))) { diff --git a/drivers/hid/i2c-hid/Kconfig b/drivers/hid/i2c-hid/Kconfig index ef7c595c9403..e8d51f410cc1 100644 --- a/drivers/hid/i2c-hid/Kconfig +++ b/drivers/hid/i2c-hid/Kconfig @@ -2,7 +2,7 @@ menuconfig I2C_HID tristate "I2C HID support" default y - depends on I2C && INPUT && HID + depends on I2C if I2C_HID diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c index d965382196c6..75544448c239 100644 --- a/drivers/hid/i2c-hid/i2c-hid-core.c +++ b/drivers/hid/i2c-hid/i2c-hid-core.c @@ -36,7 +36,7 @@ #include <linux/kernel.h> #include <linux/hid.h> #include <linux/mutex.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <drm/drm_panel.h> @@ -50,6 +50,8 @@ #define I2C_HID_QUIRK_BAD_INPUT_SIZE BIT(3) #define I2C_HID_QUIRK_NO_WAKEUP_AFTER_RESET BIT(4) #define I2C_HID_QUIRK_NO_SLEEP_ON_SUSPEND BIT(5) +#define I2C_HID_QUIRK_DELAY_WAKEUP_AFTER_RESUME BIT(6) +#define I2C_HID_QUIRK_RE_POWER_ON BIT(7) /* Command opcodes */ #define I2C_HID_OPCODE_RESET 0x01 @@ -105,6 +107,7 @@ struct i2c_hid { wait_queue_head_t wait; /* For waiting the interrupt */ + struct mutex cmd_lock; /* protects cmdbuf and rawbuf */ struct mutex reset_lock; struct i2chid_ops *ops; @@ -134,11 +137,18 @@ static const struct i2c_hid_quirks { { I2C_VENDOR_ID_CIRQUE, I2C_PRODUCT_ID_CIRQUE_1063, I2C_HID_QUIRK_NO_SLEEP_ON_SUSPEND }, /* + * Without additional power on command, at least some QTEC devices send garbage + */ + { I2C_VENDOR_ID_QTEC, HID_ANY_ID, + I2C_HID_QUIRK_RE_POWER_ON }, + /* * Sending the wakeup after reset actually break ELAN touchscreen controller */ { USB_VENDOR_ID_ELAN, HID_ANY_ID, I2C_HID_QUIRK_NO_WAKEUP_AFTER_RESET | I2C_HID_QUIRK_BOGUS_IRQ }, + { I2C_VENDOR_ID_GOODIX, I2C_DEVICE_ID_GOODIX_0D42, + I2C_HID_QUIRK_DELAY_WAKEUP_AFTER_RESUME }, { 0, 0 } }; @@ -163,6 +173,24 @@ static u32 i2c_hid_lookup_quirk(const u16 idVendor, const u16 idProduct) return quirks; } +static int i2c_hid_probe_address(struct i2c_hid *ihid) +{ + int ret; + + /* + * Some STM-based devices need 400µs after a rising clock edge to wake + * from deep sleep, in which case the first read will fail. Try after a + * short sleep to see if the device came alive on the bus. Certain + * Weida Tech devices also need this. + */ + ret = i2c_smbus_read_byte(ihid->client); + if (ret < 0) { + usleep_range(400, 500); + ret = i2c_smbus_read_byte(ihid->client); + } + return ret < 0 ? ret : 0; +} + static int i2c_hid_xfer(struct i2c_hid *ihid, u8 *send_buf, int send_len, u8 *recv_buf, int recv_len) { @@ -202,6 +230,8 @@ static int i2c_hid_xfer(struct i2c_hid *ihid, static int i2c_hid_read_register(struct i2c_hid *ihid, __le16 reg, void *buf, size_t len) { + guard(mutex)(&ihid->cmd_lock); + *(__le16 *)ihid->cmdbuf = reg; return i2c_hid_xfer(ihid, ihid->cmdbuf, sizeof(__le16), buf, len); @@ -234,6 +264,8 @@ static int i2c_hid_get_report(struct i2c_hid *ihid, i2c_hid_dbg(ihid, "%s\n", __func__); + guard(mutex)(&ihid->cmd_lock); + /* Command register goes first */ *(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister; length += sizeof(__le16); @@ -324,6 +356,8 @@ static int i2c_hid_set_or_send_report(struct i2c_hid *ihid, if (!do_set && le16_to_cpu(ihid->hdesc.wMaxOutputLength) == 0) return -ENOSYS; + guard(mutex)(&ihid->cmd_lock); + if (do_set) { /* Command register goes first */ *(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister; @@ -366,6 +400,8 @@ static int i2c_hid_set_power_command(struct i2c_hid *ihid, int power_state) { size_t length; + guard(mutex)(&ihid->cmd_lock); + /* SET_POWER uses command register */ *(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister; length = sizeof(__le16); @@ -385,25 +421,22 @@ static int i2c_hid_set_power(struct i2c_hid *ihid, int power_state) i2c_hid_dbg(ihid, "%s\n", __func__); /* - * Some devices require to send a command to wakeup before power on. - * The call will get a return value (EREMOTEIO) but device will be - * triggered and activated. After that, it goes like a normal device. + * Some STM-based devices need 400µs after a rising clock edge to wake + * from deep sleep, in which case the first request will fail due to + * the address not being acknowledged. Try after a short sleep to see + * if the device came alive on the bus. Certain Weida Tech devices also + * need this. */ - if (power_state == I2C_HID_PWR_ON) { + ret = i2c_hid_set_power_command(ihid, power_state); + if (ret && power_state == I2C_HID_PWR_ON) { + usleep_range(400, 500); ret = i2c_hid_set_power_command(ihid, I2C_HID_PWR_ON); - - /* Device was already activated */ - if (!ret) - goto set_pwr_exit; } - ret = i2c_hid_set_power_command(ihid, power_state); if (ret) dev_err(&ihid->client->dev, "failed to change power setting.\n"); -set_pwr_exit: - /* * The HID over I2C specification states that if a DEVICE needs time * after the PWR_ON request, it should utilise CLOCK stretching. @@ -437,25 +470,27 @@ static int i2c_hid_start_hwreset(struct i2c_hid *ihid) if (ret) return ret; - /* Prepare reset command. Command register goes first. */ - *(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister; - length += sizeof(__le16); - /* Next is RESET command itself */ - length += i2c_hid_encode_command(ihid->cmdbuf + length, - I2C_HID_OPCODE_RESET, 0, 0); + scoped_guard(mutex, &ihid->cmd_lock) { + /* Prepare reset command. Command register goes first. */ + *(__le16 *)ihid->cmdbuf = ihid->hdesc.wCommandRegister; + length += sizeof(__le16); + /* Next is RESET command itself */ + length += i2c_hid_encode_command(ihid->cmdbuf + length, + I2C_HID_OPCODE_RESET, 0, 0); - set_bit(I2C_HID_RESET_PENDING, &ihid->flags); + set_bit(I2C_HID_RESET_PENDING, &ihid->flags); - ret = i2c_hid_xfer(ihid, ihid->cmdbuf, length, NULL, 0); - if (ret) { - dev_err(&ihid->client->dev, - "failed to reset device: %d\n", ret); - goto err_clear_reset; - } + ret = i2c_hid_xfer(ihid, ihid->cmdbuf, length, NULL, 0); + if (ret) { + dev_err(&ihid->client->dev, + "failed to reset device: %d\n", ret); + break; + } - return 0; + return 0; + } -err_clear_reset: + /* Clean up if sending reset command failed */ clear_bit(I2C_HID_RESET_PENDING, &ihid->flags); i2c_hid_set_power(ihid, I2C_HID_PWR_SLEEP); return ret; @@ -959,6 +994,13 @@ static int i2c_hid_core_resume(struct i2c_hid *ihid) enable_irq(client->irq); + /* On Goodix 27c6:0d42 wait extra time before device wakeup. + * It's not clear why but if we send wakeup too early, the device will + * never trigger input interrupts. + */ + if (ihid->quirks & I2C_HID_QUIRK_DELAY_WAKEUP_AFTER_RESUME) + msleep(1500); + /* Instead of resetting device, simply powers the device on. This * solves "incomplete reports" on Raydium devices 2386:3118 and * 2386:4B33 and fixes various SIS touchscreens no longer sending @@ -992,8 +1034,7 @@ static int __i2c_hid_core_probe(struct i2c_hid *ihid) struct hid_device *hid = ihid->hid; int ret; - /* Make sure there is something at this address */ - ret = i2c_smbus_read_byte(client); + ret = i2c_hid_probe_address(ihid); if (ret < 0) { i2c_hid_dbg(ihid, "nothing at this address: %d\n", ret); return -ENXIO; @@ -1038,7 +1079,11 @@ static int i2c_hid_core_register_hid(struct i2c_hid *ihid) return ret; } - return 0; + /* At least some QTEC devices need this after initialization */ + if (ihid->quirks & I2C_HID_QUIRK_RE_POWER_ON) + ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON); + + return ret; } static int i2c_hid_core_probe_panel_follower(struct i2c_hid *ihid) @@ -1190,6 +1235,7 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops, ihid->is_panel_follower = drm_is_panel_follower(&client->dev); init_waitqueue_head(&ihid->wait); + mutex_init(&ihid->cmd_lock); mutex_init(&ihid->reset_lock); INIT_WORK(&ihid->panel_follower_prepare_work, ihid_core_panel_prepare_work); diff --git a/drivers/hid/i2c-hid/i2c-hid-of-elan.c b/drivers/hid/i2c-hid/i2c-hid-of-elan.c index 5b91fb106cfc..3fcff6daa0d3 100644 --- a/drivers/hid/i2c-hid/i2c-hid-of-elan.c +++ b/drivers/hid/i2c-hid/i2c-hid-of-elan.c @@ -31,6 +31,7 @@ struct i2c_hid_of_elan { struct regulator *vcc33; struct regulator *vccio; struct gpio_desc *reset_gpio; + bool no_reset_on_power_off; const struct elan_i2c_hid_chip_data *chip_data; }; @@ -40,17 +41,17 @@ static int elan_i2c_hid_power_up(struct i2chid_ops *ops) container_of(ops, struct i2c_hid_of_elan, ops); int ret; + gpiod_set_value_cansleep(ihid_elan->reset_gpio, 1); + if (ihid_elan->vcc33) { ret = regulator_enable(ihid_elan->vcc33); if (ret) - return ret; + goto err_deassert_reset; } ret = regulator_enable(ihid_elan->vccio); - if (ret) { - regulator_disable(ihid_elan->vcc33); - return ret; - } + if (ret) + goto err_disable_vcc33; if (ihid_elan->chip_data->post_power_delay_ms) msleep(ihid_elan->chip_data->post_power_delay_ms); @@ -60,6 +61,15 @@ static int elan_i2c_hid_power_up(struct i2chid_ops *ops) msleep(ihid_elan->chip_data->post_gpio_reset_on_delay_ms); return 0; + +err_disable_vcc33: + if (ihid_elan->vcc33) + regulator_disable(ihid_elan->vcc33); +err_deassert_reset: + if (ihid_elan->no_reset_on_power_off) + gpiod_set_value_cansleep(ihid_elan->reset_gpio, 0); + + return ret; } static void elan_i2c_hid_power_down(struct i2chid_ops *ops) @@ -67,7 +77,14 @@ static void elan_i2c_hid_power_down(struct i2chid_ops *ops) struct i2c_hid_of_elan *ihid_elan = container_of(ops, struct i2c_hid_of_elan, ops); - gpiod_set_value_cansleep(ihid_elan->reset_gpio, 1); + /* + * Do not assert reset when the hardware allows for it to remain + * deasserted regardless of the state of the (shared) power supply to + * avoid wasting power when the supply is left on. + */ + if (!ihid_elan->no_reset_on_power_off) + gpiod_set_value_cansleep(ihid_elan->reset_gpio, 1); + if (ihid_elan->chip_data->post_gpio_reset_off_delay_ms) msleep(ihid_elan->chip_data->post_gpio_reset_off_delay_ms); @@ -79,6 +96,7 @@ static void elan_i2c_hid_power_down(struct i2chid_ops *ops) static int i2c_hid_of_elan_probe(struct i2c_client *client) { struct i2c_hid_of_elan *ihid_elan; + int ret; ihid_elan = devm_kzalloc(&client->dev, sizeof(*ihid_elan), GFP_KERNEL); if (!ihid_elan) @@ -93,21 +111,38 @@ static int i2c_hid_of_elan_probe(struct i2c_client *client) if (IS_ERR(ihid_elan->reset_gpio)) return PTR_ERR(ihid_elan->reset_gpio); + ihid_elan->no_reset_on_power_off = of_property_read_bool(client->dev.of_node, + "no-reset-on-power-off"); + ihid_elan->vccio = devm_regulator_get(&client->dev, "vccio"); - if (IS_ERR(ihid_elan->vccio)) - return PTR_ERR(ihid_elan->vccio); + if (IS_ERR(ihid_elan->vccio)) { + ret = PTR_ERR(ihid_elan->vccio); + goto err_deassert_reset; + } ihid_elan->chip_data = device_get_match_data(&client->dev); if (ihid_elan->chip_data->main_supply_name) { ihid_elan->vcc33 = devm_regulator_get(&client->dev, ihid_elan->chip_data->main_supply_name); - if (IS_ERR(ihid_elan->vcc33)) - return PTR_ERR(ihid_elan->vcc33); + if (IS_ERR(ihid_elan->vcc33)) { + ret = PTR_ERR(ihid_elan->vcc33); + goto err_deassert_reset; + } } - return i2c_hid_core_probe(client, &ihid_elan->ops, - ihid_elan->chip_data->hid_descriptor_address, 0); + ret = i2c_hid_core_probe(client, &ihid_elan->ops, + ihid_elan->chip_data->hid_descriptor_address, 0); + if (ret) + goto err_deassert_reset; + + return 0; + +err_deassert_reset: + if (ihid_elan->no_reset_on_power_off) + gpiod_set_value_cansleep(ihid_elan->reset_gpio, 0); + + return ret; } static const struct elan_i2c_hid_chip_data elan_ekth6915_chip_data = { @@ -117,6 +152,13 @@ static const struct elan_i2c_hid_chip_data elan_ekth6915_chip_data = { .main_supply_name = "vcc33", }; +static const struct elan_i2c_hid_chip_data elan_ekth6a12nay_chip_data = { + .post_power_delay_ms = 10, + .post_gpio_reset_on_delay_ms = 300, + .hid_descriptor_address = 0x0001, + .main_supply_name = "vcc33", +}; + static const struct elan_i2c_hid_chip_data ilitek_ili9882t_chip_data = { .post_power_delay_ms = 1, .post_gpio_reset_on_delay_ms = 200, @@ -139,6 +181,7 @@ static const struct elan_i2c_hid_chip_data ilitek_ili2901_chip_data = { static const struct of_device_id elan_i2c_hid_of_match[] = { { .compatible = "elan,ekth6915", .data = &elan_ekth6915_chip_data }, + { .compatible = "elan,ekth6a12nay", .data = &elan_ekth6a12nay_chip_data }, { .compatible = "ilitek,ili9882t", .data = &ilitek_ili9882t_chip_data }, { .compatible = "ilitek,ili2901", .data = &ilitek_ili2901_chip_data }, { } diff --git a/drivers/hid/i2c-hid/i2c-hid-of.c b/drivers/hid/i2c-hid/i2c-hid-of.c index 8be4d576da77..57379b77e977 100644 --- a/drivers/hid/i2c-hid/i2c-hid-of.c +++ b/drivers/hid/i2c-hid/i2c-hid-of.c @@ -144,9 +144,9 @@ MODULE_DEVICE_TABLE(of, i2c_hid_of_match); #endif static const struct i2c_device_id i2c_hid_of_id_table[] = { - { "hid", 0 }, - { "hid-over-i2c", 0 }, - { }, + { "hid" }, + { "hid-over-i2c" }, + { } }; MODULE_DEVICE_TABLE(i2c, i2c_hid_of_id_table); diff --git a/drivers/hid/intel-ish-hid/Kconfig b/drivers/hid/intel-ish-hid/Kconfig index 253dc10d35ef..568c8688784e 100644 --- a/drivers/hid/intel-ish-hid/Kconfig +++ b/drivers/hid/intel-ish-hid/Kconfig @@ -6,7 +6,6 @@ config INTEL_ISH_HID tristate "Intel Integrated Sensor Hub" default n depends on X86 - depends on HID help The Integrated Sensor Hub (ISH) enables the ability to offload sensor polling and algorithm processing to a dedicated low power diff --git a/drivers/hid/intel-ish-hid/Makefile b/drivers/hid/intel-ish-hid/Makefile index f0a82b1c7cb9..e1e062e4b542 100644 --- a/drivers/hid/intel-ish-hid/Makefile +++ b/drivers/hid/intel-ish-hid/Makefile @@ -11,6 +11,7 @@ intel-ishtp-objs += ishtp/client.o intel-ishtp-objs += ishtp/bus.o intel-ishtp-objs += ishtp/dma-if.o intel-ishtp-objs += ishtp/client-buffers.o +intel-ishtp-objs += ishtp/loader.o obj-$(CONFIG_INTEL_ISH_HID) += intel-ish-ipc.o intel-ish-ipc-objs := ipc/ipc.o @@ -23,4 +24,4 @@ intel-ishtp-hid-objs += ishtp-hid-client.o obj-$(CONFIG_INTEL_ISH_FIRMWARE_DOWNLOADER) += intel-ishtp-loader.o intel-ishtp-loader-objs += ishtp-fw-loader.o -ccflags-y += -I $(srctree)/$(src)/ishtp +ccflags-y += -I $(src)/ishtp diff --git a/drivers/hid/intel-ish-hid/ipc/hw-ish.h b/drivers/hid/intel-ish-hid/ipc/hw-ish.h index f89b300417d7..07e90d51f073 100644 --- a/drivers/hid/intel-ish-hid/ipc/hw-ish.h +++ b/drivers/hid/intel-ish-hid/ipc/hw-ish.h @@ -13,28 +13,31 @@ #include "hw-ish-regs.h" #include "ishtp-dev.h" -#define CHV_DEVICE_ID 0x22D8 -#define BXT_Ax_DEVICE_ID 0x0AA2 -#define BXT_Bx_DEVICE_ID 0x1AA2 -#define APL_Ax_DEVICE_ID 0x5AA2 -#define SPT_Ax_DEVICE_ID 0x9D35 -#define CNL_Ax_DEVICE_ID 0x9DFC -#define GLK_Ax_DEVICE_ID 0x31A2 -#define CNL_H_DEVICE_ID 0xA37C -#define ICL_MOBILE_DEVICE_ID 0x34FC -#define SPT_H_DEVICE_ID 0xA135 -#define CML_LP_DEVICE_ID 0x02FC -#define CMP_H_DEVICE_ID 0x06FC -#define EHL_Ax_DEVICE_ID 0x4BB3 -#define TGL_LP_DEVICE_ID 0xA0FC -#define TGL_H_DEVICE_ID 0x43FC -#define ADL_S_DEVICE_ID 0x7AF8 -#define ADL_P_DEVICE_ID 0x51FC -#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 ARL_S_DEVICE_ID 0x7F78 +#define PCI_DEVICE_ID_INTEL_ISH_CHV 0x22D8 +#define PCI_DEVICE_ID_INTEL_ISH_BXT_Ax 0x0AA2 +#define PCI_DEVICE_ID_INTEL_ISH_BXT_Bx 0x1AA2 +#define PCI_DEVICE_ID_INTEL_ISH_APL_Ax 0x5AA2 +#define PCI_DEVICE_ID_INTEL_ISH_SPT_Ax 0x9D35 +#define PCI_DEVICE_ID_INTEL_ISH_CNL_Ax 0x9DFC +#define PCI_DEVICE_ID_INTEL_ISH_GLK_Ax 0x31A2 +#define PCI_DEVICE_ID_INTEL_ISH_CNL_H 0xA37C +#define PCI_DEVICE_ID_INTEL_ISH_ICL_MOBILE 0x34FC +#define PCI_DEVICE_ID_INTEL_ISH_SPT_H 0xA135 +#define PCI_DEVICE_ID_INTEL_ISH_CML_LP 0x02FC +#define PCI_DEVICE_ID_INTEL_ISH_CMP_H 0x06FC +#define PCI_DEVICE_ID_INTEL_ISH_EHL_Ax 0x4BB3 +#define PCI_DEVICE_ID_INTEL_ISH_TGL_LP 0xA0FC +#define PCI_DEVICE_ID_INTEL_ISH_TGL_H 0x43FC +#define PCI_DEVICE_ID_INTEL_ISH_ADL_S 0x7AF8 +#define PCI_DEVICE_ID_INTEL_ISH_ADL_P 0x51FC +#define PCI_DEVICE_ID_INTEL_ISH_ADL_N 0x54FC +#define PCI_DEVICE_ID_INTEL_ISH_RPL_S 0x7A78 +#define PCI_DEVICE_ID_INTEL_ISH_MTL_P 0x7E45 +#define PCI_DEVICE_ID_INTEL_ISH_ARL_H 0x7745 +#define PCI_DEVICE_ID_INTEL_ISH_ARL_S 0x7F78 +#define PCI_DEVICE_ID_INTEL_ISH_LNL_M 0xA845 +#define PCI_DEVICE_ID_INTEL_ISH_PTL_H 0xE345 +#define PCI_DEVICE_ID_INTEL_ISH_PTL_P 0xE445 #define REVISION_ID_CHT_A0 0x6 #define REVISION_ID_CHT_Ax_SI 0x0 diff --git a/drivers/hid/intel-ish-hid/ipc/ipc.c b/drivers/hid/intel-ish-hid/ipc/ipc.c index dd5fc60874ba..4c861119e97a 100644 --- a/drivers/hid/intel-ish-hid/ipc/ipc.c +++ b/drivers/hid/intel-ish-hid/ipc/ipc.c @@ -78,7 +78,7 @@ static bool check_generated_interrupt(struct ishtp_device *dev) bool interrupt_generated = true; uint32_t pisr_val = 0; - if (dev->pdev->device == CHV_DEVICE_ID) { + if (dev->pdev->device == PCI_DEVICE_ID_INTEL_ISH_CHV) { pisr_val = ish_reg_read(dev, IPC_REG_PISR_CHV_AB); interrupt_generated = IPC_INT_FROM_ISH_TO_HOST_CHV_AB(pisr_val); @@ -117,7 +117,7 @@ static bool ish_is_input_ready(struct ishtp_device *dev) */ static void set_host_ready(struct ishtp_device *dev) { - if (dev->pdev->device == CHV_DEVICE_ID) { + if (dev->pdev->device == PCI_DEVICE_ID_INTEL_ISH_CHV) { if (dev->pdev->revision == REVISION_ID_CHT_A0 || (dev->pdev->revision & REVISION_ID_SI_MASK) == REVISION_ID_CHT_Ax_SI) @@ -517,6 +517,10 @@ static int ish_fw_reset_handler(struct ishtp_device *dev) /* ISH FW is dead */ if (!ish_is_input_ready(dev)) return -EPIPE; + + /* Send clock sync at once after reset */ + ishtp_dev->prev_sync = 0; + /* * Set HOST2ISH.ILUP. Apparently we need this BEFORE sending * RESET_NOTIFY_ACK - FW will be checking for it @@ -546,11 +550,11 @@ static int ish_fw_reset_handler(struct ishtp_device *dev) /** * fw_reset_work_fn() - FW reset worker function - * @unused: not used + * @work: Work item * * Call ish_fw_reset_handler to complete FW reset */ -static void fw_reset_work_fn(struct work_struct *unused) +static void fw_reset_work_fn(struct work_struct *work) { int rv; @@ -562,7 +566,8 @@ static void fw_reset_work_fn(struct work_struct *unused) wake_up_interruptible(&ishtp_dev->wait_hw_ready); /* ISHTP notification in IPC_RESET sequence completion */ - ishtp_reset_compl_handler(ishtp_dev); + if (!work_pending(work)) + ishtp_reset_compl_handler(ishtp_dev); } else dev_err(ishtp_dev->devc, "[ishtp-ish]: FW reset failed (%d)\n", rv); @@ -576,15 +581,14 @@ static void fw_reset_work_fn(struct work_struct *unused) */ static void _ish_sync_fw_clock(struct ishtp_device *dev) { - static unsigned long prev_sync; - uint64_t usec; + struct ipc_time_update_msg time = {}; - if (prev_sync && time_before(jiffies, prev_sync + 20 * HZ)) + if (dev->prev_sync && time_before(jiffies, dev->prev_sync + 20 * HZ)) return; - prev_sync = jiffies; - usec = ktime_to_us(ktime_get_boottime()); - ipc_send_mng_msg(dev, MNG_SYNC_FW_CLOCK, &usec, sizeof(uint64_t)); + dev->prev_sync = jiffies; + /* The fields of time would be updated while sending message */ + ipc_send_mng_msg(dev, MNG_SYNC_FW_CLOCK, &time, sizeof(time)); } /** @@ -909,11 +913,11 @@ static uint32_t ish_ipc_get_header(struct ishtp_device *dev, int length, */ static bool _dma_no_cache_snooping(struct ishtp_device *dev) { - return (dev->pdev->device == EHL_Ax_DEVICE_ID || - dev->pdev->device == TGL_LP_DEVICE_ID || - dev->pdev->device == TGL_H_DEVICE_ID || - dev->pdev->device == ADL_S_DEVICE_ID || - dev->pdev->device == ADL_P_DEVICE_ID); + return (dev->pdev->device == PCI_DEVICE_ID_INTEL_ISH_EHL_Ax || + dev->pdev->device == PCI_DEVICE_ID_INTEL_ISH_TGL_LP || + dev->pdev->device == PCI_DEVICE_ID_INTEL_ISH_TGL_H || + dev->pdev->device == PCI_DEVICE_ID_INTEL_ISH_ADL_S || + dev->pdev->device == PCI_DEVICE_ID_INTEL_ISH_ADL_P); } static const struct ishtp_hw_ops ish_hw_ops = { diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c b/drivers/hid/intel-ish-hid/ipc/pci-ish.c index 56bd4f02f319..ff0fc8010072 100644 --- a/drivers/hid/intel-ish-hid/ipc/pci-ish.c +++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c @@ -23,30 +23,54 @@ #include "ishtp-dev.h" #include "hw-ish.h" +enum ishtp_driver_data_index { + ISHTP_DRIVER_DATA_NONE, + ISHTP_DRIVER_DATA_LNL_M, + ISHTP_DRIVER_DATA_PTL, +}; + +#define ISH_FW_GEN_LNL_M "lnlm" +#define ISH_FW_GEN_PTL "ptl" + +#define ISH_FIRMWARE_PATH(gen) "intel/ish/ish_" gen ".bin" +#define ISH_FIRMWARE_PATH_ALL "intel/ish/ish_*.bin" + +static struct ishtp_driver_data ishtp_driver_data[] = { + [ISHTP_DRIVER_DATA_LNL_M] = { + .fw_generation = ISH_FW_GEN_LNL_M, + }, + [ISHTP_DRIVER_DATA_PTL] = { + .fw_generation = ISH_FW_GEN_PTL, + }, +}; + static const struct pci_device_id ish_pci_tbl[] = { - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CHV_DEVICE_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Ax_DEVICE_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, BXT_Bx_DEVICE_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, APL_Ax_DEVICE_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_Ax_DEVICE_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_Ax_DEVICE_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, GLK_Ax_DEVICE_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_H_DEVICE_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ICL_MOBILE_DEVICE_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_H_DEVICE_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CML_LP_DEVICE_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, CMP_H_DEVICE_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, EHL_Ax_DEVICE_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, TGL_LP_DEVICE_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, TGL_H_DEVICE_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ADL_S_DEVICE_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ADL_P_DEVICE_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ADL_N_DEVICE_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, RPL_S_DEVICE_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MTL_P_DEVICE_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ARL_H_DEVICE_ID)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, ARL_S_DEVICE_ID)}, - {0, } + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_CHV)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_BXT_Ax)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_BXT_Bx)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_APL_Ax)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_SPT_Ax)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_CNL_Ax)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_GLK_Ax)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_CNL_H)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_ICL_MOBILE)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_SPT_H)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_CML_LP)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_CMP_H)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_EHL_Ax)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_TGL_LP)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_TGL_H)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_ADL_S)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_ADL_P)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_ADL_N)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_RPL_S)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_MTL_P)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_ARL_H)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_ARL_S)}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_LNL_M), .driver_data = ISHTP_DRIVER_DATA_LNL_M}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_PTL_H), .driver_data = ISHTP_DRIVER_DATA_PTL}, + {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ISH_PTL_P), .driver_data = ISHTP_DRIVER_DATA_PTL}, + {} }; MODULE_DEVICE_TABLE(pci, ish_pci_tbl); @@ -105,19 +129,19 @@ static int ish_init(struct ishtp_device *dev) static const struct pci_device_id ish_invalid_pci_ids[] = { /* Mehlow platform special pci ids */ - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xA309)}, - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xA30A)}, + {PCI_VDEVICE(INTEL, 0xA309)}, + {PCI_VDEVICE(INTEL, 0xA30A)}, {} }; static inline bool ish_should_enter_d0i3(struct pci_dev *pdev) { - return !pm_suspend_via_firmware() || pdev->device == CHV_DEVICE_ID; + return !pm_suspend_via_firmware() || pdev->device == PCI_DEVICE_ID_INTEL_ISH_CHV; } static inline bool ish_should_leave_d0i3(struct pci_dev *pdev) { - return !pm_resume_via_firmware() || pdev->device == CHV_DEVICE_ID; + return !pm_resume_via_firmware() || pdev->device == PCI_DEVICE_ID_INTEL_ISH_CHV; } /** @@ -166,6 +190,7 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } hw = to_ish_hw(ishtp); ishtp->print_log = ish_event_tracer; + ishtp->driver_data = &ishtp_driver_data[ent->driver_data]; /* mapping IO device memory */ hw->mem_addr = pcim_iomap_table(pdev)[0]; @@ -173,6 +198,11 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* request and enable interrupt */ ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); + if (ret < 0) { + dev_err(dev, "ISH: Failed to allocate IRQ vectors\n"); + return ret; + } + if (!pdev->msi_enabled && !pdev->msix_enabled) irq_flag = IRQF_SHARED; @@ -189,7 +219,7 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent) init_waitqueue_head(&ishtp->resume_wait); /* Enable PME for EHL */ - if (pdev->device == EHL_Ax_DEVICE_ID) + if (pdev->device == PCI_DEVICE_ID_INTEL_ISH_EHL_Ax) device_init_wakeup(dev, true); ret = ish_init(ishtp); @@ -222,7 +252,7 @@ static void ish_remove(struct pci_dev *pdev) */ static void ish_shutdown(struct pci_dev *pdev) { - if (pdev->device == EHL_Ax_DEVICE_ID) + if (pdev->device == PCI_DEVICE_ID_INTEL_ISH_EHL_Ax) pci_prepare_to_sleep(pdev); } @@ -358,6 +388,50 @@ static int __maybe_unused ish_resume(struct device *device) static SIMPLE_DEV_PM_OPS(ish_pm_ops, ish_suspend, ish_resume); +static ssize_t base_version_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct ishtp_device *dev = dev_get_drvdata(cdev); + + return sysfs_emit(buf, "%u.%u.%u.%u\n", dev->base_ver.major, + dev->base_ver.minor, dev->base_ver.hotfix, + dev->base_ver.build); +} +static DEVICE_ATTR_RO(base_version); + +static ssize_t project_version_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + struct ishtp_device *dev = dev_get_drvdata(cdev); + + return sysfs_emit(buf, "%u.%u.%u.%u\n", dev->prj_ver.major, + dev->prj_ver.minor, dev->prj_ver.hotfix, + dev->prj_ver.build); +} +static DEVICE_ATTR_RO(project_version); + +static struct attribute *ish_firmware_attrs[] = { + &dev_attr_base_version.attr, + &dev_attr_project_version.attr, + NULL +}; + +static umode_t firmware_is_visible(struct kobject *kobj, struct attribute *attr, + int i) +{ + struct ishtp_device *dev = dev_get_drvdata(kobj_to_dev(kobj)); + + return dev->driver_data->fw_generation ? attr->mode : 0; +} + +static const struct attribute_group ish_firmware_group = { + .name = "firmware", + .attrs = ish_firmware_attrs, + .is_visible = firmware_is_visible, +}; + +__ATTRIBUTE_GROUPS(ish_firmware); + static struct pci_driver ish_driver = { .name = KBUILD_MODNAME, .id_table = ish_pci_tbl, @@ -365,6 +439,7 @@ static struct pci_driver ish_driver = { .remove = ish_remove, .shutdown = ish_shutdown, .driver.pm = &ish_pm_ops, + .dev_groups = ish_firmware_groups, }; module_pci_driver(ish_driver); @@ -376,3 +451,6 @@ MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); MODULE_DESCRIPTION("Intel(R) Integrated Sensor Hub PCI Device Driver"); MODULE_LICENSE("GPL"); + +MODULE_FIRMWARE(ISH_FIRMWARE_PATH(ISH_FW_GEN_LNL_M)); +MODULE_FIRMWARE(ISH_FIRMWARE_PATH_ALL); diff --git a/drivers/hid/intel-ish-hid/ishtp-fw-loader.c b/drivers/hid/intel-ish-hid/ishtp-fw-loader.c index e157863a8b25..f4a671d6386c 100644 --- a/drivers/hid/intel-ish-hid/ishtp-fw-loader.c +++ b/drivers/hid/intel-ish-hid/ishtp-fw-loader.c @@ -635,7 +635,7 @@ static int ish_fw_xfer_direct_dma(struct ishtp_cl_data *client_data, const struct firmware *fw, const struct shim_fw_info fw_info) { - int rv; + int rv = 0; void *dma_buf; dma_addr_t dma_buf_phy; u32 fragment_offset, fragment_size, payload_max_size; @@ -793,7 +793,7 @@ static int load_fw_from_host(struct ishtp_cl_data *client_data) if (rv < 0) goto end_err_fw_release; - /* Step 3: Start ISH main firmware exeuction */ + /* Step 3: Start ISH main firmware execution */ rv = ish_fw_start(client_data); if (rv < 0) diff --git a/drivers/hid/intel-ish-hid/ishtp-hid-client.c b/drivers/hid/intel-ish-hid/ishtp-hid-client.c index fbd4f8ea1951..cb04cd1d980b 100644 --- a/drivers/hid/intel-ish-hid/ishtp-hid-client.c +++ b/drivers/hid/intel-ish-hid/ishtp-hid-client.c @@ -70,10 +70,10 @@ static void process_recv(struct ishtp_cl *hid_ishtp_cl, void *recv_buf, unsigned char *payload; struct device_info *dev_info; int i, j; - size_t payload_len, total_len, cur_pos, raw_len; + size_t payload_len, total_len, cur_pos, raw_len, msg_len; int report_type; struct report_list *reports_list; - char *reports; + struct report *report; size_t report_len; struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl); int curr_hid_dev = client_data->cur_hid_dev; @@ -280,14 +280,13 @@ do_get_report: case HOSTIF_PUBLISH_INPUT_REPORT_LIST: report_type = HID_INPUT_REPORT; reports_list = (struct report_list *)payload; - reports = (char *)reports_list->reports; + report = reports_list->reports; for (j = 0; j < reports_list->num_of_reports; j++) { - recv_msg = (struct hostif_msg *)(reports + - sizeof(uint16_t)); - report_len = *(uint16_t *)reports; - payload = reports + sizeof(uint16_t) + - sizeof(struct hostif_msg_hdr); + recv_msg = container_of(&report->msg, + struct hostif_msg, hdr); + report_len = report->size; + payload = recv_msg->payload; payload_len = report_len - sizeof(struct hostif_msg_hdr); @@ -304,7 +303,7 @@ do_get_report: 0); } - reports += sizeof(uint16_t) + report_len; + report += sizeof(*report) + payload_len; } break; default: @@ -316,12 +315,12 @@ do_get_report: } - if (!cur_pos && cur_pos + payload_len + - sizeof(struct hostif_msg) < total_len) + msg_len = payload_len + sizeof(struct hostif_msg); + if (!cur_pos && cur_pos + msg_len < total_len) ++client_data->multi_packet_cnt; - cur_pos += payload_len + sizeof(struct hostif_msg); - payload += payload_len + sizeof(struct hostif_msg); + cur_pos += msg_len; + payload += msg_len; } while (cur_pos < total_len); } diff --git a/drivers/hid/intel-ish-hid/ishtp-hid.h b/drivers/hid/intel-ish-hid/ishtp-hid.h index 35dddc5015b3..2bc19e8ba13e 100644 --- a/drivers/hid/intel-ish-hid/ishtp-hid.h +++ b/drivers/hid/intel-ish-hid/ishtp-hid.h @@ -31,6 +31,7 @@ struct hostif_msg_hdr { struct hostif_msg { struct hostif_msg_hdr hdr; + uint8_t payload[]; } __packed; struct hostif_msg_to_sensor { @@ -52,15 +53,17 @@ struct ishtp_version { uint16_t build; } __packed; +struct report { + uint16_t size; + struct hostif_msg_hdr msg; +} __packed; + /* struct for ISHTP aggregated input data */ struct report_list { uint16_t total_size; uint8_t num_of_reports; uint8_t flags; - struct { - uint16_t size_of_report; - uint8_t report[1]; - } __packed reports[1]; + struct report reports[]; } __packed; /* HOSTIF commands */ diff --git a/drivers/hid/intel-ish-hid/ishtp/bus.c b/drivers/hid/intel-ish-hid/ishtp/bus.c index 03d5601ce807..5ac7d70a7c84 100644 --- a/drivers/hid/intel-ish-hid/ishtp/bus.c +++ b/drivers/hid/intel-ish-hid/ishtp/bus.c @@ -236,7 +236,7 @@ static int ishtp_cl_device_probe(struct device *dev) * * Return: 1 if dev & drv matches, 0 otherwise. */ -static int ishtp_cl_bus_match(struct device *dev, struct device_driver *drv) +static int ishtp_cl_bus_match(struct device *dev, const struct device_driver *drv) { struct ishtp_cl_device *device = to_ishtp_cl_device(dev); struct ishtp_cl_driver *driver = to_ishtp_cl_driver(drv); @@ -844,6 +844,7 @@ EXPORT_SYMBOL(ishtp_device); /** * ishtp_wait_resume() - Wait for IPC resume + * @dev: ishtp device * * Wait for IPC resume * @@ -931,4 +932,5 @@ static void __exit ishtp_bus_unregister(void) module_init(ishtp_bus_register); module_exit(ishtp_bus_unregister); +MODULE_DESCRIPTION("ISHTP bus driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hid/intel-ish-hid/ishtp/bus.h b/drivers/hid/intel-ish-hid/ishtp/bus.h index 5bb85c932e4c..53645ac89ee8 100644 --- a/drivers/hid/intel-ish-hid/ishtp/bus.h +++ b/drivers/hid/intel-ish-hid/ishtp/bus.h @@ -46,7 +46,6 @@ struct ishtp_cl_device { }; int ishtp_bus_new_client(struct ishtp_device *dev); -void ishtp_remove_all_clients(struct ishtp_device *dev); int ishtp_cl_device_bind(struct ishtp_cl *cl); void ishtp_cl_bus_rx_event(struct ishtp_cl_device *device); diff --git a/drivers/hid/intel-ish-hid/ishtp/client-buffers.c b/drivers/hid/intel-ish-hid/ishtp/client-buffers.c index 513d7a4a1b8a..97f4026b1627 100644 --- a/drivers/hid/intel-ish-hid/ishtp/client-buffers.c +++ b/drivers/hid/intel-ish-hid/ishtp/client-buffers.c @@ -252,27 +252,6 @@ int ishtp_cl_io_rb_recycle(struct ishtp_cl_rb *rb) EXPORT_SYMBOL(ishtp_cl_io_rb_recycle); /** - * ishtp_cl_tx_empty() -test whether client device tx buffer is empty - * @cl: Pointer to client device instance - * - * Look client device tx buffer list, and check whether this list is empty - * - * Return: true if client tx buffer list is empty else false - */ -bool ishtp_cl_tx_empty(struct ishtp_cl *cl) -{ - int tx_list_empty; - unsigned long tx_flags; - - spin_lock_irqsave(&cl->tx_list_spinlock, tx_flags); - tx_list_empty = list_empty(&cl->tx_list.list); - spin_unlock_irqrestore(&cl->tx_list_spinlock, tx_flags); - - return !!tx_list_empty; -} -EXPORT_SYMBOL(ishtp_cl_tx_empty); - -/** * ishtp_cl_rx_get_rb() -Get a rb from client device rx buffer list * @cl: Pointer to client device instance * diff --git a/drivers/hid/intel-ish-hid/ishtp/client.c b/drivers/hid/intel-ish-hid/ishtp/client.c index 8a7f2f6a4f86..21a2c0773cc2 100644 --- a/drivers/hid/intel-ish-hid/ishtp/client.c +++ b/drivers/hid/intel-ish-hid/ishtp/client.c @@ -14,25 +14,6 @@ #include "hbm.h" #include "client.h" -int ishtp_cl_get_tx_free_buffer_size(struct ishtp_cl *cl) -{ - unsigned long tx_free_flags; - int size; - - spin_lock_irqsave(&cl->tx_free_list_spinlock, tx_free_flags); - size = cl->tx_ring_free_size * cl->device->fw_client->props.max_msg_length; - spin_unlock_irqrestore(&cl->tx_free_list_spinlock, tx_free_flags); - - return size; -} -EXPORT_SYMBOL(ishtp_cl_get_tx_free_buffer_size); - -int ishtp_cl_get_tx_free_rings(struct ishtp_cl *cl) -{ - return cl->tx_ring_free_size; -} -EXPORT_SYMBOL(ishtp_cl_get_tx_free_rings); - /** * ishtp_read_list_flush() - Flush read queue * @cl: ishtp client instance @@ -863,7 +844,7 @@ static void ipc_tx_send(void *prm) /* Send ipc fragment */ ishtp_hdr.length = dev->mtu; ishtp_hdr.msg_complete = 0; - /* All fregments submitted to IPC queue with no callback */ + /* All fragments submitted to IPC queue with no callback */ ishtp_write_message(dev, &ishtp_hdr, pmsg); cl->tx_offs += dev->mtu; rem = cl_msg->send_buf.size - cl->tx_offs; diff --git a/drivers/hid/intel-ish-hid/ishtp/client.h b/drivers/hid/intel-ish-hid/ishtp/client.h index fc62dd1495da..0efd49dd2530 100644 --- a/drivers/hid/intel-ish-hid/ishtp/client.h +++ b/drivers/hid/intel-ish-hid/ishtp/client.h @@ -109,7 +109,6 @@ struct ishtp_cl { }; /* Client connection managenment internal functions */ -int ishtp_can_client_connect(struct ishtp_device *ishtp_dev, guid_t *uuid); int ishtp_fw_cl_by_id(struct ishtp_device *dev, uint8_t client_id); void ishtp_cl_send_msg(struct ishtp_device *dev, struct ishtp_cl *cl); void recv_ishtp_cl_msg(struct ishtp_device *dev, @@ -121,8 +120,6 @@ int ishtp_cl_alloc_rx_ring(struct ishtp_cl *cl); int ishtp_cl_alloc_tx_ring(struct ishtp_cl *cl); void ishtp_cl_free_rx_ring(struct ishtp_cl *cl); void ishtp_cl_free_tx_ring(struct ishtp_cl *cl); -int ishtp_cl_get_tx_free_buffer_size(struct ishtp_cl *cl); -int ishtp_cl_get_tx_free_rings(struct ishtp_cl *cl); /* DMA I/F functions */ void recv_ishtp_cl_msg_dma(struct ishtp_device *dev, void *msg, diff --git a/drivers/hid/intel-ish-hid/ishtp/hbm.c b/drivers/hid/intel-ish-hid/ishtp/hbm.c index 9c031a06e4c4..8ee5467127d8 100644 --- a/drivers/hid/intel-ish-hid/ishtp/hbm.c +++ b/drivers/hid/intel-ish-hid/ishtp/hbm.c @@ -13,6 +13,7 @@ #include "ishtp-dev.h" #include "hbm.h" #include "client.h" +#include "loader.h" /** * ishtp_hbm_fw_cl_allocate() - Allocate FW clients @@ -570,6 +571,10 @@ void ishtp_hbm_dispatch(struct ishtp_device *dev, return; } + /* Start firmware loading process if it has loader capability */ + if (version_res->host_version_supported & ISHTP_SUPPORT_CAP_LOADER) + schedule_work(&dev->work_fw_loader); + dev->version.major_version = HBM_MAJOR_VERSION; dev->version.minor_version = HBM_MINOR_VERSION; if (dev->dev_state == ISHTP_DEV_INIT_CLIENTS && @@ -865,6 +870,20 @@ eoi: } /** + * ishtp_loader_recv_msg() - Receive a message from the ISHTP device + * @dev: The ISHTP device + * @buf: The buffer containing the message + */ +static void ishtp_loader_recv_msg(struct ishtp_device *dev, void *buf) +{ + if (dev->fw_loader_rx_buf) + memcpy(dev->fw_loader_rx_buf, buf, dev->fw_loader_rx_size); + + dev->fw_loader_received = true; + wake_up_interruptible(&dev->wait_loader_recvd_msg); +} + +/** * recv_fixed_cl_msg() - Receive fixed client message * @dev: ISHTP device instance * @ishtp_hdr: received bus message @@ -890,6 +909,8 @@ void recv_fixed_cl_msg(struct ishtp_device *dev, else dev_err(dev->devc, "unknown fixed client msg [%02X]\n", msg_hdr->cmd); + } else if (ishtp_hdr->fw_addr == ISHTP_LOADER_CLIENT_ADDR) { + ishtp_loader_recv_msg(dev, rd_msg_buf); } } diff --git a/drivers/hid/intel-ish-hid/ishtp/init.c b/drivers/hid/intel-ish-hid/ishtp/init.c index 02a00cc2dd11..26bf9045a8de 100644 --- a/drivers/hid/intel-ish-hid/ishtp/init.c +++ b/drivers/hid/intel-ish-hid/ishtp/init.c @@ -5,42 +5,14 @@ * Copyright (c) 2003-2016, Intel Corporation. */ +#include <linux/devm-helpers.h> #include <linux/export.h> #include <linux/slab.h> #include <linux/sched.h> #include "ishtp-dev.h" #include "hbm.h" #include "client.h" - -/** - * ishtp_dev_state_str() -Convert to string format - * @state: state to convert - * - * Convert state to string for prints - * - * Return: character pointer to converted string - */ -const char *ishtp_dev_state_str(int state) -{ - switch (state) { - case ISHTP_DEV_INITIALIZING: - return "INITIALIZING"; - case ISHTP_DEV_INIT_CLIENTS: - return "INIT_CLIENTS"; - case ISHTP_DEV_ENABLED: - return "ENABLED"; - case ISHTP_DEV_RESETTING: - return "RESETTING"; - case ISHTP_DEV_DISABLED: - return "DISABLED"; - case ISHTP_DEV_POWER_DOWN: - return "POWER_DOWN"; - case ISHTP_DEV_POWER_UP: - return "POWER_UP"; - default: - return "unknown"; - } -} +#include "loader.h" /** * ishtp_device_init() - ishtp device init @@ -51,6 +23,8 @@ const char *ishtp_dev_state_str(int state) */ void ishtp_device_init(struct ishtp_device *dev) { + int ret; + dev->dev_state = ISHTP_DEV_INITIALIZING; INIT_LIST_HEAD(&dev->cl_list); INIT_LIST_HEAD(&dev->device_list); @@ -59,6 +33,7 @@ void ishtp_device_init(struct ishtp_device *dev) spin_lock_init(&dev->rd_msg_spinlock); init_waitqueue_head(&dev->wait_hbm_recvd_msg); + init_waitqueue_head(&dev->wait_loader_recvd_msg); spin_lock_init(&dev->read_list_spinlock); spin_lock_init(&dev->device_lock); spin_lock_init(&dev->device_list_lock); @@ -76,6 +51,9 @@ void ishtp_device_init(struct ishtp_device *dev) INIT_LIST_HEAD(&dev->read_list.list); + ret = devm_work_autocancel(dev->devc, &dev->work_fw_loader, ishtp_loader_work); + if (ret) + dev_err_probe(dev->devc, ret, "Failed to initialise FW loader work\n"); } EXPORT_SYMBOL(ishtp_device_init); diff --git a/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h b/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h index 32142c7d9a04..ec9f6e87aaf2 100644 --- a/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h +++ b/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h @@ -57,7 +57,6 @@ enum ishtp_dev_state { ISHTP_DEV_POWER_DOWN, ISHTP_DEV_POWER_UP }; -const char *ishtp_dev_state_str(int state); struct ishtp_cl; @@ -123,11 +122,37 @@ struct ishtp_hw_ops { }; /** + * struct ishtp_driver_data - Driver-specific data for ISHTP devices + * + * This structure holds driver-specific data that can be associated with each + * ISHTP device instance. It allows for the storage of data that is unique to + * a particular driver or hardware variant. + * + * @fw_generation: The generation name associated with a specific hardware + * variant of the Intel Integrated Sensor Hub (ISH). This allows + * the driver to load the correct firmware based on the device's + * hardware variant. For example, "lnlm" for the Lunar Lake-M + * platform. The generation name must not exceed 8 characters + * in length. + */ +struct ishtp_driver_data { + char *fw_generation; +}; + +struct ish_version { + u16 major; + u16 minor; + u16 hotfix; + u16 build; +}; + +/** * struct ishtp_device - ISHTP private device struct */ struct ishtp_device { struct device *devc; /* pointer to lowest device */ struct pci_dev *pdev; /* PCI device to get device ids */ + struct ishtp_driver_data *driver_data; /* pointer to driver-specific data */ /* waitq for waiting for suspend response */ wait_queue_head_t suspend_wait; @@ -147,6 +172,17 @@ struct ishtp_device { struct hbm_version version; int transfer_path; /* Choice of transfer path: IPC or DMA */ + /* work structure for scheduling firmware loading tasks */ + struct work_struct work_fw_loader; + /* waitq for waiting for command response from the firmware loader */ + wait_queue_head_t wait_loader_recvd_msg; + /* indicating whether a message from the firmware loader has been received */ + bool fw_loader_received; + /* pointer to a buffer for receiving messages from the firmware loader */ + void *fw_loader_rx_buf; + /* size of the buffer pointed to by fw_loader_rx_buf */ + int fw_loader_rx_size; + /* ishtp device states */ enum ishtp_dev_state dev_state; enum ishtp_hbm_state hbm_state; @@ -206,12 +242,19 @@ struct ishtp_device { /* Dump to trace buffers if enabled*/ ishtp_print_log print_log; + /* Base version of Intel's released firmware */ + struct ish_version base_ver; + /* Vendor-customized project version */ + struct ish_version prj_ver; + /* Debug stats */ unsigned int ipc_rx_cnt; unsigned long long ipc_rx_bytes_cnt; unsigned int ipc_tx_cnt; unsigned long long ipc_tx_bytes_cnt; + /* Time of the last clock sync */ + unsigned long prev_sync; const struct ishtp_hw_ops *ops; size_t mtu; uint32_t ishtp_msg_hdr; diff --git a/drivers/hid/intel-ish-hid/ishtp/loader.c b/drivers/hid/intel-ish-hid/ishtp/loader.c new file mode 100644 index 000000000000..f34086b29cf0 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp/loader.c @@ -0,0 +1,432 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ISHTP firmware loader function + * + * Copyright (c) 2024, Intel Corporation. + * + * This module implements the functionality to load the main ISH firmware from the host, starting + * with the Lunar Lake generation. It leverages a new method that enhances space optimization and + * flexibility by dividing the ISH firmware into a bootloader and main firmware. + * + * Please refer to the [Documentation](Documentation/hid/intel-ish-hid.rst) for the details on + * flows. + * + * Additionally, address potential error scenarios to ensure graceful failure handling. + * - Firmware Image Not Found: + * Occurs when `request_firmware()` cannot locate the firmware image. The ISH firmware will + * remain in a state awaiting firmware loading from the host, with no further action from + * the ISHTP driver. + * Recovery: Re-insmod the ISH drivers allows for a retry of the firmware loading from the host. + * + * - DMA Buffer Allocation Failure: + * This happens if allocating a DMA buffer during `prepare_dma_bufs()` fails. The ISH firmware + * will stay in a waiting state, and the ISHTP driver will release any allocated DMA buffers and + * firmware without further actions. + * Recovery: Re-insmod the ISH drivers allows for a retry of the firmware loading from the host. + * + * - Incorrect Firmware Image: + * Using an incorrect firmware image will initiate the firmware loading process but will + * eventually be refused by the ISH firmware after three unsuccessful attempts, indicated by + * returning an error code. The ISHTP driver will stop attempting after three tries. + * Recovery: A platform reset is required to retry firmware loading from the host. + */ + +#define dev_fmt(fmt) "ISH loader: " fmt + +#include <linux/cacheflush.h> +#include <linux/container_of.h> +#include <linux/crc32.h> +#include <linux/dev_printk.h> +#include <linux/dma-mapping.h> +#include <linux/dmi.h> +#include <linux/errno.h> +#include <linux/firmware.h> +#include <linux/gfp_types.h> +#include <linux/math.h> +#include <linux/module.h> +#include <linux/pfn.h> +#include <linux/sprintf.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/wait.h> + +#include "hbm.h" +#include "loader.h" + +/** + * loader_write_message() - Write a message to the ISHTP device + * @dev: The ISHTP device + * @buf: The buffer containing the message + * @len: The length of the message + * + * Return: 0 on success, negative error code on failure + */ +static int loader_write_message(struct ishtp_device *dev, void *buf, int len) +{ + struct ishtp_msg_hdr ishtp_hdr = { + .fw_addr = ISHTP_LOADER_CLIENT_ADDR, + .length = len, + .msg_complete = 1, + }; + + dev->fw_loader_received = false; + + return ishtp_write_message(dev, &ishtp_hdr, buf); +} + +/** + * loader_xfer_cmd() - Transfer a command to the ISHTP device + * @dev: The ISHTP device + * @req: The request buffer + * @req_len: The length of the request + * @resp: The response buffer + * @resp_len: The length of the response + * + * Return: 0 on success, negative error code on failure + */ +static int loader_xfer_cmd(struct ishtp_device *dev, void *req, int req_len, + void *resp, int resp_len) +{ + union loader_msg_header req_hdr; + union loader_msg_header resp_hdr; + struct device *devc = dev->devc; + int rv; + + dev->fw_loader_rx_buf = resp; + dev->fw_loader_rx_size = resp_len; + + rv = loader_write_message(dev, req, req_len); + req_hdr.val32 = le32_to_cpup(req); + + if (rv < 0) { + dev_err(devc, "write cmd %u failed:%d\n", req_hdr.command, rv); + return rv; + } + + /* Wait the ACK */ + wait_event_interruptible_timeout(dev->wait_loader_recvd_msg, dev->fw_loader_received, + ISHTP_LOADER_TIMEOUT); + resp_hdr.val32 = le32_to_cpup(resp); + dev->fw_loader_rx_size = 0; + dev->fw_loader_rx_buf = NULL; + if (!dev->fw_loader_received) { + dev_err(devc, "wait response of cmd %u timeout\n", req_hdr.command); + return -ETIMEDOUT; + } + + if (!resp_hdr.is_response) { + dev_err(devc, "not a response for %u\n", req_hdr.command); + return -EBADMSG; + } + + if (req_hdr.command != resp_hdr.command) { + dev_err(devc, "unexpected cmd response %u:%u\n", req_hdr.command, + resp_hdr.command); + return -EBADMSG; + } + + if (resp_hdr.status) { + dev_err(devc, "cmd %u failed %u\n", req_hdr.command, resp_hdr.status); + return -EIO; + } + + return 0; +} + +/** + * release_dma_bufs() - Release the DMA buffer for transferring firmware fragments + * @dev: The ISHTP device + * @fragment: The ISHTP firmware fragment descriptor + * @dma_bufs: The array of DMA fragment buffers + * @fragment_size: The size of a single DMA fragment + */ +static void release_dma_bufs(struct ishtp_device *dev, + struct loader_xfer_dma_fragment *fragment, + void **dma_bufs, u32 fragment_size) +{ + dma_addr_t dma_addr; + int i; + + for (i = 0; i < FRAGMENT_MAX_NUM; i++) { + if (dma_bufs[i]) { + dma_addr = le64_to_cpu(fragment->fragment_tbl[i].ddr_adrs); + dma_free_coherent(dev->devc, fragment_size, dma_bufs[i], dma_addr); + dma_bufs[i] = NULL; + } + } +} + +/** + * prepare_dma_bufs() - Prepare the DMA buffer for transferring firmware fragments + * @dev: The ISHTP device + * @ish_fw: The ISH firmware + * @fragment: The ISHTP firmware fragment descriptor + * @dma_bufs: The array of DMA fragment buffers + * @fragment_size: The size of a single DMA fragment + * @fragment_count: Number of fragments + * + * Return: 0 on success, negative error code on failure + */ +static int prepare_dma_bufs(struct ishtp_device *dev, + const struct firmware *ish_fw, + struct loader_xfer_dma_fragment *fragment, + void **dma_bufs, u32 fragment_size, u32 fragment_count) +{ + dma_addr_t dma_addr; + u32 offset = 0; + u32 length; + int i; + + for (i = 0; i < fragment_count && offset < ish_fw->size; i++) { + dma_bufs[i] = dma_alloc_coherent(dev->devc, fragment_size, &dma_addr, GFP_KERNEL); + if (!dma_bufs[i]) + return -ENOMEM; + + fragment->fragment_tbl[i].ddr_adrs = cpu_to_le64(dma_addr); + length = clamp(ish_fw->size - offset, 0, fragment_size); + fragment->fragment_tbl[i].length = cpu_to_le32(length); + fragment->fragment_tbl[i].fw_off = cpu_to_le32(offset); + memcpy(dma_bufs[i], ish_fw->data + offset, length); + clflush_cache_range(dma_bufs[i], fragment_size); + + offset += length; + } + + return 0; +} + +#define ISH_FW_FILE_VENDOR_NAME_SKU_FMT "intel/ish/ish_%s_%08x_%08x_%08x.bin" +#define ISH_FW_FILE_VENDOR_SKU_FMT "intel/ish/ish_%s_%08x_%08x.bin" +#define ISH_FW_FILE_VENDOR_NAME_FMT "intel/ish/ish_%s_%08x_%08x.bin" +#define ISH_FW_FILE_VENDOR_FMT "intel/ish/ish_%s_%08x.bin" +#define ISH_FW_FILE_DEFAULT_FMT "intel/ish/ish_%s.bin" + +#define ISH_FW_FILENAME_LEN_MAX 56 + +#define ISH_CRC_INIT (~0u) +#define ISH_CRC_XOROUT (~0u) + +static int _request_ish_firmware(const struct firmware **firmware_p, + const char *name, struct device *dev) +{ + int ret; + + dev_dbg(dev, "Try to load firmware: %s\n", name); + ret = firmware_request_nowarn(firmware_p, name, dev); + if (!ret) + dev_info(dev, "load firmware: %s\n", name); + + return ret; +} + +/** + * request_ish_firmware() - Request and load the ISH firmware. + * @firmware_p: Pointer to the firmware image. + * @dev: Device for which firmware is being requested. + * + * This function attempts to load the Integrated Sensor Hub (ISH) firmware + * for the given device in the following order, prioritizing custom firmware + * with more precise matching patterns: + * + * ish_${fw_generation}_${SYS_VENDOR_CRC32}_$(PRODUCT_NAME_CRC32)_${PRODUCT_SKU_CRC32}.bin + * ish_${fw_generation}_${SYS_VENDOR_CRC32}_${PRODUCT_SKU_CRC32}.bin + * ish_${fw_generation}_${SYS_VENDOR_CRC32}_$(PRODUCT_NAME_CRC32).bin + * ish_${fw_generation}_${SYS_VENDOR_CRC32}.bin + * ish_${fw_generation}.bin + * + * The driver will load the first matching firmware and skip the rest. If no + * matching firmware is found, it will proceed to the next pattern in the + * specified order. If all searches fail, the default Intel firmware, listed + * last in the order above, will be loaded. + * + * The firmware file name is constructed using CRC32 checksums of strings. + * This is done to create a valid file name that does not contain spaces + * or special characters which may be present in the original strings. + * + * The CRC-32 algorithm uses the following parameters: + * Poly: 0x04C11DB7 + * Init: 0xFFFFFFFF + * RefIn: true + * RefOut: true + * XorOut: 0xFFFFFFFF + * + * Return: 0 on success, negative error code on failure. + */ +static int request_ish_firmware(const struct firmware **firmware_p, + struct device *dev) +{ + const char *gen, *sys_vendor, *product_name, *product_sku; + struct ishtp_device *ishtp = dev_get_drvdata(dev); + u32 vendor_crc, name_crc, sku_crc; + char filename[ISH_FW_FILENAME_LEN_MAX]; + int ret; + + gen = ishtp->driver_data->fw_generation; + sys_vendor = dmi_get_system_info(DMI_SYS_VENDOR); + product_name = dmi_get_system_info(DMI_PRODUCT_NAME); + product_sku = dmi_get_system_info(DMI_PRODUCT_SKU); + + if (sys_vendor) + vendor_crc = crc32(ISH_CRC_INIT, sys_vendor, strlen(sys_vendor)) ^ ISH_CRC_XOROUT; + if (product_name) + name_crc = crc32(ISH_CRC_INIT, product_name, strlen(product_name)) ^ ISH_CRC_XOROUT; + if (product_sku) + sku_crc = crc32(ISH_CRC_INIT, product_sku, strlen(product_sku)) ^ ISH_CRC_XOROUT; + + if (sys_vendor && product_name && product_sku) { + snprintf(filename, sizeof(filename), ISH_FW_FILE_VENDOR_NAME_SKU_FMT, gen, + vendor_crc, name_crc, sku_crc); + ret = _request_ish_firmware(firmware_p, filename, dev); + if (!ret) + return 0; + } + + if (sys_vendor && product_sku) { + snprintf(filename, sizeof(filename), ISH_FW_FILE_VENDOR_SKU_FMT, gen, vendor_crc, + sku_crc); + ret = _request_ish_firmware(firmware_p, filename, dev); + if (!ret) + return 0; + } + + if (sys_vendor && product_name) { + snprintf(filename, sizeof(filename), ISH_FW_FILE_VENDOR_NAME_FMT, gen, vendor_crc, + name_crc); + ret = _request_ish_firmware(firmware_p, filename, dev); + if (!ret) + return 0; + } + + if (sys_vendor) { + snprintf(filename, sizeof(filename), ISH_FW_FILE_VENDOR_FMT, gen, vendor_crc); + ret = _request_ish_firmware(firmware_p, filename, dev); + if (!ret) + return 0; + } + + snprintf(filename, sizeof(filename), ISH_FW_FILE_DEFAULT_FMT, gen); + return _request_ish_firmware(firmware_p, filename, dev); +} + +static int copy_manifest(const struct firmware *fw, struct ish_global_manifest *manifest) +{ + u32 offset; + + for (offset = 0; offset + sizeof(*manifest) < fw->size; offset += ISH_MANIFEST_ALIGNMENT) { + memcpy(manifest, fw->data + offset, sizeof(*manifest)); + + if (le32_to_cpu(manifest->sig_fourcc) == ISH_GLOBAL_SIG) + return 0; + } + + return -1; +} + +static void copy_ish_version(struct version_in_manifest *src, struct ish_version *dst) +{ + dst->major = le16_to_cpu(src->major); + dst->minor = le16_to_cpu(src->minor); + dst->hotfix = le16_to_cpu(src->hotfix); + dst->build = le16_to_cpu(src->build); +} + +/** + * ishtp_loader_work() - Load the ISHTP firmware + * @work: The work structure + * + * The ISH Loader attempts to load firmware by sending a series of commands + * to the ISH device. If a command fails to be acknowledged by the ISH device, + * the loader will retry sending the command, up to a maximum of + * ISHTP_LOADER_RETRY_TIMES. + * + * After the maximum number of retries has been reached without success, the + * ISH bootloader will return an error status code and will no longer respond + * to the driver's commands. This behavior indicates that the ISH Loader has + * encountered a critical error during the firmware loading process. + * + * In such a case, where the ISH bootloader is unresponsive after all retries + * have been exhausted, a platform reset is required to restore communication + * with the ISH device and to recover from this error state. + */ +void ishtp_loader_work(struct work_struct *work) +{ + DEFINE_RAW_FLEX(struct loader_xfer_dma_fragment, fragment, fragment_tbl, FRAGMENT_MAX_NUM); + struct ishtp_device *dev = container_of(work, struct ishtp_device, work_fw_loader); + union loader_msg_header query_hdr = { .command = LOADER_CMD_XFER_QUERY, }; + union loader_msg_header start_hdr = { .command = LOADER_CMD_START, }; + union loader_msg_header fragment_hdr = { .command = LOADER_CMD_XFER_FRAGMENT, }; + struct loader_xfer_query query = { .header = cpu_to_le32(query_hdr.val32), }; + struct loader_start start = { .header = cpu_to_le32(start_hdr.val32), }; + union loader_recv_message recv_msg; + struct ish_global_manifest manifest; + const struct firmware *ish_fw; + void *dma_bufs[FRAGMENT_MAX_NUM] = {}; + u32 fragment_size; + u32 fragment_count; + int retry = ISHTP_LOADER_RETRY_TIMES; + int rv; + + rv = request_ish_firmware(&ish_fw, dev->devc); + if (rv < 0) { + dev_err(dev->devc, "request ISH firmware failed:%d\n", rv); + return; + } + + fragment->fragment.header = cpu_to_le32(fragment_hdr.val32); + fragment->fragment.xfer_mode = cpu_to_le32(LOADER_XFER_MODE_DMA); + fragment->fragment.is_last = cpu_to_le32(1); + fragment->fragment.size = cpu_to_le32(ish_fw->size); + /* Calculate the size of a single DMA fragment */ + fragment_size = PFN_ALIGN(DIV_ROUND_UP(ish_fw->size, FRAGMENT_MAX_NUM)); + /* Calculate the count of DMA fragments */ + fragment_count = DIV_ROUND_UP(ish_fw->size, fragment_size); + fragment->fragment_cnt = cpu_to_le32(fragment_count); + + rv = prepare_dma_bufs(dev, ish_fw, fragment, dma_bufs, fragment_size, fragment_count); + if (rv) { + dev_err(dev->devc, "prepare DMA buffer failed.\n"); + goto out; + } + + do { + query.image_size = cpu_to_le32(ish_fw->size); + rv = loader_xfer_cmd(dev, &query, sizeof(query), recv_msg.raw_data, + sizeof(struct loader_xfer_query_ack)); + if (rv) + continue; /* try again if failed */ + + dev_dbg(dev->devc, "ISH Bootloader Version %u.%u.%u.%u\n", + recv_msg.query_ack.version_major, + recv_msg.query_ack.version_minor, + recv_msg.query_ack.version_hotfix, + recv_msg.query_ack.version_build); + + rv = loader_xfer_cmd(dev, fragment, + struct_size(fragment, fragment_tbl, fragment_count), + recv_msg.raw_data, sizeof(struct loader_xfer_fragment_ack)); + if (rv) + continue; /* try again if failed */ + + rv = loader_xfer_cmd(dev, &start, sizeof(start), recv_msg.raw_data, + sizeof(struct loader_start_ack)); + if (rv) + continue; /* try again if failed */ + + dev_info(dev->devc, "firmware loaded. size:%zu\n", ish_fw->size); + if (!copy_manifest(ish_fw, &manifest)) { + copy_ish_version(&manifest.base_ver, &dev->base_ver); + copy_ish_version(&manifest.prj_ver, &dev->prj_ver); + dev_info(dev->devc, "FW base version: %u.%u.%u.%u\n", + dev->base_ver.major, dev->base_ver.minor, + dev->base_ver.hotfix, dev->base_ver.build); + dev_info(dev->devc, "FW project version: %u.%u.%u.%u\n", + dev->prj_ver.major, dev->prj_ver.minor, + dev->prj_ver.hotfix, dev->prj_ver.build); + } + break; + } while (--retry); + +out: + release_dma_bufs(dev, fragment, dma_bufs, fragment_size); + release_firmware(ish_fw); +} diff --git a/drivers/hid/intel-ish-hid/ishtp/loader.h b/drivers/hid/intel-ish-hid/ishtp/loader.h new file mode 100644 index 000000000000..4dda038b4947 --- /dev/null +++ b/drivers/hid/intel-ish-hid/ishtp/loader.h @@ -0,0 +1,265 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ISHTP firmware loader header + * + * Copyright (c) 2024, Intel Corporation. + */ + +#ifndef _ISHTP_LOADER_H_ +#define _ISHTP_LOADER_H_ + +#include <linux/bits.h> +#include <linux/jiffies.h> +#include <linux/sizes.h> +#include <linux/types.h> + +#include "ishtp-dev.h" + +struct work_struct; + +#define LOADER_MSG_SIZE \ + (IPC_PAYLOAD_SIZE - sizeof(struct ishtp_msg_hdr)) + +/* + * ISHTP firmware loader protocol definition + */ +#define LOADER_CMD_XFER_QUERY 0 /* SW -> FW */ +#define LOADER_CMD_XFER_FRAGMENT 1 /* SW -> FW */ +#define LOADER_CMD_START 2 /* SW -> FW */ + +/* Only support DMA mode */ +#define LOADER_XFER_MODE_DMA BIT(0) + +/** + * union loader_msg_header - ISHTP firmware loader message header + * @command: Command type + * @is_response: Indicates if the message is a response + * @has_next: Indicates if there is a next message + * @reserved: Reserved for future use + * @status: Status of the message + * @val32: entire header as a 32-bit value + */ +union loader_msg_header { + struct { + __u32 command:7; + __u32 is_response:1; + __u32 has_next:1; + __u32 reserved:15; + __u32 status:8; + }; + __u32 val32; +}; + +/** + * struct loader_xfer_query - ISHTP firmware loader transfer query packet + * @header: Header of the message + * @image_size: Size of the image + */ +struct loader_xfer_query { + __le32 header; + __le32 image_size; +}; + +/** + * struct loader_version - ISHTP firmware loader version + * @value: Value of the version + * @major: Major version + * @minor: Minor version + * @hotfix: Hotfix version + * @build: Build version + */ +struct loader_version { + union { + __le32 value; + struct { + __u8 major; + __u8 minor; + __u8 hotfix; + __u8 build; + }; + }; +}; + +/** + * struct loader_capability - ISHTP firmware loader capability + * @max_fw_image_size: Maximum firmware image size + * @support_mode: Support mode + * @reserved: Reserved for future use + * @platform: Platform + * @max_dma_buf_size: Maximum DMA buffer size, multiples of 4096 + */ +struct loader_capability { + __le32 max_fw_image_size; + __le16 support_mode; + __u8 reserved; + __u8 platform; + __le32 max_dma_buf_size; +}; + +/** + * struct loader_xfer_query_ack - ISHTP firmware loader transfer query acknowledgment + * @header: Header of the message + * @version_major: ISH Major version + * @version_minor: ISH Minor version + * @version_hotfix: ISH Hotfix version + * @version_build: ISH Build version + * @protocol_version: Protocol version + * @loader_version: Loader version + * @capability: Loader capability + */ +struct loader_xfer_query_ack { + __le32 header; + __le16 version_major; + __le16 version_minor; + __le16 version_hotfix; + __le16 version_build; + __le32 protocol_version; + struct loader_version loader_version; + struct loader_capability capability; +}; + +/** + * struct loader_xfer_fragment - ISHTP firmware loader transfer fragment + * @header: Header of the message + * @xfer_mode: Transfer mode + * @offset: Offset + * @size: Size + * @is_last: Is last + */ +struct loader_xfer_fragment { + __le32 header; + __le32 xfer_mode; + __le32 offset; + __le32 size; + __le32 is_last; +}; + +/** + * struct loader_xfer_fragment_ack - ISHTP firmware loader transfer fragment acknowledgment + * @header: Header of the message + */ +struct loader_xfer_fragment_ack { + __le32 header; +}; + +/** + * struct fragment_dscrpt - ISHTP firmware loader fragment descriptor + * @ddr_adrs: The address in host DDR + * @fw_off: The offset of the fragment in the fw image + * @length: The length of the fragment + */ +struct fragment_dscrpt { + __le64 ddr_adrs; + __le32 fw_off; + __le32 length; +}; + +#define FRAGMENT_MAX_NUM \ + ((LOADER_MSG_SIZE - sizeof(struct loader_xfer_dma_fragment)) / \ + sizeof(struct fragment_dscrpt)) + +/** + * struct loader_xfer_dma_fragment - ISHTP firmware loader transfer DMA fragment + * @fragment: Fragment + * @fragment_cnt: How many descriptors in the fragment_tbl + * @fragment_tbl: Fragment table + */ +struct loader_xfer_dma_fragment { + struct loader_xfer_fragment fragment; + __le32 fragment_cnt; + struct fragment_dscrpt fragment_tbl[] __counted_by(fragment_cnt); +}; + +/** + * struct loader_start - ISHTP firmware loader start + * @header: Header of the message + */ +struct loader_start { + __le32 header; +}; + +/** + * struct loader_start_ack - ISHTP firmware loader start acknowledgment + * @header: Header of the message + */ +struct loader_start_ack { + __le32 header; +}; + +union loader_recv_message { + __le32 header; + struct loader_xfer_query_ack query_ack; + struct loader_xfer_fragment_ack fragment_ack; + struct loader_start_ack start_ack; + __u8 raw_data[LOADER_MSG_SIZE]; +}; + +/* + * ISHTP firmware loader internal use + */ +/* ISHTP firmware loader command timeout */ +#define ISHTP_LOADER_TIMEOUT msecs_to_jiffies(100) + +/* ISHTP firmware loader retry times */ +#define ISHTP_LOADER_RETRY_TIMES 3 + +/** + * struct ish_firmware_variant - ISH firmware variant + * @device: PCI Device ID + * @filename: The firmware file name + */ +struct ish_firmware_variant { + unsigned short device; + const char *filename; +}; + +/* + * ISHTP firmware loader API for ISHTP hbm + */ + +/* ISHTP capability bit for firmware loader */ +#define ISHTP_SUPPORT_CAP_LOADER BIT(4) + +/* Firmware loader address */ +#define ISHTP_LOADER_CLIENT_ADDR 16 + +/** + * ishtp_loader_work - The work function to start the firmware loading process + * @work: The work structure + */ +void ishtp_loader_work(struct work_struct *work); + +/* ISH Manifest alignment in binary is 4KB aligned */ +#define ISH_MANIFEST_ALIGNMENT SZ_4K + +/* Signature for ISH global manifest */ +#define ISH_GLOBAL_SIG 0x47485349 /* FourCC 'I', 'S', 'H', 'G' */ + +struct version_in_manifest { + __le16 major; + __le16 minor; + __le16 hotfix; + __le16 build; +}; + +/** + * struct ish_global_manifest - global manifest for ISH + * @sig_fourcc: Signature FourCC, should be 'I', 'S', 'H', 'G'. + * @len: Length of the manifest. + * @header_version: Version of the manifest header. + * @flags: Flags for additional information. + * @base_ver: Base version of Intel's released firmware. + * @reserved: Reserved space for future use. + * @prj_ver: Vendor-customized project version. + */ +struct ish_global_manifest { + __le32 sig_fourcc; + __le32 len; + __le32 header_version; + __le32 flags; + struct version_in_manifest base_ver; + __le32 reserved[13]; + struct version_in_manifest prj_ver; +}; + +#endif /* _ISHTP_LOADER_H_ */ diff --git a/drivers/hid/intel-thc-hid/Kconfig b/drivers/hid/intel-thc-hid/Kconfig new file mode 100644 index 000000000000..0351d1137607 --- /dev/null +++ b/drivers/hid/intel-thc-hid/Kconfig @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2024, Intel Corporation. + +menu "Intel THC HID Support" + depends on X86_64 && PCI + +config INTEL_THC_HID + tristate "Intel Touch Host Controller" + depends on ACPI + help + THC (Touch Host Controller) is the name of the IP block in PCH that + interfaces with Touch Devices (ex: touchscreen, touchpad etc.). It + is comprised of 3 key functional blocks: A natively half-duplex + Quad I/O capable SPI master; a low latency I2C interface to support + HIDI2C compliant devices; a hardware sequencer with Read/Write DMA + capability to system memory. + + Say Y/M here if you want to support Intel THC. If unsure, say N. + +config INTEL_QUICKSPI + tristate "Intel QuickSPI driver based on Intel Touch Host Controller" + depends on INTEL_THC_HID + help + Intel QuickSPI, based on Touch Host Controller (THC), implements + HIDSPI (HID over SPI) protocol. It configures THC to work at SPI + mode, and controls THC hardware sequencer to accelerate HIDSPI + transaction flow. + + Say Y/M here if you want to support Intel QuickSPI. If unsure, say N. + +config INTEL_QUICKI2C + tristate "Intel QuickI2C driver based on Intel Touch Host Controller" + depends on INTEL_THC_HID + help + Intel QuickI2C, uses Touch Host Controller (THC) hardware, implements + HIDI2C (HID over I2C) protocol. It configures THC to work in I2C + mode, and controls THC hardware sequencer to accelerate HIDI2C + transaction flow. + + Say Y/M here if you want to support Intel QuickI2C. If unsure, say N. + +endmenu diff --git a/drivers/hid/intel-thc-hid/Makefile b/drivers/hid/intel-thc-hid/Makefile new file mode 100644 index 000000000000..6f762d87af07 --- /dev/null +++ b/drivers/hid/intel-thc-hid/Makefile @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile - Intel Touch Host Controller (THC) drivers +# Copyright (c) 2024, Intel Corporation. +# +# + +obj-$(CONFIG_INTEL_THC_HID) += intel-thc.o +intel-thc-objs += intel-thc/intel-thc-dev.o +intel-thc-objs += intel-thc/intel-thc-dma.o + +obj-$(CONFIG_INTEL_QUICKSPI) += intel-quickspi.o +intel-quickspi-objs += intel-quickspi/pci-quickspi.o +intel-quickspi-objs += intel-quickspi/quickspi-hid.o +intel-quickspi-objs += intel-quickspi/quickspi-protocol.o + +obj-$(CONFIG_INTEL_QUICKI2C) += intel-quicki2c.o +intel-quicki2c-objs += intel-quicki2c/pci-quicki2c.o +intel-quicki2c-objs += intel-quicki2c/quicki2c-hid.o +intel-quicki2c-objs += intel-quicki2c/quicki2c-protocol.o + +ccflags-y += -I $(src)/intel-thc diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c new file mode 100644 index 000000000000..2de93f4a25ca --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c @@ -0,0 +1,969 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#include <linux/acpi.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/irqreturn.h> +#include <linux/pci.h> +#include <linux/sizes.h> +#include <linux/pm_runtime.h> + +#include "intel-thc-dev.h" +#include "intel-thc-hw.h" + +#include "quicki2c-dev.h" +#include "quicki2c-hid.h" +#include "quicki2c-protocol.h" + +/* THC QuickI2C ACPI method to get device properties */ +/* HIDI2C device method */ +static guid_t i2c_hid_guid = + GUID_INIT(0x3cdff6f7, 0x4267, 0x4555, 0xad, 0x05, 0xb3, 0x0a, 0x3d, 0x89, 0x38, 0xde); + +/* platform method */ +static guid_t thc_platform_guid = + GUID_INIT(0x84005682, 0x5b71, 0x41a4, 0x8d, 0x66, 0x81, 0x30, 0xf7, 0x87, 0xa1, 0x38); + +/** + * quicki2c_acpi_get_dsm_property - Query device ACPI DSM parameter + * + * @adev: point to ACPI device + * @guid: ACPI method's guid + * @rev: ACPI method's revision + * @func: ACPI method's function number + * @type: ACPI parameter's data type + * @prop_buf: point to return buffer + * + * This is a helper function for device to query its ACPI DSM parameters. + * + * Return: 0 if success or ENODEV on failed. + */ +static int quicki2c_acpi_get_dsm_property(struct acpi_device *adev, const guid_t *guid, + u64 rev, u64 func, acpi_object_type type, void *prop_buf) +{ + acpi_handle handle = acpi_device_handle(adev); + union acpi_object *obj; + + obj = acpi_evaluate_dsm_typed(handle, guid, rev, func, NULL, type); + if (!obj) { + acpi_handle_err(handle, + "Error _DSM call failed, rev: %d, func: %d, type: %d\n", + (int)rev, (int)func, (int)type); + return -ENODEV; + } + + if (type == ACPI_TYPE_INTEGER) + *(u32 *)prop_buf = (u32)obj->integer.value; + else if (type == ACPI_TYPE_BUFFER) + memcpy(prop_buf, obj->buffer.pointer, obj->buffer.length); + + ACPI_FREE(obj); + + return 0; +} + +/** + * quicki2c_acpi_get_dsd_property - Query device ACPI DSD parameter + * + * @adev: point to ACPI device + * @dsd_method_name: ACPI method's property name + * @type: ACPI parameter's data type + * @prop_buf: point to return buffer + * + * This is a helper function for device to query its ACPI DSD parameters. + * + * Return: 0 if success or ENODEV on failed. + */ +static int quicki2c_acpi_get_dsd_property(struct acpi_device *adev, acpi_string dsd_method_name, + acpi_object_type type, void *prop_buf) +{ + acpi_handle handle = acpi_device_handle(adev); + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object obj = { .type = type }; + struct acpi_object_list arg_list = { + .count = 1, + .pointer = &obj, + }; + union acpi_object *ret_obj; + acpi_status status; + + status = acpi_evaluate_object(handle, dsd_method_name, &arg_list, &buffer); + if (ACPI_FAILURE(status)) { + acpi_handle_err(handle, + "Can't evaluate %s method: %d\n", dsd_method_name, status); + return -ENODEV; + } + + ret_obj = buffer.pointer; + + memcpy(prop_buf, ret_obj->buffer.pointer, ret_obj->buffer.length); + + return 0; +} + +/** + * quicki2c_get_acpi_resources - Query all quicki2c devices' ACPI parameters + * + * @qcdev: point to quicki2c device + * + * This function gets all quicki2c devices' ACPI resource. + * + * Return: 0 if success or error code on failed. + */ +static int quicki2c_get_acpi_resources(struct quicki2c_device *qcdev) +{ + struct acpi_device *adev = ACPI_COMPANION(qcdev->dev); + struct quicki2c_subip_acpi_parameter i2c_param; + struct quicki2c_subip_acpi_config i2c_config; + u32 hid_desc_addr; + int ret = -EINVAL; + + if (!adev) { + dev_err(qcdev->dev, "Invalid acpi device pointer\n"); + return ret; + } + + qcdev->acpi_dev = adev; + + ret = quicki2c_acpi_get_dsm_property(adev, &i2c_hid_guid, + QUICKI2C_ACPI_REVISION_NUM, + QUICKI2C_ACPI_FUNC_NUM_HID_DESC_ADDR, + ACPI_TYPE_INTEGER, + &hid_desc_addr); + if (ret) + return ret; + + qcdev->hid_desc_addr = (u16)hid_desc_addr; + + ret = quicki2c_acpi_get_dsm_property(adev, &thc_platform_guid, + QUICKI2C_ACPI_REVISION_NUM, + QUICKI2C_ACPI_FUNC_NUM_ACTIVE_LTR_VAL, + ACPI_TYPE_INTEGER, + &qcdev->active_ltr_val); + if (ret) + return ret; + + ret = quicki2c_acpi_get_dsm_property(adev, &thc_platform_guid, + QUICKI2C_ACPI_REVISION_NUM, + QUICKI2C_ACPI_FUNC_NUM_LP_LTR_VAL, + ACPI_TYPE_INTEGER, + &qcdev->low_power_ltr_val); + if (ret) + return ret; + + ret = quicki2c_acpi_get_dsd_property(adev, QUICKI2C_ACPI_METHOD_NAME_ICRS, + ACPI_TYPE_BUFFER, &i2c_param); + if (ret) + return ret; + + if (i2c_param.addressing_mode != HIDI2C_ADDRESSING_MODE_7BIT) + return -EOPNOTSUPP; + + qcdev->i2c_slave_addr = i2c_param.device_address; + + ret = quicki2c_acpi_get_dsd_property(adev, QUICKI2C_ACPI_METHOD_NAME_ISUB, + ACPI_TYPE_BUFFER, &i2c_config); + if (ret) + return ret; + + if (i2c_param.connection_speed > 0 && + i2c_param.connection_speed <= QUICKI2C_SUBIP_STANDARD_MODE_MAX_SPEED) { + qcdev->i2c_speed_mode = THC_I2C_STANDARD; + qcdev->i2c_clock_hcnt = i2c_config.SMHX; + qcdev->i2c_clock_lcnt = i2c_config.SMLX; + } else if (i2c_param.connection_speed > QUICKI2C_SUBIP_STANDARD_MODE_MAX_SPEED && + i2c_param.connection_speed <= QUICKI2C_SUBIP_FAST_MODE_MAX_SPEED) { + qcdev->i2c_speed_mode = THC_I2C_FAST_AND_PLUS; + qcdev->i2c_clock_hcnt = i2c_config.FMHX; + qcdev->i2c_clock_lcnt = i2c_config.FMLX; + } else if (i2c_param.connection_speed > QUICKI2C_SUBIP_FAST_MODE_MAX_SPEED && + i2c_param.connection_speed <= QUICKI2C_SUBIP_FASTPLUS_MODE_MAX_SPEED) { + qcdev->i2c_speed_mode = THC_I2C_FAST_AND_PLUS; + qcdev->i2c_clock_hcnt = i2c_config.FPHX; + qcdev->i2c_clock_lcnt = i2c_config.FPLX; + } else if (i2c_param.connection_speed > QUICKI2C_SUBIP_FASTPLUS_MODE_MAX_SPEED && + i2c_param.connection_speed <= QUICKI2C_SUBIP_HIGH_SPEED_MODE_MAX_SPEED) { + qcdev->i2c_speed_mode = THC_I2C_HIGH_SPEED; + qcdev->i2c_clock_hcnt = i2c_config.HMHX; + qcdev->i2c_clock_lcnt = i2c_config.HMLX; + } else { + return -EOPNOTSUPP; + } + + return 0; +} + +/** + * quicki2c_irq_quick_handler - The ISR of the quicki2c driver + * + * @irq: The irq number + * @dev_id: pointer to the device structure + * + * Return: IRQ_WAKE_THREAD if further process needed. + */ +static irqreturn_t quicki2c_irq_quick_handler(int irq, void *dev_id) +{ + struct quicki2c_device *qcdev = dev_id; + + if (qcdev->state == QUICKI2C_DISABLED) + return IRQ_HANDLED; + + /* Disable THC interrupt before current interrupt be handled */ + thc_interrupt_enable(qcdev->thc_hw, false); + + return IRQ_WAKE_THREAD; +} + +/** + * try_recover - Try to recovery THC and Device + * @qcdev: pointer to quicki2c device + * + * This function is a error handler, called when fatal error happens. + * It try to reset Touch Device and re-configure THC to recovery + * transferring between Device and THC. + * + * Return: 0 if successful or error code on failed + */ +static int try_recover(struct quicki2c_device *qcdev) +{ + int ret; + + thc_dma_unconfigure(qcdev->thc_hw); + + ret = thc_dma_configure(qcdev->thc_hw); + if (ret) { + dev_err(qcdev->dev, "Reconfig DMA failed\n"); + return ret; + } + + return 0; +} + +static int handle_input_report(struct quicki2c_device *qcdev) +{ + struct hidi2c_report_packet *pkt = (struct hidi2c_report_packet *)qcdev->input_buf; + int rx_dma_finished = 0; + size_t report_len; + int ret; + + while (!rx_dma_finished) { + ret = thc_rxdma_read(qcdev->thc_hw, THC_RXDMA2, + (u8 *)pkt, &report_len, + &rx_dma_finished); + if (ret) + return ret; + + if (!pkt->len) { + if (qcdev->state == QUICKI2C_RESETING) { + qcdev->reset_ack = true; + wake_up(&qcdev->reset_ack_wq); + + qcdev->state = QUICKI2C_RESETED; + } else { + dev_warn(qcdev->dev, "unexpected DIR happen\n"); + } + + continue; + } + + /* discard samples before driver probe complete */ + if (qcdev->state != QUICKI2C_ENABLED) + continue; + + quicki2c_hid_send_report(qcdev, pkt->data, + HIDI2C_DATA_LEN(le16_to_cpu(pkt->len))); + } + + return 0; +} + +/** + * quicki2c_irq_thread_handler - IRQ thread handler of quicki2c driver + * + * @irq: The IRQ number + * @dev_id: pointer to the quicki2c device structure + * + * Return: IRQ_HANDLED to finish this handler. + */ +static irqreturn_t quicki2c_irq_thread_handler(int irq, void *dev_id) +{ + struct quicki2c_device *qcdev = dev_id; + int err_recover = 0; + int int_mask; + int ret; + + if (qcdev->state == QUICKI2C_DISABLED) + return IRQ_HANDLED; + + ret = pm_runtime_resume_and_get(qcdev->dev); + if (ret) + return IRQ_HANDLED; + + int_mask = thc_interrupt_handler(qcdev->thc_hw); + + if (int_mask & BIT(THC_FATAL_ERR_INT) || int_mask & BIT(THC_TXN_ERR_INT) || + int_mask & BIT(THC_UNKNOWN_INT)) { + err_recover = 1; + goto exit; + } + + if (int_mask & BIT(THC_RXDMA2_INT)) { + err_recover = handle_input_report(qcdev); + if (err_recover) + goto exit; + } + +exit: + thc_interrupt_enable(qcdev->thc_hw, true); + + if (err_recover) + if (try_recover(qcdev)) + qcdev->state = QUICKI2C_DISABLED; + + pm_runtime_mark_last_busy(qcdev->dev); + pm_runtime_put_autosuspend(qcdev->dev); + + return IRQ_HANDLED; +} + +/** + * quicki2c_dev_init - Initialize quicki2c device + * + * @pdev: pointer to the thc pci device + * @mem_addr: The pointer of MMIO memory address + * + * Alloc quicki2c device structure and initialized THC device, + * then configure THC to HIDI2C mode. + * + * If success, enable THC hardware interrupt. + * + * Return: pointer to the quicki2c device structure if success + * or NULL on failed. + */ +static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __iomem *mem_addr) +{ + struct device *dev = &pdev->dev; + struct quicki2c_device *qcdev; + int ret; + + qcdev = devm_kzalloc(dev, sizeof(struct quicki2c_device), GFP_KERNEL); + if (!qcdev) + return ERR_PTR(-ENOMEM); + + qcdev->pdev = pdev; + qcdev->dev = dev; + qcdev->mem_addr = mem_addr; + qcdev->state = QUICKI2C_DISABLED; + + init_waitqueue_head(&qcdev->reset_ack_wq); + + /* thc hw init */ + qcdev->thc_hw = thc_dev_init(qcdev->dev, qcdev->mem_addr); + if (IS_ERR(qcdev->thc_hw)) { + ret = PTR_ERR(qcdev->thc_hw); + dev_err_once(dev, "Failed to initialize THC device context, ret = %d.\n", ret); + return ERR_PTR(ret); + } + + ret = quicki2c_get_acpi_resources(qcdev); + if (ret) { + dev_err_once(dev, "Get ACPI resources failed, ret = %d\n", ret); + return ERR_PTR(ret); + } + + ret = thc_interrupt_quiesce(qcdev->thc_hw, true); + if (ret) + return ERR_PTR(ret); + + ret = thc_port_select(qcdev->thc_hw, THC_PORT_TYPE_I2C); + if (ret) { + dev_err_once(dev, "Failed to select THC port, ret = %d.\n", ret); + return ERR_PTR(ret); + } + + ret = thc_i2c_subip_init(qcdev->thc_hw, qcdev->i2c_slave_addr, + qcdev->i2c_speed_mode, + qcdev->i2c_clock_hcnt, + qcdev->i2c_clock_lcnt); + if (ret) + return ERR_PTR(ret); + + thc_int_trigger_type_select(qcdev->thc_hw, false); + + thc_interrupt_config(qcdev->thc_hw); + + thc_interrupt_enable(qcdev->thc_hw, true); + + qcdev->state = QUICKI2C_INITED; + + return qcdev; +} + +/** + * quicki2c_dev_deinit - De-initialize quicki2c device + * + * @qcdev: pointer to the quicki2c device structure + * + * Disable THC interrupt and deinitilize THC. + */ +static void quicki2c_dev_deinit(struct quicki2c_device *qcdev) +{ + thc_interrupt_enable(qcdev->thc_hw, false); + thc_ltr_unconfig(qcdev->thc_hw); + + qcdev->state = QUICKI2C_DISABLED; +} + +/** + * quicki2c_dma_init - Configure THC DMA for quicki2c device + * @qcdev: pointer to the quicki2c device structure + * + * This function uses TIC's parameters(such as max input length, max output + * length) to allocate THC DMA buffers and configure THC DMA engines. + * + * Return: 0 if success or error code on failed. + */ +static int quicki2c_dma_init(struct quicki2c_device *qcdev) +{ + size_t swdma_max_len; + int ret; + + swdma_max_len = max(le16_to_cpu(qcdev->dev_desc.max_input_len), + le16_to_cpu(qcdev->dev_desc.report_desc_len)); + + ret = thc_dma_set_max_packet_sizes(qcdev->thc_hw, 0, + le16_to_cpu(qcdev->dev_desc.max_input_len), + le16_to_cpu(qcdev->dev_desc.max_output_len), + swdma_max_len); + if (ret) + return ret; + + ret = thc_dma_allocate(qcdev->thc_hw); + if (ret) { + dev_err(qcdev->dev, "Allocate THC DMA buffer failed, ret = %d\n", ret); + return ret; + } + + /* Enable RxDMA */ + ret = thc_dma_configure(qcdev->thc_hw); + if (ret) { + dev_err(qcdev->dev, "Configure THC DMA failed, ret = %d\n", ret); + thc_dma_unconfigure(qcdev->thc_hw); + thc_dma_release(qcdev->thc_hw); + return ret; + } + + return ret; +} + +/** + * quicki2c_dma_deinit - Release THC DMA for quicki2c device + * @qcdev: pointer to the quicki2c device structure + * + * Stop THC DMA engines and release all DMA buffers. + * + */ +static void quicki2c_dma_deinit(struct quicki2c_device *qcdev) +{ + thc_dma_unconfigure(qcdev->thc_hw); + thc_dma_release(qcdev->thc_hw); +} + +/** + * quicki2c_alloc_report_buf - Alloc report buffers + * @qcdev: pointer to the quicki2c device structure + * + * Allocate report descriptor buffer, it will be used for restore TIC HID + * report descriptor. + * + * Allocate input report buffer, it will be used for receive HID input report + * data from TIC. + * + * Allocate output report buffer, it will be used for store HID output report, + * such as set feature. + * + * Return: 0 if success or error code on failed. + */ +static int quicki2c_alloc_report_buf(struct quicki2c_device *qcdev) +{ + size_t max_report_len; + + qcdev->report_descriptor = devm_kzalloc(qcdev->dev, + le16_to_cpu(qcdev->dev_desc.report_desc_len), + GFP_KERNEL); + if (!qcdev->report_descriptor) + return -ENOMEM; + + /* + * Some HIDI2C devices don't declare input/output max length correctly, + * give default 4K buffer to avoid DMA buffer overrun. + */ + max_report_len = max(le16_to_cpu(qcdev->dev_desc.max_input_len), SZ_4K); + + qcdev->input_buf = devm_kzalloc(qcdev->dev, max_report_len, GFP_KERNEL); + if (!qcdev->input_buf) + return -ENOMEM; + + if (!le16_to_cpu(qcdev->dev_desc.max_output_len)) + qcdev->dev_desc.max_output_len = cpu_to_le16(SZ_4K); + + max_report_len = max(le16_to_cpu(qcdev->dev_desc.max_output_len), + max_report_len); + + qcdev->report_buf = devm_kzalloc(qcdev->dev, max_report_len, GFP_KERNEL); + if (!qcdev->report_buf) + return -ENOMEM; + + qcdev->report_len = max_report_len; + + return 0; +} + +/* + * quicki2c_probe: Quicki2c driver probe function + * + * @pdev: point to pci device + * @id: point to pci_device_id structure + * + * This function initializes THC and HIDI2C device, the flow is: + * - do THC pci device initialization + * - query HIDI2C ACPI parameters + * - configure THC to HIDI2C mode + * - go through HIDI2C enumeration flow + * |- read device descriptor + * |- reset HIDI2C device + * - enable THC interrupt and DMA + * - read report descriptor + * - register HID device + * - enable runtime power management + * + * Return 0 if success or error code on failed. + */ +static int quicki2c_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct quicki2c_device *qcdev; + void __iomem *mem_addr; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) { + dev_err_once(&pdev->dev, "Failed to enable PCI device, ret = %d.\n", ret); + return ret; + } + + pci_set_master(pdev); + + ret = pcim_iomap_regions(pdev, BIT(0), KBUILD_MODNAME); + if (ret) { + dev_err_once(&pdev->dev, "Failed to get PCI regions, ret = %d.\n", ret); + goto disable_pci_device; + } + + mem_addr = pcim_iomap_table(pdev)[0]; + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (ret) { + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err_once(&pdev->dev, "No usable DMA configuration %d\n", ret); + goto unmap_io_region; + } + } + + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); + if (ret < 0) { + dev_err_once(&pdev->dev, + "Failed to allocate IRQ vectors. ret = %d\n", ret); + goto unmap_io_region; + } + + pdev->irq = pci_irq_vector(pdev, 0); + + qcdev = quicki2c_dev_init(pdev, mem_addr); + if (IS_ERR(qcdev)) { + dev_err_once(&pdev->dev, "QuickI2C device init failed\n"); + ret = PTR_ERR(qcdev); + goto unmap_io_region; + } + + pci_set_drvdata(pdev, qcdev); + + ret = devm_request_threaded_irq(&pdev->dev, pdev->irq, + quicki2c_irq_quick_handler, + quicki2c_irq_thread_handler, + IRQF_ONESHOT, KBUILD_MODNAME, + qcdev); + if (ret) { + dev_err_once(&pdev->dev, + "Failed to request threaded IRQ, irq = %d.\n", pdev->irq); + goto dev_deinit; + } + + ret = quicki2c_get_device_descriptor(qcdev); + if (ret) { + dev_err(&pdev->dev, "Get device descriptor failed, ret = %d\n", ret); + goto dev_deinit; + } + + ret = quicki2c_alloc_report_buf(qcdev); + if (ret) { + dev_err(&pdev->dev, "Alloc report buffers failed, ret= %d\n", ret); + goto dev_deinit; + } + + ret = quicki2c_dma_init(qcdev); + if (ret) { + dev_err(&pdev->dev, "Setup THC DMA failed, ret= %d\n", ret); + goto dev_deinit; + } + + ret = thc_interrupt_quiesce(qcdev->thc_hw, false); + if (ret) + goto dev_deinit; + + ret = quicki2c_set_power(qcdev, HIDI2C_ON); + if (ret) { + dev_err(&pdev->dev, "Set Power On command failed, ret= %d\n", ret); + goto dev_deinit; + } + + ret = quicki2c_reset(qcdev); + if (ret) { + dev_err(&pdev->dev, "Reset HIDI2C device failed, ret= %d\n", ret); + goto dev_deinit; + } + + ret = quicki2c_get_report_descriptor(qcdev); + if (ret) { + dev_err(&pdev->dev, "Get report descriptor failed, ret = %d\n", ret); + goto dma_deinit; + } + + ret = quicki2c_hid_probe(qcdev); + if (ret) { + dev_err(&pdev->dev, "Failed to register HID device, ret = %d\n", ret); + goto dma_deinit; + } + + qcdev->state = QUICKI2C_ENABLED; + + /* Enable runtime power management */ + pm_runtime_use_autosuspend(qcdev->dev); + pm_runtime_set_autosuspend_delay(qcdev->dev, DEFAULT_AUTO_SUSPEND_DELAY_MS); + pm_runtime_mark_last_busy(qcdev->dev); + pm_runtime_put_noidle(qcdev->dev); + pm_runtime_put_autosuspend(qcdev->dev); + + dev_dbg(&pdev->dev, "QuickI2C probe success\n"); + + return 0; + +dma_deinit: + quicki2c_dma_deinit(qcdev); +dev_deinit: + quicki2c_dev_deinit(qcdev); +unmap_io_region: + pcim_iounmap_regions(pdev, BIT(0)); +disable_pci_device: + pci_clear_master(pdev); + + return ret; +} + +/** + * quicki2c_remove - Device Removal Routine + * + * @pdev: PCI device structure + * + * This is called by the PCI subsystem to alert the driver + * that it should release a PCI device. + */ +static void quicki2c_remove(struct pci_dev *pdev) +{ + struct quicki2c_device *qcdev; + + qcdev = pci_get_drvdata(pdev); + if (!qcdev) + return; + + quicki2c_hid_remove(qcdev); + quicki2c_dma_deinit(qcdev); + + pm_runtime_get_noresume(qcdev->dev); + + quicki2c_dev_deinit(qcdev); + + pcim_iounmap_regions(pdev, BIT(0)); + pci_clear_master(pdev); +} + +/** + * quicki2c_shutdown - Device Shutdown Routine + * + * @pdev: PCI device structure + * + * This is called from the reboot notifier + * it's a simplified version of remove so we go down + * faster. + */ +static void quicki2c_shutdown(struct pci_dev *pdev) +{ + struct quicki2c_device *qcdev; + + qcdev = pci_get_drvdata(pdev); + if (!qcdev) + return; + + /* Must stop DMA before reboot to avoid DMA entering into unknown state */ + quicki2c_dma_deinit(qcdev); + + quicki2c_dev_deinit(qcdev); +} + +static int quicki2c_suspend(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quicki2c_device *qcdev; + int ret; + + qcdev = pci_get_drvdata(pdev); + if (!qcdev) + return -ENODEV; + + /* + * As I2C is THC subsystem, no register auto save/restore support, + * need driver to do that explicitly for every D3 case. + */ + ret = thc_i2c_subip_regs_save(qcdev->thc_hw); + if (ret) + return ret; + + ret = thc_interrupt_quiesce(qcdev->thc_hw, true); + if (ret) + return ret; + + thc_interrupt_enable(qcdev->thc_hw, false); + + thc_dma_unconfigure(qcdev->thc_hw); + + return 0; +} + +static int quicki2c_resume(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quicki2c_device *qcdev; + int ret; + + qcdev = pci_get_drvdata(pdev); + if (!qcdev) + return -ENODEV; + + ret = thc_port_select(qcdev->thc_hw, THC_PORT_TYPE_I2C); + if (ret) + return ret; + + ret = thc_i2c_subip_regs_restore(qcdev->thc_hw); + if (ret) + return ret; + + thc_interrupt_config(qcdev->thc_hw); + + thc_interrupt_enable(qcdev->thc_hw, true); + + ret = thc_dma_configure(qcdev->thc_hw); + if (ret) + return ret; + + ret = thc_interrupt_quiesce(qcdev->thc_hw, false); + if (ret) + return ret; + + return 0; +} + +static int quicki2c_freeze(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quicki2c_device *qcdev; + int ret; + + qcdev = pci_get_drvdata(pdev); + if (!qcdev) + return -ENODEV; + + ret = thc_interrupt_quiesce(qcdev->thc_hw, true); + if (ret) + return ret; + + thc_interrupt_enable(qcdev->thc_hw, false); + + thc_dma_unconfigure(qcdev->thc_hw); + + return 0; +} + +static int quicki2c_thaw(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quicki2c_device *qcdev; + int ret; + + qcdev = pci_get_drvdata(pdev); + if (!qcdev) + return -ENODEV; + + ret = thc_dma_configure(qcdev->thc_hw); + if (ret) + return ret; + + thc_interrupt_enable(qcdev->thc_hw, true); + + ret = thc_interrupt_quiesce(qcdev->thc_hw, false); + if (ret) + return ret; + + return 0; +} + +static int quicki2c_poweroff(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quicki2c_device *qcdev; + int ret; + + qcdev = pci_get_drvdata(pdev); + if (!qcdev) + return -ENODEV; + + ret = thc_interrupt_quiesce(qcdev->thc_hw, true); + if (ret) + return ret; + + thc_interrupt_enable(qcdev->thc_hw, false); + + thc_ltr_unconfig(qcdev->thc_hw); + + quicki2c_dma_deinit(qcdev); + + return 0; +} + +static int quicki2c_restore(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quicki2c_device *qcdev; + int ret; + + qcdev = pci_get_drvdata(pdev); + if (!qcdev) + return -ENODEV; + + /* Reconfig THC HW when back from hibernate */ + ret = thc_port_select(qcdev->thc_hw, THC_PORT_TYPE_I2C); + if (ret) + return ret; + + ret = thc_i2c_subip_init(qcdev->thc_hw, qcdev->i2c_slave_addr, + qcdev->i2c_speed_mode, + qcdev->i2c_clock_hcnt, + qcdev->i2c_clock_lcnt); + if (ret) + return ret; + + thc_interrupt_config(qcdev->thc_hw); + + thc_interrupt_enable(qcdev->thc_hw, true); + + ret = thc_interrupt_quiesce(qcdev->thc_hw, false); + if (ret) + return ret; + + ret = thc_dma_configure(qcdev->thc_hw); + if (ret) + return ret; + + thc_ltr_config(qcdev->thc_hw, + qcdev->active_ltr_val, + qcdev->low_power_ltr_val); + + thc_change_ltr_mode(qcdev->thc_hw, THC_LTR_MODE_ACTIVE); + + return 0; +} + +static int quicki2c_runtime_suspend(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quicki2c_device *qcdev; + + qcdev = pci_get_drvdata(pdev); + if (!qcdev) + return -ENODEV; + + thc_change_ltr_mode(qcdev->thc_hw, THC_LTR_MODE_LP); + + pci_save_state(pdev); + + return 0; +} + +static int quicki2c_runtime_resume(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quicki2c_device *qcdev; + + qcdev = pci_get_drvdata(pdev); + if (!qcdev) + return -ENODEV; + + thc_change_ltr_mode(qcdev->thc_hw, THC_LTR_MODE_ACTIVE); + + return 0; +} + +static const struct dev_pm_ops quicki2c_pm_ops = { + .suspend = quicki2c_suspend, + .resume = quicki2c_resume, + .freeze = quicki2c_freeze, + .thaw = quicki2c_thaw, + .poweroff = quicki2c_poweroff, + .restore = quicki2c_restore, + .runtime_suspend = quicki2c_runtime_suspend, + .runtime_resume = quicki2c_runtime_resume, + .runtime_idle = NULL, +}; + +static const struct pci_device_id quicki2c_pci_tbl[] = { + {PCI_VDEVICE(INTEL, THC_LNL_DEVICE_ID_I2C_PORT1), }, + {PCI_VDEVICE(INTEL, THC_LNL_DEVICE_ID_I2C_PORT2), }, + {PCI_VDEVICE(INTEL, THC_PTL_H_DEVICE_ID_I2C_PORT1), }, + {PCI_VDEVICE(INTEL, THC_PTL_H_DEVICE_ID_I2C_PORT2), }, + {PCI_VDEVICE(INTEL, THC_PTL_U_DEVICE_ID_I2C_PORT1), }, + {PCI_VDEVICE(INTEL, THC_PTL_U_DEVICE_ID_I2C_PORT2), }, + {} +}; +MODULE_DEVICE_TABLE(pci, quicki2c_pci_tbl); + +static struct pci_driver quicki2c_driver = { + .name = KBUILD_MODNAME, + .id_table = quicki2c_pci_tbl, + .probe = quicki2c_probe, + .remove = quicki2c_remove, + .shutdown = quicki2c_shutdown, + .driver.pm = &quicki2c_pm_ops, + .driver.probe_type = PROBE_PREFER_ASYNCHRONOUS, +}; + +module_pci_driver(quicki2c_driver); + +MODULE_AUTHOR("Xinpeng Sun <xinpeng.sun@intel.com>"); +MODULE_AUTHOR("Even Xu <even.xu@intel.com>"); + +MODULE_DESCRIPTION("Intel(R) QuickI2C Driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("INTEL_THC"); diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h new file mode 100644 index 000000000000..6ddb584bd611 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h @@ -0,0 +1,186 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#ifndef _QUICKI2C_DEV_H_ +#define _QUICKI2C_DEV_H_ + +#include <linux/hid-over-i2c.h> +#include <linux/workqueue.h> + +#define THC_LNL_DEVICE_ID_I2C_PORT1 0xA848 +#define THC_LNL_DEVICE_ID_I2C_PORT2 0xA84A +#define THC_PTL_H_DEVICE_ID_I2C_PORT1 0xE348 +#define THC_PTL_H_DEVICE_ID_I2C_PORT2 0xE34A +#define THC_PTL_U_DEVICE_ID_I2C_PORT1 0xE448 +#define THC_PTL_U_DEVICE_ID_I2C_PORT2 0xE44A + +/* Packet size value, the unit is 16 bytes */ +#define MAX_PACKET_SIZE_VALUE_LNL 256 + +/* HIDI2C special ACPI parameters DSD name */ +#define QUICKI2C_ACPI_METHOD_NAME_ICRS "ICRS" +#define QUICKI2C_ACPI_METHOD_NAME_ISUB "ISUB" + +/* HIDI2C special ACPI parameters DSM methods */ +#define QUICKI2C_ACPI_REVISION_NUM 1 +#define QUICKI2C_ACPI_FUNC_NUM_HID_DESC_ADDR 1 +#define QUICKI2C_ACPI_FUNC_NUM_ACTIVE_LTR_VAL 1 +#define QUICKI2C_ACPI_FUNC_NUM_LP_LTR_VAL 2 + +#define QUICKI2C_SUBIP_STANDARD_MODE_MAX_SPEED 100000 +#define QUICKI2C_SUBIP_FAST_MODE_MAX_SPEED 400000 +#define QUICKI2C_SUBIP_FASTPLUS_MODE_MAX_SPEED 1000000 +#define QUICKI2C_SUBIP_HIGH_SPEED_MODE_MAX_SPEED 3400000 + +#define QUICKI2C_DEFAULT_ACTIVE_LTR_VALUE 5 +#define QUICKI2C_DEFAULT_LP_LTR_VALUE 500 +#define QUICKI2C_RPM_TIMEOUT_MS 500 + +/* + * THC uses runtime auto suspend to dynamically switch between THC active LTR + * and low power LTR to save CPU power. + * Default value is 5000ms, that means if no touch event in this time, THC will + * change to low power LTR mode. + */ +#define DEFAULT_AUTO_SUSPEND_DELAY_MS 5000 + +enum quicki2c_dev_state { + QUICKI2C_NONE, + QUICKI2C_RESETING, + QUICKI2C_RESETED, + QUICKI2C_INITED, + QUICKI2C_ENABLED, + QUICKI2C_DISABLED, +}; + +enum { + HIDI2C_ADDRESSING_MODE_7BIT, + HIDI2C_ADDRESSING_MODE_10BIT, +}; + +/** + * struct quicki2c_subip_acpi_parameter - QuickI2C ACPI DSD parameters + * @device_address: I2C device slave address + * @connection_speed: I2C device expected connection speed + * @addressing_mode: I2C device slave address mode, 7bit or 10bit + * + * Those properties get from QUICKI2C_ACPI_METHOD_NAME_ICRS method, used for + * Bus parameter. + */ +struct quicki2c_subip_acpi_parameter { + u16 device_address; + u64 connection_speed; + u8 addressing_mode; +} __packed; + +/** + * struct quicki2c_subip_acpi_config - QuickI2C ACPI DSD parameters + * @SMHX: Standard Mode (100 kbit/s) Serial Clock Line HIGH Period + * @SMLX: Standard Mode (100 kbit/s) Serial Clock Line LOW Period + * @SMTD: Standard Mode (100 kbit/s) Serial Data Line Transmit Hold Period + * @SMRD: Standard Mode (100 kbit/s) Serial Data Receive Hold Period + * @FMHX: Fast Mode (400 kbit/s) Serial Clock Line HIGH Period + * @FMLX: Fast Mode (400 kbit/s) Serial Clock Line LOW Period + * @FMTD: Fast Mode (400 kbit/s) Serial Data Line Transmit Hold Period + * @FMRD: Fast Mode (400 kbit/s) Serial Data Line Receive Hold Period + * @FMSL: Maximum length (in ic_clk_cycles) of suppressed spikes + * in Standard Mode, Fast Mode and Fast Mode Plus + * @FPHX: Fast Mode Plus (1Mbit/sec) Serial Clock Line HIGH Period + * @FPLX: Fast Mode Plus (1Mbit/sec) Serial Clock Line LOW Period + * @FPTD: Fast Mode Plus (1Mbit/sec) Serial Data Line Transmit HOLD Period + * @FPRD: Fast Mode Plus (1Mbit/sec) Serial Data Line Receive HOLD Period + * @HMHX: High Speed Mode Plus (3.4Mbits/sec) Serial Clock Line HIGH Period + * @HMLX: High Speed Mode Plus (3.4Mbits/sec) Serial Clock Line LOW Period + * @HMTD: High Speed Mode Plus (3.4Mbits/sec) Serial Data Line Transmit HOLD Period + * @HMRD: High Speed Mode Plus (3.4Mbits/sec) Serial Data Line Receive HOLD Period + * @HMSL: Maximum length (in ic_clk_cycles) of suppressed spikes in High Speed Mode + * + * Those properties get from QUICKI2C_ACPI_METHOD_NAME_ISUB method, used for + * I2C timing configure. + */ +struct quicki2c_subip_acpi_config { + u64 SMHX; + u64 SMLX; + u64 SMTD; + u64 SMRD; + + u64 FMHX; + u64 FMLX; + u64 FMTD; + u64 FMRD; + u64 FMSL; + + u64 FPHX; + u64 FPLX; + u64 FPTD; + u64 FPRD; + + u64 HMHX; + u64 HMLX; + u64 HMTD; + u64 HMRD; + u64 HMSL; +}; + +struct device; +struct pci_dev; +struct thc_device; +struct hid_device; +struct acpi_device; + +/** + * struct quicki2c_device - THC QuickI2C device struct + * @dev: point to kernel device + * @pdev: point to PCI device + * @thc_hw: point to THC device + * @hid_dev: point to hid device + * @acpi_dev: point to ACPI device + * @driver_data: point to quicki2c specific driver data + * @state: THC I2C device state + * @mem_addr: MMIO memory address + * @dev_desc: device descriptor for HIDI2C protocol + * @i2c_slave_addr: HIDI2C device slave address + * @hid_desc_addr: Register address for retrieve HID device descriptor + * @active_ltr_val: THC active LTR value + * @low_power_ltr_val: THC low power LTR value + * @i2c_speed_mode: 0 - standard mode, 1 - fast mode, 2 - fast mode plus + * @i2c_clock_hcnt: I2C CLK high period time (unit in cycle count) + * @i2c_clock_lcnt: I2C CLK low period time (unit in cycle count) + * @report_descriptor: store a copy of device report descriptor + * @input_buf: store a copy of latest input report data + * @report_buf: store a copy of latest input/output report packet from set/get feature + * @report_len: the length of input/output report packet + * @reset_ack_wq: workqueue for waiting reset response from device + * @reset_ack: indicate reset response received or not + */ +struct quicki2c_device { + struct device *dev; + struct pci_dev *pdev; + struct thc_device *thc_hw; + struct hid_device *hid_dev; + struct acpi_device *acpi_dev; + enum quicki2c_dev_state state; + + void __iomem *mem_addr; + + struct hidi2c_dev_descriptor dev_desc; + u8 i2c_slave_addr; + u16 hid_desc_addr; + + u32 active_ltr_val; + u32 low_power_ltr_val; + + u32 i2c_speed_mode; + u32 i2c_clock_hcnt; + u32 i2c_clock_lcnt; + + u8 *report_descriptor; + u8 *input_buf; + u8 *report_buf; + u32 report_len; + + wait_queue_head_t reset_ack_wq; + bool reset_ack; +}; + +#endif /* _QUICKI2C_DEV_H_ */ diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c new file mode 100644 index 000000000000..5c3ec95bb3fd --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#include <linux/hid.h> +#include <linux/input.h> +#include <linux/pm_runtime.h> + +#include "quicki2c-dev.h" +#include "quicki2c-hid.h" +#include "quicki2c-protocol.h" + +/** + * quicki2c_hid_parse() - HID core parse() callback + * + * @hid: HID device instance + * + * This function gets called during call to hid_add_device + * + * Return: 0 on success and non zero on error. + */ +static int quicki2c_hid_parse(struct hid_device *hid) +{ + struct quicki2c_device *qcdev = hid->driver_data; + + if (qcdev->report_descriptor) + return hid_parse_report(hid, qcdev->report_descriptor, + le16_to_cpu(qcdev->dev_desc.report_desc_len)); + + dev_err_once(qcdev->dev, "invalid report descriptor\n"); + return -EINVAL; +} + +static int quicki2c_hid_start(struct hid_device *hid) +{ + return 0; +} + +static void quicki2c_hid_stop(struct hid_device *hid) +{ +} + +static int quicki2c_hid_open(struct hid_device *hid) +{ + return 0; +} + +static void quicki2c_hid_close(struct hid_device *hid) +{ +} + +static int quicki2c_hid_raw_request(struct hid_device *hid, + unsigned char reportnum, + __u8 *buf, size_t len, + unsigned char rtype, int reqtype) +{ + struct quicki2c_device *qcdev = hid->driver_data; + int ret = 0; + + ret = pm_runtime_resume_and_get(qcdev->dev); + if (ret) + return ret; + + switch (reqtype) { + case HID_REQ_GET_REPORT: + ret = quicki2c_get_report(qcdev, rtype, reportnum, buf, len); + break; + case HID_REQ_SET_REPORT: + ret = quicki2c_set_report(qcdev, rtype, reportnum, buf, len); + break; + default: + dev_err(qcdev->dev, "Not supported request type %d\n", reqtype); + break; + } + + pm_runtime_mark_last_busy(qcdev->dev); + pm_runtime_put_autosuspend(qcdev->dev); + + return ret; +} + +static int quicki2c_hid_power(struct hid_device *hid, int lvl) +{ + return 0; +} + +static struct hid_ll_driver quicki2c_hid_ll_driver = { + .parse = quicki2c_hid_parse, + .start = quicki2c_hid_start, + .stop = quicki2c_hid_stop, + .open = quicki2c_hid_open, + .close = quicki2c_hid_close, + .power = quicki2c_hid_power, + .raw_request = quicki2c_hid_raw_request, +}; + +/** + * quicki2c_hid_probe() - Register HID low level driver + * + * @qcdev: point to quicki2c device + * + * This function is used to allocate and add HID device. + * + * Return: 0 on success, non zero on error. + */ +int quicki2c_hid_probe(struct quicki2c_device *qcdev) +{ + struct hid_device *hid; + int ret; + + hid = hid_allocate_device(); + if (IS_ERR(hid)) + return PTR_ERR(hid); + + hid->ll_driver = &quicki2c_hid_ll_driver; + hid->bus = BUS_PCI; + hid->dev.parent = qcdev->dev; + hid->driver_data = qcdev; + hid->version = le16_to_cpu(qcdev->dev_desc.version_id); + hid->vendor = le16_to_cpu(qcdev->dev_desc.vendor_id); + hid->product = le16_to_cpu(qcdev->dev_desc.product_id); + snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "quicki2c-hid", + hid->vendor, hid->product); + + ret = hid_add_device(hid); + if (ret) { + hid_destroy_device(hid); + return ret; + } + + qcdev->hid_dev = hid; + + return 0; +} + +/** + * quicki2c_hid_remove() - Destroy HID device + * + * @qcdev: point to quicki2c device + * + * Return: 0 on success, non zero on error. + */ +void quicki2c_hid_remove(struct quicki2c_device *qcdev) +{ + hid_destroy_device(qcdev->hid_dev); +} + +/** + * quicki2c_hid_send_report() - Send HID input report data to HID core + * + * @qcdev: point to quicki2c device + * @data: point to input report data buffer + * @data_len: the length of input report data + * + * Return: 0 on success, non zero on error. + */ +int quicki2c_hid_send_report(struct quicki2c_device *qcdev, + void *data, size_t data_len) +{ + int ret; + + ret = hid_input_report(qcdev->hid_dev, HID_INPUT_REPORT, data, data_len, 1); + if (ret) + dev_err(qcdev->dev, "Failed to send HID input report, ret = %d.\n", ret); + + return ret; +} diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.h b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.h new file mode 100644 index 000000000000..e80df5f339fe --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#ifndef _QUICKI2C_HID_H_ +#define _QUICKI2C_HID_H_ + +struct quicki2c_device; + +int quicki2c_hid_send_report(struct quicki2c_device *qcdev, + void *data, size_t data_size); +int quicki2c_hid_probe(struct quicki2c_device *qcdev); +void quicki2c_hid_remove(struct quicki2c_device *qcdev); + +#endif /* _QUICKI2C_HID_H_ */ diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c new file mode 100644 index 000000000000..f493df0d5dc4 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.c @@ -0,0 +1,224 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2024 Intel Corporation */ + +#include <linux/bitfield.h> +#include <linux/hid.h> +#include <linux/hid-over-i2c.h> + +#include "intel-thc-dev.h" +#include "intel-thc-dma.h" + +#include "quicki2c-dev.h" +#include "quicki2c-hid.h" +#include "quicki2c-protocol.h" + +static int quicki2c_init_write_buf(struct quicki2c_device *qcdev, u32 cmd, int cmd_len, + bool append_data_reg, u8 *data, int data_len, + u8 *write_buf, int write_buf_len) +{ + int buf_len, offset = 0; + + buf_len = HIDI2C_REG_LEN + cmd_len; + + if (append_data_reg) + buf_len += HIDI2C_REG_LEN; + + if (data && data_len) + buf_len += data_len + HIDI2C_LENGTH_LEN; + + if (buf_len > write_buf_len) + return -EINVAL; + + memcpy(write_buf, &qcdev->dev_desc.cmd_reg, HIDI2C_REG_LEN); + offset += HIDI2C_REG_LEN; + memcpy(write_buf + offset, &cmd, cmd_len); + offset += cmd_len; + + if (append_data_reg) { + memcpy(write_buf + offset, &qcdev->dev_desc.data_reg, HIDI2C_REG_LEN); + offset += HIDI2C_REG_LEN; + } + + if (data && data_len) { + __le16 len = cpu_to_le16(data_len + HIDI2C_LENGTH_LEN); + + memcpy(write_buf + offset, &len, HIDI2C_LENGTH_LEN); + offset += HIDI2C_LENGTH_LEN; + memcpy(write_buf + offset, data, data_len); + } + + return buf_len; +} + +static int quicki2c_encode_cmd(struct quicki2c_device *qcdev, u32 *cmd_buf, + u8 opcode, u8 report_type, u8 report_id) +{ + int cmd_len; + + *cmd_buf = FIELD_PREP(HIDI2C_CMD_OPCODE, opcode) | + FIELD_PREP(HIDI2C_CMD_REPORT_TYPE, report_type); + + if (report_id < HIDI2C_CMD_MAX_RI) { + *cmd_buf |= FIELD_PREP(HIDI2C_CMD_REPORT_ID, report_id); + cmd_len = HIDI2C_CMD_LEN; + } else { + *cmd_buf |= FIELD_PREP(HIDI2C_CMD_REPORT_ID, HIDI2C_CMD_MAX_RI) | + FIELD_PREP(HIDI2C_CMD_3RD_BYTE, report_id); + cmd_len = HIDI2C_CMD_LEN_OPT; + } + + return cmd_len; +} + +static int write_cmd_to_txdma(struct quicki2c_device *qcdev, int opcode, + int report_type, int report_id, u8 *buf, int buf_len) +{ + size_t write_buf_len; + int cmd_len, ret; + u32 cmd; + + cmd_len = quicki2c_encode_cmd(qcdev, &cmd, opcode, report_type, report_id); + + ret = quicki2c_init_write_buf(qcdev, cmd, cmd_len, buf ? true : false, buf, + buf_len, qcdev->report_buf, qcdev->report_len); + if (ret < 0) + return ret; + + write_buf_len = ret; + + return thc_dma_write(qcdev->thc_hw, qcdev->report_buf, write_buf_len); +} + +int quicki2c_set_power(struct quicki2c_device *qcdev, enum hidi2c_power_state power_state) +{ + return write_cmd_to_txdma(qcdev, HIDI2C_SET_POWER, HIDI2C_RESERVED, power_state, NULL, 0); +} + +int quicki2c_get_device_descriptor(struct quicki2c_device *qcdev) +{ + u32 read_len = 0; + int ret; + + ret = thc_tic_pio_write_and_read(qcdev->thc_hw, qcdev->hid_desc_addr, + HIDI2C_REG_LEN, NULL, HIDI2C_DEV_DESC_LEN, + &read_len, (u32 *)&qcdev->dev_desc); + if (ret || HIDI2C_DEV_DESC_LEN != read_len) { + dev_err_once(qcdev->dev, "Get device descriptor failed, ret %d, read len %u\n", + ret, read_len); + return -EIO; + } + + if (le16_to_cpu(qcdev->dev_desc.bcd_ver) != HIDI2C_HID_DESC_BCDVERSION) + return -EOPNOTSUPP; + + return 0; +} + +int quicki2c_get_report_descriptor(struct quicki2c_device *qcdev) +{ + u16 desc_reg = le16_to_cpu(qcdev->dev_desc.report_desc_reg); + size_t read_len = le16_to_cpu(qcdev->dev_desc.report_desc_len); + u32 prd_len = read_len; + + return thc_swdma_read(qcdev->thc_hw, (u8 *)&desc_reg, HIDI2C_REG_LEN, + &prd_len, qcdev->report_descriptor, &read_len); +} + +int quicki2c_get_report(struct quicki2c_device *qcdev, u8 report_type, + unsigned int reportnum, void *buf, u32 buf_len) +{ + struct hidi2c_report_packet *rpt; + size_t write_buf_len, read_len = 0; + int cmd_len, rep_type; + u32 cmd; + int ret; + + if (report_type == HID_INPUT_REPORT) { + rep_type = HIDI2C_INPUT; + } else if (report_type == HID_FEATURE_REPORT) { + rep_type = HIDI2C_FEATURE; + } else { + dev_err(qcdev->dev, "Unsupported report type for GET REPORT: %d\n", report_type); + return -EINVAL; + } + + cmd_len = quicki2c_encode_cmd(qcdev, &cmd, HIDI2C_GET_REPORT, rep_type, reportnum); + + ret = quicki2c_init_write_buf(qcdev, cmd, cmd_len, true, NULL, 0, + qcdev->report_buf, qcdev->report_len); + if (ret < 0) + return ret; + + write_buf_len = ret; + + rpt = (struct hidi2c_report_packet *)qcdev->input_buf; + + ret = thc_swdma_read(qcdev->thc_hw, qcdev->report_buf, write_buf_len, + NULL, rpt, &read_len); + if (ret) { + dev_err_once(qcdev->dev, "Get report failed, ret %d, read len (%zu vs %d)\n", + ret, read_len, buf_len); + return ret; + } + + if (HIDI2C_DATA_LEN(le16_to_cpu(rpt->len)) != buf_len || rpt->data[0] != reportnum) { + dev_err_once(qcdev->dev, "Invalid packet, len (%d vs %d) report id (%d vs %d)\n", + le16_to_cpu(rpt->len), buf_len, rpt->data[0], reportnum); + return -EINVAL; + } + + memcpy(buf, rpt->data, buf_len); + + return buf_len; +} + +int quicki2c_set_report(struct quicki2c_device *qcdev, u8 report_type, + unsigned int reportnum, void *buf, u32 buf_len) +{ + int rep_type; + int ret; + + if (report_type == HID_OUTPUT_REPORT) { + rep_type = HIDI2C_OUTPUT; + } else if (report_type == HID_FEATURE_REPORT) { + rep_type = HIDI2C_FEATURE; + } else { + dev_err(qcdev->dev, "Unsupported report type for SET REPORT: %d\n", report_type); + return -EINVAL; + } + + ret = write_cmd_to_txdma(qcdev, HIDI2C_SET_REPORT, rep_type, reportnum, buf, buf_len); + if (ret) { + dev_err_once(qcdev->dev, "Set Report failed, ret %d\n", ret); + return ret; + } + + return buf_len; +} + +#define HIDI2C_RESET_TIMEOUT 5 + +int quicki2c_reset(struct quicki2c_device *qcdev) +{ + int ret; + + qcdev->reset_ack = false; + qcdev->state = QUICKI2C_RESETING; + + ret = write_cmd_to_txdma(qcdev, HIDI2C_RESET, HIDI2C_RESERVED, 0, NULL, 0); + if (ret) { + dev_err_once(qcdev->dev, "Send reset command failed, ret %d\n", ret); + return ret; + } + + ret = wait_event_interruptible_timeout(qcdev->reset_ack_wq, qcdev->reset_ack, + HIDI2C_RESET_TIMEOUT * HZ); + if (ret <= 0 || !qcdev->reset_ack) { + dev_err_once(qcdev->dev, + "Wait reset response timed out ret:%d timeout:%ds\n", + ret, HIDI2C_RESET_TIMEOUT); + return -ETIMEDOUT; + } + + return 0; +} diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h new file mode 100644 index 000000000000..bf4908cce59c --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-protocol.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#ifndef _QUICKI2C_PROTOCOL_H_ +#define _QUICKI2C_PROTOCOL_H_ + +#include <linux/hid-over-i2c.h> + +struct quicki2c_device; + +int quicki2c_set_power(struct quicki2c_device *qcdev, enum hidi2c_power_state power_state); +int quicki2c_get_report(struct quicki2c_device *qcdev, u8 report_type, + unsigned int reportnum, void *buf, u32 buf_len); +int quicki2c_set_report(struct quicki2c_device *qcdev, u8 report_type, + unsigned int reportnum, void *buf, u32 buf_len); +int quicki2c_get_device_descriptor(struct quicki2c_device *qcdev); +int quicki2c_get_report_descriptor(struct quicki2c_device *qcdev); +int quicki2c_reset(struct quicki2c_device *qcdev); + +#endif /* _QUICKI2C_PROTOCOL_H_ */ diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c new file mode 100644 index 000000000000..4641e818dfa4 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c @@ -0,0 +1,987 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#include <linux/acpi.h> +#include <linux/bitfield.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/irqreturn.h> +#include <linux/pci.h> +#include <linux/pm_runtime.h> + +#include "intel-thc-dev.h" +#include "intel-thc-hw.h" + +#include "quickspi-dev.h" +#include "quickspi-hid.h" +#include "quickspi-protocol.h" + +struct quickspi_driver_data mtl = { + .max_packet_size_value = MAX_PACKET_SIZE_VALUE_MTL, +}; + +struct quickspi_driver_data lnl = { + .max_packet_size_value = MAX_PACKET_SIZE_VALUE_LNL, +}; + +struct quickspi_driver_data ptl = { + .max_packet_size_value = MAX_PACKET_SIZE_VALUE_LNL, +}; + +/* THC QuickSPI ACPI method to get device properties */ +/* HIDSPI Method: {6e2ac436-0fcf-41af-a265-b32a220dcfab} */ +static guid_t hidspi_guid = + GUID_INIT(0x6e2ac436, 0x0fcf, 0x41af, 0xa2, 0x65, 0xb3, 0x2a, + 0x22, 0x0d, 0xcf, 0xab); + +/* QuickSpi Method: {300D35b7-ac20-413e-8e9c-92e4dafd0afe} */ +static guid_t thc_quickspi_guid = + GUID_INIT(0x300d35b7, 0xac20, 0x413e, 0x8e, 0x9c, 0x92, 0xe4, + 0xda, 0xfd, 0x0a, 0xfe); + +/* Platform Method: {84005682-5b71-41a4-0x8d668130f787a138} */ +static guid_t thc_platform_guid = + GUID_INIT(0x84005682, 0x5b71, 0x41a4, 0x8d, 0x66, 0x81, 0x30, + 0xf7, 0x87, 0xa1, 0x38); + +/** + * thc_acpi_get_property - Query device ACPI parameter + * + * @adev: point to ACPI device + * @guid: ACPI method's guid + * @rev: ACPI method's revision + * @func: ACPI method's function number + * @type: ACPI parameter's data type + * @prop_buf: point to return buffer + * + * This is a helper function for device to query its ACPI parameters. + * + * Return: 0 if successful or ENODEV on failed. + */ +static int thc_acpi_get_property(struct acpi_device *adev, const guid_t *guid, + u64 rev, u64 func, acpi_object_type type, void *prop_buf) +{ + acpi_handle handle = acpi_device_handle(adev); + union acpi_object *obj; + + obj = acpi_evaluate_dsm_typed(handle, guid, rev, func, NULL, type); + if (!obj) { + acpi_handle_err(handle, + "Error _DSM call failed, rev: %llu, func: %llu, type: %u\n", + rev, func, type); + return -ENODEV; + } + + if (type == ACPI_TYPE_INTEGER) + *(u32 *)prop_buf = (u32)obj->integer.value; + else if (type == ACPI_TYPE_BUFFER) + memcpy(prop_buf, obj->buffer.pointer, obj->buffer.length); + + ACPI_FREE(obj); + + return 0; +} + +/** + * quickspi_get_acpi_resources - Query all quickspi devices' ACPI parameters + * + * @qsdev: point to quickspi device + * + * This function gets all quickspi devices' ACPI resource. + * + * Return: 0 if successful or error code on failed. + */ +static int quickspi_get_acpi_resources(struct quickspi_device *qsdev) +{ + struct acpi_device *adev = ACPI_COMPANION(qsdev->dev); + int ret = -EINVAL; + + if (!adev) { + dev_err(qsdev->dev, "no valid ACPI companion\n"); + return ret; + } + + qsdev->acpi_dev = adev; + + ret = thc_acpi_get_property(adev, &hidspi_guid, + ACPI_QUICKSPI_REVISION_NUM, + ACPI_QUICKSPI_FUNC_NUM_INPUT_REP_HDR_ADDR, + ACPI_TYPE_INTEGER, + &qsdev->input_report_hdr_addr); + if (ret) + return ret; + + ret = thc_acpi_get_property(adev, &hidspi_guid, + ACPI_QUICKSPI_REVISION_NUM, + ACPI_QUICKSPI_FUNC_NUM_INPUT_REP_BDY_ADDR, + ACPI_TYPE_INTEGER, + &qsdev->input_report_bdy_addr); + if (ret) + return ret; + + ret = thc_acpi_get_property(adev, &hidspi_guid, + ACPI_QUICKSPI_REVISION_NUM, + ACPI_QUICKSPI_FUNC_NUM_OUTPUT_REP_ADDR, + ACPI_TYPE_INTEGER, + &qsdev->output_report_addr); + if (ret) + return ret; + + ret = thc_acpi_get_property(adev, &hidspi_guid, + ACPI_QUICKSPI_REVISION_NUM, + ACPI_QUICKSPI_FUNC_NUM_READ_OPCODE, + ACPI_TYPE_BUFFER, + &qsdev->spi_read_opcode); + if (ret) + return ret; + + ret = thc_acpi_get_property(adev, &hidspi_guid, + ACPI_QUICKSPI_REVISION_NUM, + ACPI_QUICKSPI_FUNC_NUM_WRITE_OPCODE, + ACPI_TYPE_BUFFER, + &qsdev->spi_write_opcode); + if (ret) + return ret; + + ret = thc_acpi_get_property(adev, &hidspi_guid, + ACPI_QUICKSPI_REVISION_NUM, + ACPI_QUICKSPI_FUNC_NUM_IO_MODE, + ACPI_TYPE_INTEGER, + &qsdev->spi_read_io_mode); + if (ret) + return ret; + + if (qsdev->spi_read_io_mode & SPI_WRITE_IO_MODE) + qsdev->spi_write_io_mode = FIELD_GET(SPI_IO_MODE_OPCODE, qsdev->spi_read_io_mode); + else + qsdev->spi_write_io_mode = THC_SINGLE_IO; + + qsdev->spi_read_io_mode = FIELD_GET(SPI_IO_MODE_OPCODE, qsdev->spi_read_io_mode); + + ret = thc_acpi_get_property(adev, &thc_quickspi_guid, + ACPI_QUICKSPI_REVISION_NUM, + ACPI_QUICKSPI_FUNC_NUM_CONNECTION_SPEED, + ACPI_TYPE_INTEGER, + &qsdev->spi_freq_val); + if (ret) + return ret; + + ret = thc_acpi_get_property(adev, &thc_quickspi_guid, + ACPI_QUICKSPI_REVISION_NUM, + ACPI_QUICKSPI_FUNC_NUM_LIMIT_PACKET_SIZE, + ACPI_TYPE_INTEGER, + &qsdev->limit_packet_size); + if (ret) + return ret; + + if (qsdev->limit_packet_size || !qsdev->driver_data) + qsdev->spi_packet_size = DEFAULT_MIN_PACKET_SIZE_VALUE; + else + qsdev->spi_packet_size = qsdev->driver_data->max_packet_size_value; + + ret = thc_acpi_get_property(adev, &thc_quickspi_guid, + ACPI_QUICKSPI_REVISION_NUM, + ACPI_QUICKSPI_FUNC_NUM_PERFORMANCE_LIMIT, + ACPI_TYPE_INTEGER, + &qsdev->performance_limit); + if (ret) + return ret; + + qsdev->performance_limit = FIELD_GET(PERFORMANCE_LIMITATION, qsdev->performance_limit); + + ret = thc_acpi_get_property(adev, &thc_platform_guid, + ACPI_QUICKSPI_REVISION_NUM, + ACPI_QUICKSPI_FUNC_NUM_ACTIVE_LTR, + ACPI_TYPE_INTEGER, + &qsdev->active_ltr_val); + if (ret) + return ret; + + ret = thc_acpi_get_property(adev, &thc_platform_guid, + ACPI_QUICKSPI_REVISION_NUM, + ACPI_QUICKSPI_FUNC_NUM_LP_LTR, + ACPI_TYPE_INTEGER, + &qsdev->low_power_ltr_val); + if (ret) + return ret; + + return 0; +} + +/** + * quickspi_irq_quick_handler - The ISR of the quickspi driver + * + * @irq: The irq number + * @dev_id: pointer to the device structure + * + * Return: IRQ_WAKE_THREAD if further process needed. + */ +static irqreturn_t quickspi_irq_quick_handler(int irq, void *dev_id) +{ + struct quickspi_device *qsdev = dev_id; + + if (qsdev->state == QUICKSPI_DISABLED) + return IRQ_HANDLED; + + /* Disable THC interrupt before current interrupt be handled */ + thc_interrupt_enable(qsdev->thc_hw, false); + + return IRQ_WAKE_THREAD; +} + +/** + * try_recover - Try to recovery THC and Device + * @qsdev: pointer to quickspi device + * + * This function is a error handler, called when fatal error happens. + * It try to reset Touch Device and re-configure THC to recovery + * transferring between Device and THC. + * + * Return: 0 if successful or error code on failed. + */ +static int try_recover(struct quickspi_device *qsdev) +{ + int ret; + + ret = reset_tic(qsdev); + if (ret) { + dev_err(qsdev->dev, "Reset touch device failed, ret = %d\n", ret); + return ret; + } + + thc_dma_unconfigure(qsdev->thc_hw); + + ret = thc_dma_configure(qsdev->thc_hw); + if (ret) { + dev_err(qsdev->dev, "Re-configure THC DMA failed, ret = %d\n", ret); + return ret; + } + + return 0; +} + +/** + * quickspi_irq_thread_handler - IRQ thread handler of quickspi driver + * + * @irq: The IRQ number + * @dev_id: pointer to the quickspi device structure + * + * Return: IRQ_HANDLED to finish this handler. + */ +static irqreturn_t quickspi_irq_thread_handler(int irq, void *dev_id) +{ + struct quickspi_device *qsdev = dev_id; + size_t input_len; + int read_finished = 0; + int err_recover = 0; + int int_mask; + int ret; + + if (qsdev->state == QUICKSPI_DISABLED) + return IRQ_HANDLED; + + ret = pm_runtime_resume_and_get(qsdev->dev); + if (ret) + return IRQ_HANDLED; + + int_mask = thc_interrupt_handler(qsdev->thc_hw); + + if (int_mask & BIT(THC_FATAL_ERR_INT) || int_mask & BIT(THC_TXN_ERR_INT)) { + err_recover = 1; + goto end; + } + + if (int_mask & BIT(THC_NONDMA_INT)) { + if (qsdev->state == QUICKSPI_RESETING) { + qsdev->reset_ack = true; + wake_up_interruptible(&qsdev->reset_ack_wq); + } else { + qsdev->nondma_int_received = true; + wake_up_interruptible(&qsdev->nondma_int_received_wq); + } + } + + if (int_mask & BIT(THC_RXDMA2_INT)) { + while (!read_finished) { + ret = thc_rxdma_read(qsdev->thc_hw, THC_RXDMA2, qsdev->input_buf, + &input_len, &read_finished); + if (ret) { + err_recover = 1; + goto end; + } + + quickspi_handle_input_data(qsdev, input_len); + } + } + +end: + thc_interrupt_enable(qsdev->thc_hw, true); + + if (err_recover) + if (try_recover(qsdev)) + qsdev->state = QUICKSPI_DISABLED; + + pm_runtime_mark_last_busy(qsdev->dev); + pm_runtime_put_autosuspend(qsdev->dev); + + return IRQ_HANDLED; +} + +/** + * quickspi_dev_init - Initialize quickspi device + * + * @pdev: pointer to the thc pci device + * @mem_addr: The pointer of MMIO memory address + * @id: point to pci_device_id structure + * + * Alloc quickspi device structure and initialized THC device, + * then configure THC to HIDSPI mode. + * + * If success, enable THC hardware interrupt. + * + * Return: pointer to the quickspi device structure if success + * or NULL on failed. + */ +static struct quickspi_device *quickspi_dev_init(struct pci_dev *pdev, void __iomem *mem_addr, + const struct pci_device_id *id) +{ + struct device *dev = &pdev->dev; + struct quickspi_device *qsdev; + int ret; + + qsdev = devm_kzalloc(dev, sizeof(struct quickspi_device), GFP_KERNEL); + if (!qsdev) + return ERR_PTR(-ENOMEM); + + qsdev->pdev = pdev; + qsdev->dev = dev; + qsdev->mem_addr = mem_addr; + qsdev->state = QUICKSPI_DISABLED; + qsdev->driver_data = (struct quickspi_driver_data *)id->driver_data; + + init_waitqueue_head(&qsdev->reset_ack_wq); + init_waitqueue_head(&qsdev->nondma_int_received_wq); + init_waitqueue_head(&qsdev->report_desc_got_wq); + init_waitqueue_head(&qsdev->get_report_cmpl_wq); + init_waitqueue_head(&qsdev->set_report_cmpl_wq); + + /* thc hw init */ + qsdev->thc_hw = thc_dev_init(qsdev->dev, qsdev->mem_addr); + if (IS_ERR(qsdev->thc_hw)) { + ret = PTR_ERR(qsdev->thc_hw); + dev_err(dev, "Failed to initialize THC device context, ret = %d.\n", ret); + return ERR_PTR(ret); + } + + ret = thc_interrupt_quiesce(qsdev->thc_hw, true); + if (ret) + return ERR_PTR(ret); + + ret = thc_port_select(qsdev->thc_hw, THC_PORT_TYPE_SPI); + if (ret) { + dev_err(dev, "Failed to select THC port, ret = %d.\n", ret); + return ERR_PTR(ret); + } + + ret = quickspi_get_acpi_resources(qsdev); + if (ret) { + dev_err(dev, "Get ACPI resources failed, ret = %d\n", ret); + return ERR_PTR(ret); + } + + /* THC config for input/output address */ + thc_spi_input_output_address_config(qsdev->thc_hw, + qsdev->input_report_hdr_addr, + qsdev->input_report_bdy_addr, + qsdev->output_report_addr); + + /* THC config for spi read operation */ + ret = thc_spi_read_config(qsdev->thc_hw, qsdev->spi_freq_val, + qsdev->spi_read_io_mode, + qsdev->spi_read_opcode, + qsdev->spi_packet_size); + if (ret) { + dev_err(dev, "thc_spi_read_config failed, ret = %d\n", ret); + return ERR_PTR(ret); + } + + /* THC config for spi write operation */ + ret = thc_spi_write_config(qsdev->thc_hw, qsdev->spi_freq_val, + qsdev->spi_write_io_mode, + qsdev->spi_write_opcode, + qsdev->spi_packet_size, + qsdev->performance_limit); + if (ret) { + dev_err(dev, "thc_spi_write_config failed, ret = %d\n", ret); + return ERR_PTR(ret); + } + + thc_ltr_config(qsdev->thc_hw, + qsdev->active_ltr_val, + qsdev->low_power_ltr_val); + + thc_interrupt_config(qsdev->thc_hw); + + thc_interrupt_enable(qsdev->thc_hw, true); + + qsdev->state = QUICKSPI_INITED; + + return qsdev; +} + +/** + * quickspi_dev_deinit - De-initialize quickspi device + * + * @qsdev: pointer to the quickspi device structure + * + * Disable THC interrupt and deinitilize THC. + */ +static void quickspi_dev_deinit(struct quickspi_device *qsdev) +{ + thc_interrupt_enable(qsdev->thc_hw, false); + thc_ltr_unconfig(qsdev->thc_hw); + + qsdev->state = QUICKSPI_DISABLED; +} + +/** + * quickspi_dma_init - Configure THC DMA for quickspi device + * @qsdev: pointer to the quickspi device structure + * + * This function uses TIC's parameters(such as max input length, max output + * length) to allocate THC DMA buffers and configure THC DMA engines. + * + * Return: 0 if successful or error code on failed. + */ +static int quickspi_dma_init(struct quickspi_device *qsdev) +{ + int ret; + + ret = thc_dma_set_max_packet_sizes(qsdev->thc_hw, 0, + le16_to_cpu(qsdev->dev_desc.max_input_len), + le16_to_cpu(qsdev->dev_desc.max_output_len), + 0); + if (ret) + return ret; + + ret = thc_dma_allocate(qsdev->thc_hw); + if (ret) { + dev_err(qsdev->dev, "Allocate THC DMA buffer failed, ret = %d\n", ret); + return ret; + } + + /* Enable RxDMA */ + ret = thc_dma_configure(qsdev->thc_hw); + if (ret) { + dev_err(qsdev->dev, "Configure THC DMA failed, ret = %d\n", ret); + thc_dma_unconfigure(qsdev->thc_hw); + thc_dma_release(qsdev->thc_hw); + return ret; + } + + return ret; +} + +/** + * quickspi_dma_deinit - Release THC DMA for quickspi device + * @qsdev: pointer to the quickspi device structure + * + * Stop THC DMA engines and release all DMA buffers. + * + */ +static void quickspi_dma_deinit(struct quickspi_device *qsdev) +{ + thc_dma_unconfigure(qsdev->thc_hw); + thc_dma_release(qsdev->thc_hw); +} + +/** + * quickspi_alloc_report_buf - Alloc report buffers + * @qsdev: pointer to the quickspi device structure + * + * Allocate report descriptor buffer, it will be used for restore TIC HID + * report descriptor. + * + * Allocate input report buffer, it will be used for receive HID input report + * data from TIC. + * + * Allocate output report buffer, it will be used for store HID output report, + * such as set feature. + * + * Return: 0 if successful or error code on failed. + */ +static int quickspi_alloc_report_buf(struct quickspi_device *qsdev) +{ + size_t max_report_len; + size_t max_input_len; + + qsdev->report_descriptor = devm_kzalloc(qsdev->dev, + le16_to_cpu(qsdev->dev_desc.rep_desc_len), + GFP_KERNEL); + if (!qsdev->report_descriptor) + return -ENOMEM; + + max_input_len = max(le16_to_cpu(qsdev->dev_desc.rep_desc_len), + le16_to_cpu(qsdev->dev_desc.max_input_len)); + + qsdev->input_buf = devm_kzalloc(qsdev->dev, max_input_len, GFP_KERNEL); + if (!qsdev->input_buf) + return -ENOMEM; + + max_report_len = max(le16_to_cpu(qsdev->dev_desc.max_output_len), + le16_to_cpu(qsdev->dev_desc.max_input_len)); + + qsdev->report_buf = devm_kzalloc(qsdev->dev, max_report_len, GFP_KERNEL); + if (!qsdev->report_buf) + return -ENOMEM; + + return 0; +} + +/* + * quickspi_probe: Quickspi driver probe function + * + * @pdev: point to pci device + * @id: point to pci_device_id structure + * + * This function initializes THC and HIDSPI device, the flow is: + * - do THC pci device initialization + * - query HIDSPI ACPI parameters + * - configure THC to HIDSPI mode + * - go through HIDSPI enumeration flow + * |- reset HIDSPI device + * |- read device descriptor + * - enable THC interrupt and DMA + * - read report descriptor + * - register HID device + * - enable runtime power management + * + * Return 0 if success or error code on failure. + */ +static int quickspi_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct quickspi_device *qsdev; + void __iomem *mem_addr; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "Failed to enable PCI device, ret = %d.\n", ret); + return ret; + } + + pci_set_master(pdev); + + ret = pcim_iomap_regions(pdev, BIT(0), KBUILD_MODNAME); + if (ret) { + dev_err(&pdev->dev, "Failed to get PCI regions, ret = %d.\n", ret); + goto disable_pci_device; + } + + mem_addr = pcim_iomap_table(pdev)[0]; + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (ret) { + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "No usable DMA configuration %d\n", ret); + goto unmap_io_region; + } + } + + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); + if (ret < 0) { + dev_err(&pdev->dev, + "Failed to allocate IRQ vectors. ret = %d\n", ret); + goto unmap_io_region; + } + + pdev->irq = pci_irq_vector(pdev, 0); + + qsdev = quickspi_dev_init(pdev, mem_addr, id); + if (IS_ERR(qsdev)) { + dev_err(&pdev->dev, "QuickSPI device init failed\n"); + ret = PTR_ERR(qsdev); + goto unmap_io_region; + } + + pci_set_drvdata(pdev, qsdev); + + ret = devm_request_threaded_irq(&pdev->dev, pdev->irq, + quickspi_irq_quick_handler, + quickspi_irq_thread_handler, + IRQF_ONESHOT, KBUILD_MODNAME, + qsdev); + if (ret) { + dev_err(&pdev->dev, + "Failed to request threaded IRQ, irq = %d.\n", pdev->irq); + goto dev_deinit; + } + + ret = reset_tic(qsdev); + if (ret) { + dev_err(&pdev->dev, "Reset Touch Device failed, ret = %d\n", ret); + goto dev_deinit; + } + + ret = quickspi_alloc_report_buf(qsdev); + if (ret) { + dev_err(&pdev->dev, "Alloc report buffers failed, ret= %d\n", ret); + goto dev_deinit; + } + + ret = quickspi_dma_init(qsdev); + if (ret) { + dev_err(&pdev->dev, "Setup THC DMA failed, ret= %d\n", ret); + goto dev_deinit; + } + + ret = quickspi_get_report_descriptor(qsdev); + if (ret) { + dev_err(&pdev->dev, "Get report descriptor failed, ret = %d\n", ret); + goto dma_deinit; + } + + ret = quickspi_hid_probe(qsdev); + if (ret) { + dev_err(&pdev->dev, "Failed to register HID device, ret = %d\n", ret); + goto dma_deinit; + } + + qsdev->state = QUICKSPI_ENABLED; + + /* Enable runtime power management */ + pm_runtime_use_autosuspend(qsdev->dev); + pm_runtime_set_autosuspend_delay(qsdev->dev, DEFAULT_AUTO_SUSPEND_DELAY_MS); + pm_runtime_mark_last_busy(qsdev->dev); + pm_runtime_put_noidle(qsdev->dev); + pm_runtime_put_autosuspend(qsdev->dev); + + dev_dbg(&pdev->dev, "QuickSPI probe success\n"); + + return 0; + +dma_deinit: + quickspi_dma_deinit(qsdev); +dev_deinit: + quickspi_dev_deinit(qsdev); +unmap_io_region: + pcim_iounmap_regions(pdev, BIT(0)); +disable_pci_device: + pci_clear_master(pdev); + + return ret; +} + +/** + * quickspi_remove - Device Removal Routine + * + * @pdev: PCI device structure + * + * This is called by the PCI subsystem to alert the driver + * that it should release a PCI device. + */ +static void quickspi_remove(struct pci_dev *pdev) +{ + struct quickspi_device *qsdev; + + qsdev = pci_get_drvdata(pdev); + if (!qsdev) + return; + + quickspi_hid_remove(qsdev); + quickspi_dma_deinit(qsdev); + + pm_runtime_get_noresume(qsdev->dev); + + quickspi_dev_deinit(qsdev); + + pcim_iounmap_regions(pdev, BIT(0)); + pci_clear_master(pdev); +} + +/** + * quickspi_shutdown - Device Shutdown Routine + * + * @pdev: PCI device structure + * + * This is called from the reboot notifier + * it's a simplified version of remove so we go down + * faster. + */ +static void quickspi_shutdown(struct pci_dev *pdev) +{ + struct quickspi_device *qsdev; + + qsdev = pci_get_drvdata(pdev); + if (!qsdev) + return; + + /* Must stop DMA before reboot to avoid DMA entering into unknown state */ + quickspi_dma_deinit(qsdev); + + quickspi_dev_deinit(qsdev); +} + +static int quickspi_suspend(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quickspi_device *qsdev; + int ret; + + qsdev = pci_get_drvdata(pdev); + if (!qsdev) + return -ENODEV; + + ret = quickspi_set_power(qsdev, HIDSPI_SLEEP); + if (ret) + return ret; + + ret = thc_interrupt_quiesce(qsdev->thc_hw, true); + if (ret) + return ret; + + thc_interrupt_enable(qsdev->thc_hw, false); + + thc_dma_unconfigure(qsdev->thc_hw); + + return 0; +} + +static int quickspi_resume(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quickspi_device *qsdev; + int ret; + + qsdev = pci_get_drvdata(pdev); + if (!qsdev) + return -ENODEV; + + ret = thc_port_select(qsdev->thc_hw, THC_PORT_TYPE_SPI); + if (ret) + return ret; + + thc_interrupt_config(qsdev->thc_hw); + + thc_interrupt_enable(qsdev->thc_hw, true); + + ret = thc_dma_configure(qsdev->thc_hw); + if (ret) + return ret; + + ret = thc_interrupt_quiesce(qsdev->thc_hw, false); + if (ret) + return ret; + + ret = quickspi_set_power(qsdev, HIDSPI_ON); + if (ret) + return ret; + + return 0; +} + +static int quickspi_freeze(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quickspi_device *qsdev; + int ret; + + qsdev = pci_get_drvdata(pdev); + if (!qsdev) + return -ENODEV; + + ret = thc_interrupt_quiesce(qsdev->thc_hw, true); + if (ret) + return ret; + + thc_interrupt_enable(qsdev->thc_hw, false); + + thc_dma_unconfigure(qsdev->thc_hw); + + return 0; +} + +static int quickspi_thaw(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quickspi_device *qsdev; + int ret; + + qsdev = pci_get_drvdata(pdev); + if (!qsdev) + return -ENODEV; + + ret = thc_dma_configure(qsdev->thc_hw); + if (ret) + return ret; + + thc_interrupt_enable(qsdev->thc_hw, true); + + ret = thc_interrupt_quiesce(qsdev->thc_hw, false); + if (ret) + return ret; + + return 0; +} + +static int quickspi_poweroff(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quickspi_device *qsdev; + int ret; + + qsdev = pci_get_drvdata(pdev); + if (!qsdev) + return -ENODEV; + + ret = thc_interrupt_quiesce(qsdev->thc_hw, true); + if (ret) + return ret; + + thc_interrupt_enable(qsdev->thc_hw, false); + + thc_ltr_unconfig(qsdev->thc_hw); + + quickspi_dma_deinit(qsdev); + + return 0; +} + +static int quickspi_restore(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quickspi_device *qsdev; + int ret; + + qsdev = pci_get_drvdata(pdev); + if (!qsdev) + return -ENODEV; + + ret = thc_interrupt_quiesce(qsdev->thc_hw, true); + if (ret) + return ret; + + /* Reconfig THC HW when back from hibernate */ + ret = thc_port_select(qsdev->thc_hw, THC_PORT_TYPE_SPI); + if (ret) + return ret; + + thc_spi_input_output_address_config(qsdev->thc_hw, + qsdev->input_report_hdr_addr, + qsdev->input_report_bdy_addr, + qsdev->output_report_addr); + + ret = thc_spi_read_config(qsdev->thc_hw, qsdev->spi_freq_val, + qsdev->spi_read_io_mode, + qsdev->spi_read_opcode, + qsdev->spi_packet_size); + if (ret) + return ret; + + ret = thc_spi_write_config(qsdev->thc_hw, qsdev->spi_freq_val, + qsdev->spi_write_io_mode, + qsdev->spi_write_opcode, + qsdev->spi_packet_size, + qsdev->performance_limit); + if (ret) + return ret; + + thc_interrupt_config(qsdev->thc_hw); + + thc_interrupt_enable(qsdev->thc_hw, true); + + /* TIC may lose power, needs go through reset flow */ + ret = reset_tic(qsdev); + if (ret) + return ret; + + ret = thc_dma_configure(qsdev->thc_hw); + if (ret) + return ret; + + thc_ltr_config(qsdev->thc_hw, + qsdev->active_ltr_val, + qsdev->low_power_ltr_val); + + thc_change_ltr_mode(qsdev->thc_hw, THC_LTR_MODE_ACTIVE); + + return 0; +} + +static int quickspi_runtime_suspend(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quickspi_device *qsdev; + + qsdev = pci_get_drvdata(pdev); + if (!qsdev) + return -ENODEV; + + thc_change_ltr_mode(qsdev->thc_hw, THC_LTR_MODE_LP); + + pci_save_state(pdev); + + return 0; +} + +static int quickspi_runtime_resume(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct quickspi_device *qsdev; + + qsdev = pci_get_drvdata(pdev); + if (!qsdev) + return -ENODEV; + + thc_change_ltr_mode(qsdev->thc_hw, THC_LTR_MODE_ACTIVE); + + return 0; +} + +static const struct dev_pm_ops quickspi_pm_ops = { + .suspend = quickspi_suspend, + .resume = quickspi_resume, + .freeze = quickspi_freeze, + .thaw = quickspi_thaw, + .poweroff = quickspi_poweroff, + .restore = quickspi_restore, + .runtime_suspend = quickspi_runtime_suspend, + .runtime_resume = quickspi_runtime_resume, + .runtime_idle = NULL, +}; + +static const struct pci_device_id quickspi_pci_tbl[] = { + {PCI_DEVICE_DATA(INTEL, THC_MTL_DEVICE_ID_SPI_PORT1, &mtl), }, + {PCI_DEVICE_DATA(INTEL, THC_MTL_DEVICE_ID_SPI_PORT2, &mtl), }, + {PCI_DEVICE_DATA(INTEL, THC_LNL_DEVICE_ID_SPI_PORT1, &lnl), }, + {PCI_DEVICE_DATA(INTEL, THC_LNL_DEVICE_ID_SPI_PORT2, &lnl), }, + {PCI_DEVICE_DATA(INTEL, THC_PTL_H_DEVICE_ID_SPI_PORT1, &ptl), }, + {PCI_DEVICE_DATA(INTEL, THC_PTL_H_DEVICE_ID_SPI_PORT2, &ptl), }, + {PCI_DEVICE_DATA(INTEL, THC_PTL_U_DEVICE_ID_SPI_PORT1, &ptl), }, + {PCI_DEVICE_DATA(INTEL, THC_PTL_U_DEVICE_ID_SPI_PORT2, &ptl), }, + {} +}; +MODULE_DEVICE_TABLE(pci, quickspi_pci_tbl); + +static struct pci_driver quickspi_driver = { + .name = KBUILD_MODNAME, + .id_table = quickspi_pci_tbl, + .probe = quickspi_probe, + .remove = quickspi_remove, + .shutdown = quickspi_shutdown, + .driver.pm = &quickspi_pm_ops, + .driver.probe_type = PROBE_PREFER_ASYNCHRONOUS, +}; + +module_pci_driver(quickspi_driver); + +MODULE_AUTHOR("Xinpeng Sun <xinpeng.sun@intel.com>"); +MODULE_AUTHOR("Even Xu <even.xu@intel.com>"); + +MODULE_DESCRIPTION("Intel(R) QuickSPI Driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("INTEL_THC"); diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h new file mode 100644 index 000000000000..75179bb26767 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-dev.h @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#ifndef _QUICKSPI_DEV_H_ +#define _QUICKSPI_DEV_H_ + +#include <linux/bits.h> +#include <linux/hid-over-spi.h> +#include <linux/sizes.h> +#include <linux/wait.h> + +#include "quickspi-protocol.h" + +#define PCI_DEVICE_ID_INTEL_THC_MTL_DEVICE_ID_SPI_PORT1 0x7E49 +#define PCI_DEVICE_ID_INTEL_THC_MTL_DEVICE_ID_SPI_PORT2 0x7E4B +#define PCI_DEVICE_ID_INTEL_THC_LNL_DEVICE_ID_SPI_PORT1 0xA849 +#define PCI_DEVICE_ID_INTEL_THC_LNL_DEVICE_ID_SPI_PORT2 0xA84B +#define PCI_DEVICE_ID_INTEL_THC_PTL_H_DEVICE_ID_SPI_PORT1 0xE349 +#define PCI_DEVICE_ID_INTEL_THC_PTL_H_DEVICE_ID_SPI_PORT2 0xE34B +#define PCI_DEVICE_ID_INTEL_THC_PTL_U_DEVICE_ID_SPI_PORT1 0xE449 +#define PCI_DEVICE_ID_INTEL_THC_PTL_U_DEVICE_ID_SPI_PORT2 0xE44B + +/* HIDSPI special ACPI parameters DSM methods */ +#define ACPI_QUICKSPI_REVISION_NUM 2 +#define ACPI_QUICKSPI_FUNC_NUM_INPUT_REP_HDR_ADDR 1 +#define ACPI_QUICKSPI_FUNC_NUM_INPUT_REP_BDY_ADDR 2 +#define ACPI_QUICKSPI_FUNC_NUM_OUTPUT_REP_ADDR 3 +#define ACPI_QUICKSPI_FUNC_NUM_READ_OPCODE 4 +#define ACPI_QUICKSPI_FUNC_NUM_WRITE_OPCODE 5 +#define ACPI_QUICKSPI_FUNC_NUM_IO_MODE 6 + +/* QickSPI device special ACPI parameters DSM methods */ +#define ACPI_QUICKSPI_FUNC_NUM_CONNECTION_SPEED 1 +#define ACPI_QUICKSPI_FUNC_NUM_LIMIT_PACKET_SIZE 2 +#define ACPI_QUICKSPI_FUNC_NUM_PERFORMANCE_LIMIT 3 + +/* Platform special ACPI parameters DSM methods */ +#define ACPI_QUICKSPI_FUNC_NUM_ACTIVE_LTR 1 +#define ACPI_QUICKSPI_FUNC_NUM_LP_LTR 2 + +#define SPI_WRITE_IO_MODE BIT(13) +#define SPI_IO_MODE_OPCODE GENMASK(15, 14) +#define PERFORMANCE_LIMITATION GENMASK(15, 0) + +/* Packet size value, the unit is 16 bytes */ +#define DEFAULT_MIN_PACKET_SIZE_VALUE 4 +#define MAX_PACKET_SIZE_VALUE_MTL 128 +#define MAX_PACKET_SIZE_VALUE_LNL 256 + +/* + * THC uses runtime auto suspend to dynamically switch between THC active LTR + * and low power LTR to save CPU power. + * Default value is 5000ms, that means if no touch event in this time, THC will + * change to low power LTR mode. + */ +#define DEFAULT_AUTO_SUSPEND_DELAY_MS 5000 + +enum quickspi_dev_state { + QUICKSPI_NONE, + QUICKSPI_RESETING, + QUICKSPI_RESETED, + QUICKSPI_INITED, + QUICKSPI_ENABLED, + QUICKSPI_DISABLED, +}; + +/** + * struct quickspi_driver_data - Driver specific data for quickspi device + * @max_packet_size_value: identify max packet size, unit is 16 bytes + */ +struct quickspi_driver_data { + u32 max_packet_size_value; +}; + +struct device; +struct pci_dev; +struct thc_device; +struct hid_device; +struct acpi_device; + +/** + * struct quickspi_device - THC QuickSpi device struct + * @dev: point to kernel device + * @pdev: point to PCI device + * @thc_hw: point to THC device + * @hid_dev: point to hid device + * @acpi_dev: point to ACPI device + * @driver_data: point to quickspi specific driver data + * @state: THC SPI device state + * @mem_addr: MMIO memory address + * @dev_desc: device descriptor for HIDSPI protocol + * @input_report_hdr_addr: device input report header address + * @input_report_bdy_addr: device input report body address + * @output_report_bdy_addr: device output report address + * @spi_freq_val: device supported max SPI frequnecy, in Hz + * @spi_read_io_mode: device supported SPI read io mode + * @spi_write_io_mode: device supported SPI write io mode + * @spi_read_opcode: device read opcode + * @spi_write_opcode: device write opcode + * @limit_packet_size: 1 - limit read/write packet to 64Bytes + * 0 - device no packet size limiation for read/write + * @performance_limit: delay time, in ms. + * if device has performance limitation, must give a delay + * before write operation after a read operation. + * @active_ltr_val: THC active LTR value + * @low_power_ltr_val: THC low power LTR value + * @report_descriptor: store a copy of device report descriptor + * @input_buf: store a copy of latest input report data + * @report_buf: store a copy of latest input/output report packet from set/get feature + * @report_len: the length of input/output report packet + * @reset_ack_wq: workqueue for waiting reset response from device + * @reset_ack: indicate reset response received or not + * @nondma_int_received_wq: workqueue for waiting THC non-DMA interrupt + * @nondma_int_received: indicate THC non-DMA interrupt received or not + * @report_desc_got_wq: workqueue for waiting device report descriptor + * @report_desc_got: indicate device report descritor received or not + * @set_power_on_wq: workqueue for waiting set power on response from device + * @set_power_on: indicate set power on response received or not + * @get_feature_cmpl_wq: workqueue for waiting get feature response from device + * @get_feature_cmpl: indicate get feature received or not + * @set_feature_cmpl_wq: workqueue for waiting set feature to device + * @set_feature_cmpl: indicate set feature send complete or not + */ +struct quickspi_device { + struct device *dev; + struct pci_dev *pdev; + struct thc_device *thc_hw; + struct hid_device *hid_dev; + struct acpi_device *acpi_dev; + struct quickspi_driver_data *driver_data; + enum quickspi_dev_state state; + + void __iomem *mem_addr; + + struct hidspi_dev_descriptor dev_desc; + u32 input_report_hdr_addr; + u32 input_report_bdy_addr; + u32 output_report_addr; + u32 spi_freq_val; + u32 spi_read_io_mode; + u32 spi_write_io_mode; + u32 spi_read_opcode; + u32 spi_write_opcode; + u32 limit_packet_size; + u32 spi_packet_size; + u32 performance_limit; + + u32 active_ltr_val; + u32 low_power_ltr_val; + + u8 *report_descriptor; + u8 *input_buf; + u8 *report_buf; + u32 report_len; + + wait_queue_head_t reset_ack_wq; + bool reset_ack; + + wait_queue_head_t nondma_int_received_wq; + bool nondma_int_received; + + wait_queue_head_t report_desc_got_wq; + bool report_desc_got; + + wait_queue_head_t get_report_cmpl_wq; + bool get_report_cmpl; + + wait_queue_head_t set_report_cmpl_wq; + bool set_report_cmpl; +}; + +#endif /* _QUICKSPI_DEV_H_ */ diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c new file mode 100644 index 000000000000..ad52e402c28a --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#include <linux/hid.h> +#include <linux/input.h> +#include <linux/pm_runtime.h> + +#include "quickspi-dev.h" +#include "quickspi-hid.h" + +/** + * quickspi_hid_parse() - HID core parse() callback + * + * @hid: HID device instance + * + * This function gets called during call to hid_add_device + * + * Return: 0 on success and non zero on error. + */ +static int quickspi_hid_parse(struct hid_device *hid) +{ + struct quickspi_device *qsdev = hid->driver_data; + + if (qsdev->report_descriptor) + return hid_parse_report(hid, qsdev->report_descriptor, + le16_to_cpu(qsdev->dev_desc.rep_desc_len)); + + dev_err(qsdev->dev, "invalid report descriptor\n"); + return -EINVAL; +} + +static int quickspi_hid_start(struct hid_device *hid) +{ + return 0; +} + +static void quickspi_hid_stop(struct hid_device *hid) +{ +} + +static int quickspi_hid_open(struct hid_device *hid) +{ + return 0; +} + +static void quickspi_hid_close(struct hid_device *hid) +{ +} + +static int quickspi_hid_raw_request(struct hid_device *hid, + unsigned char reportnum, + __u8 *buf, size_t len, + unsigned char rtype, int reqtype) +{ + struct quickspi_device *qsdev = hid->driver_data; + int ret = 0; + + ret = pm_runtime_resume_and_get(qsdev->dev); + if (ret) + return ret; + + switch (reqtype) { + case HID_REQ_GET_REPORT: + ret = quickspi_get_report(qsdev, rtype, reportnum, buf); + break; + case HID_REQ_SET_REPORT: + ret = quickspi_set_report(qsdev, rtype, reportnum, buf, len); + break; + default: + dev_err_once(qsdev->dev, "Not supported request type %d\n", reqtype); + break; + } + + pm_runtime_mark_last_busy(qsdev->dev); + pm_runtime_put_autosuspend(qsdev->dev); + + return ret; +} + +static int quickspi_hid_power(struct hid_device *hid, int lvl) +{ + return 0; +} + +static struct hid_ll_driver quickspi_hid_ll_driver = { + .parse = quickspi_hid_parse, + .start = quickspi_hid_start, + .stop = quickspi_hid_stop, + .open = quickspi_hid_open, + .close = quickspi_hid_close, + .power = quickspi_hid_power, + .raw_request = quickspi_hid_raw_request, +}; + +/** + * quickspi_hid_probe() - Register HID low level driver + * + * @qsdev: point to quickspi device + * + * This function is used to allocate and add HID device. + * + * Return: 0 on success, non zero on error. + */ +int quickspi_hid_probe(struct quickspi_device *qsdev) +{ + struct hid_device *hid; + int ret; + + hid = hid_allocate_device(); + if (IS_ERR(hid)) + return PTR_ERR(hid); + + hid->ll_driver = &quickspi_hid_ll_driver; + hid->bus = BUS_PCI; + hid->dev.parent = qsdev->dev; + hid->driver_data = qsdev; + hid->version = le16_to_cpu(qsdev->dev_desc.version_id); + hid->vendor = le16_to_cpu(qsdev->dev_desc.vendor_id); + hid->product = le16_to_cpu(qsdev->dev_desc.product_id); + snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", "quickspi-hid", + hid->vendor, hid->product); + + ret = hid_add_device(hid); + if (ret) { + hid_destroy_device(hid); + return ret; + } + + qsdev->hid_dev = hid; + + return 0; +} + +/** + * quickspi_hid_remove() - Destroy HID device + * + * @qsdev: point to quickspi device + * + * Return: 0 on success, non zero on error. + */ +void quickspi_hid_remove(struct quickspi_device *qsdev) +{ + hid_destroy_device(qsdev->hid_dev); +} + +/** + * quickspi_hid_send_report() - Send HID input report data to HID core + * + * @qsdev: point to quickspi device + * @data: point to input report data buffer + * @data_len: the length of input report data + * + * Return: 0 on success, non zero on error. + */ +int quickspi_hid_send_report(struct quickspi_device *qsdev, + void *data, size_t data_len) +{ + int ret; + + ret = hid_input_report(qsdev->hid_dev, HID_INPUT_REPORT, data, data_len, 1); + if (ret) + dev_err(qsdev->dev, "Failed to send HID input report, ret = %d.\n", ret); + + return ret; +} diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.h b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.h new file mode 100644 index 000000000000..f640fa876a40 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#ifndef _QUICKSPI_HID_H_ +#define _QUICKSPI_HID_H_ + +struct quickspi_device; + +int quickspi_hid_send_report(struct quickspi_device *qsdev, + void *data, size_t data_size); +int quickspi_hid_probe(struct quickspi_device *qsdev); +void quickspi_hid_remove(struct quickspi_device *qsdev); + +#endif /* _QUICKSPI_HID_H_ */ diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c new file mode 100644 index 000000000000..7373238ceb18 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.c @@ -0,0 +1,414 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright © 2024 Intel Corporation */ + +#include <linux/acpi.h> +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/hid.h> + +#include "intel-thc-dev.h" +#include "intel-thc-dma.h" + +#include "quickspi-dev.h" +#include "quickspi-hid.h" +#include "quickspi-protocol.h" + +/* THC uses HW to accelerate HID over SPI protocol, THC_M_PRT_DEV_INT_CAUSE + * register is used to store message header and body header, below definition + * let driver retrieve needed data filed easier from THC_M_PRT_DEV_INT_CAUSE + * register. + */ +#define HIDSPI_IN_REP_BDY_HDR_REP_TYPE GENMASK(7, 0) + +static int write_cmd_to_txdma(struct quickspi_device *qsdev, + int report_type, int report_id, + u8 *report_buf, const int report_buf_len) +{ + struct output_report *write_buf; + int write_buf_len; + int ret; + + write_buf = (struct output_report *)qsdev->report_buf; + + write_buf->output_hdr.report_type = report_type; + write_buf->output_hdr.content_len = cpu_to_le16(report_buf_len); + write_buf->output_hdr.content_id = report_id; + + if (report_buf && report_buf_len > 0) + memcpy(write_buf->content, report_buf, report_buf_len); + + write_buf_len = HIDSPI_OUTPUT_REPORT_SIZE(report_buf_len); + + ret = thc_dma_write(qsdev->thc_hw, write_buf, write_buf_len); + if (ret) + dev_err_once(qsdev->dev, "DMA write failed, ret = %d\n", ret); + + return ret; +} + +static int quickspi_get_device_descriptor(struct quickspi_device *qsdev) +{ + u8 read_buf[HIDSPI_INPUT_DEVICE_DESCRIPTOR_SIZE]; + struct output_report output_rep; + u32 input_len, read_len = 0; + u32 int_cause_val; + u8 input_rep_type; + int ret; + + output_rep.output_hdr.report_type = DEVICE_DESCRIPTOR; + output_rep.output_hdr.content_len = 0; + output_rep.output_hdr.content_id = 0; + + qsdev->nondma_int_received = false; + + ret = thc_tic_pio_write(qsdev->thc_hw, qsdev->output_report_addr, + HIDSPI_OUTPUT_REPORT_SIZE(0), (u32 *)&output_rep); + if (ret) { + dev_err_once(qsdev->dev, + "Write DEVICE_DESCRIPTOR command failed, ret = %d\n", ret); + return ret; + } + + ret = wait_event_interruptible_timeout(qsdev->nondma_int_received_wq, + qsdev->nondma_int_received, + QUICKSPI_ACK_WAIT_TIMEOUT * HZ); + if (ret <= 0 || !qsdev->nondma_int_received) { + dev_err_once(qsdev->dev, "Wait DEVICE_DESCRIPTOR timeout, ret:%d\n", ret); + return -ETIMEDOUT; + } + qsdev->nondma_int_received = false; + + int_cause_val = thc_int_cause_read(qsdev->thc_hw); + input_len = FIELD_GET(HIDSPI_INPUT_HEADER_REPORT_LEN, int_cause_val); + + input_len = input_len * sizeof(u32); + if (input_len != HIDSPI_INPUT_DEVICE_DESCRIPTOR_SIZE) { + dev_err_once(qsdev->dev, "Receive wrong DEVICE_DESCRIPTOR length, len = %u\n", + input_len); + return -EINVAL; + } + + ret = thc_tic_pio_read(qsdev->thc_hw, qsdev->input_report_bdy_addr, + input_len, &read_len, (u32 *)read_buf); + if (ret || read_len != input_len) { + dev_err_once(qsdev->dev, "Read DEVICE_DESCRIPTOR failed, ret = %d\n", ret); + dev_err_once(qsdev->dev, "DEVICE_DESCRIPTOR expected len = %u, actual read = %u\n", + input_len, read_len); + return ret; + } + + input_rep_type = ((struct input_report_body_header *)read_buf)->input_report_type; + + if (input_rep_type == DEVICE_DESCRIPTOR_RESPONSE) { + memcpy(&qsdev->dev_desc, + read_buf + HIDSPI_INPUT_BODY_HEADER_SIZE, + HIDSPI_DEVICE_DESCRIPTOR_SIZE); + + return 0; + } + + dev_err_once(qsdev->dev, "Unexpected intput report type: %d\n", input_rep_type); + return -EINVAL; +} + +int quickspi_get_report_descriptor(struct quickspi_device *qsdev) +{ + int ret; + + ret = write_cmd_to_txdma(qsdev, REPORT_DESCRIPTOR, 0, NULL, 0); + if (ret) { + dev_err_once(qsdev->dev, + "Write REPORT_DESCRIPTOR command failed, ret = %d\n", ret); + return ret; + } + + ret = wait_event_interruptible_timeout(qsdev->report_desc_got_wq, + qsdev->report_desc_got, + QUICKSPI_ACK_WAIT_TIMEOUT * HZ); + if (ret <= 0 || !qsdev->report_desc_got) { + dev_err_once(qsdev->dev, "Wait Report Descriptor timeout, ret:%d\n", ret); + return -ETIMEDOUT; + } + qsdev->report_desc_got = false; + + return 0; +} + +int quickspi_set_power(struct quickspi_device *qsdev, + enum hidspi_power_state power_state) +{ + u8 cmd_content = power_state; + int ret; + + ret = write_cmd_to_txdma(qsdev, COMMAND_CONTENT, + HIDSPI_SET_POWER_CMD_ID, + &cmd_content, + sizeof(cmd_content)); + if (ret) { + dev_err_once(qsdev->dev, "Write SET_POWER command failed, ret = %d\n", ret); + return ret; + } + + return 0; +} + +void quickspi_handle_input_data(struct quickspi_device *qsdev, u32 buf_len) +{ + struct input_report_body_header *body_hdr; + struct input_report_body *input_body; + u8 *input_report; + u32 input_len; + int ret = 0; + + input_body = (struct input_report_body *)qsdev->input_buf; + body_hdr = &input_body->body_hdr; + input_len = le16_to_cpu(body_hdr->content_len); + + if (HIDSPI_INPUT_BODY_SIZE(input_len) > buf_len) { + dev_err_once(qsdev->dev, "Wrong input report length: %u", + input_len); + return; + } + + switch (body_hdr->input_report_type) { + case REPORT_DESCRIPTOR_RESPONSE: + if (input_len != le16_to_cpu(qsdev->dev_desc.rep_desc_len)) { + dev_err_once(qsdev->dev, "Unexpected report descriptor length: %u\n", + input_len); + return; + } + + memcpy(qsdev->report_descriptor, input_body->content, input_len); + + qsdev->report_desc_got = true; + wake_up_interruptible(&qsdev->report_desc_got_wq); + + break; + + case COMMAND_RESPONSE: + if (body_hdr->content_id == HIDSPI_SET_POWER_CMD_ID) { + dev_dbg(qsdev->dev, "Receive set power on response\n"); + } else { + dev_err_once(qsdev->dev, "Unknown command response type: %u\n", + body_hdr->content_id); + } + + break; + + case RESET_RESPONSE: + if (qsdev->state == QUICKSPI_RESETING) { + qsdev->reset_ack = true; + wake_up_interruptible(&qsdev->reset_ack_wq); + dev_dbg(qsdev->dev, "Receive HIR reset response\n"); + } else { + dev_info(qsdev->dev, "Receive DIR\n"); + } + break; + + case GET_FEATURE_RESPONSE: + case GET_INPUT_REPORT_RESPONSE: + qsdev->report_len = sizeof(body_hdr->content_id) + input_len; + input_report = input_body->content - sizeof(body_hdr->content_id); + + memcpy(qsdev->report_buf, input_report, qsdev->report_len); + + qsdev->get_report_cmpl = true; + wake_up_interruptible(&qsdev->get_report_cmpl_wq); + + break; + + case SET_FEATURE_RESPONSE: + case OUTPUT_REPORT_RESPONSE: + qsdev->set_report_cmpl = true; + wake_up_interruptible(&qsdev->set_report_cmpl_wq); + + break; + + case DATA: + if (qsdev->state != QUICKSPI_ENABLED) + return; + + if (input_len > le16_to_cpu(qsdev->dev_desc.max_input_len)) { + dev_err_once(qsdev->dev, "Unexpected too large input report length: %u\n", + input_len); + return; + } + + input_len = sizeof(body_hdr->content_id) + input_len; + input_report = input_body->content - sizeof(body_hdr->content_id); + + ret = quickspi_hid_send_report(qsdev, input_report, input_len); + if (ret) + dev_err_once(qsdev->dev, "Failed to send HID input report: %d\n", ret); + + break; + + default: + dev_err_once(qsdev->dev, "Unsupported input report type: %u\n", + body_hdr->input_report_type); + break; + } +} + +static int acpi_tic_reset(struct quickspi_device *qsdev) +{ + acpi_status status = 0; + acpi_handle handle; + + if (!qsdev->acpi_dev) + return -ENODEV; + + handle = acpi_device_handle(qsdev->acpi_dev); + status = acpi_execute_simple_method(handle, "_RST", 0); + if (ACPI_FAILURE(status)) { + dev_err_once(qsdev->dev, + "Failed to reset device through ACPI method, ret = %d\n", status); + return -EIO; + } + + return 0; +} + +int reset_tic(struct quickspi_device *qsdev) +{ + u32 actual_read_len, read_len = 0; + u32 input_report_len, reset_response, int_cause_val; + u8 input_rep_type; + int ret; + + qsdev->state = QUICKSPI_RESETING; + + qsdev->reset_ack = false; + + /* First interrupt uses level trigger to avoid missing interrupt */ + thc_int_trigger_type_select(qsdev->thc_hw, false); + + ret = acpi_tic_reset(qsdev); + if (ret) + return ret; + + ret = thc_interrupt_quiesce(qsdev->thc_hw, false); + if (ret) + return ret; + + ret = wait_event_interruptible_timeout(qsdev->reset_ack_wq, + qsdev->reset_ack, + QUICKSPI_ACK_WAIT_TIMEOUT * HZ); + if (ret <= 0 || !qsdev->reset_ack) { + dev_err_once(qsdev->dev, "Wait RESET_RESPONSE timeout, ret:%d\n", ret); + return -ETIMEDOUT; + } + + int_cause_val = thc_int_cause_read(qsdev->thc_hw); + input_report_len = FIELD_GET(HIDSPI_INPUT_HEADER_REPORT_LEN, int_cause_val); + + read_len = input_report_len * sizeof(u32); + if (read_len != HIDSPI_INPUT_BODY_SIZE(0)) { + dev_err_once(qsdev->dev, "Receive wrong RESET_RESPONSE, len = %u\n", + read_len); + return -EINVAL; + } + + /* Switch to edge trigger matching with HIDSPI protocol definition */ + thc_int_trigger_type_select(qsdev->thc_hw, true); + + ret = thc_tic_pio_read(qsdev->thc_hw, qsdev->input_report_bdy_addr, + read_len, &actual_read_len, + (u32 *)&reset_response); + if (ret || actual_read_len != read_len) { + dev_err_once(qsdev->dev, "Read RESET_RESPONSE body failed, ret = %d\n", ret); + dev_err_once(qsdev->dev, "RESET_RESPONSE body expected len = %u, actual = %u\n", + read_len, actual_read_len); + return ret; + } + + input_rep_type = FIELD_GET(HIDSPI_IN_REP_BDY_HDR_REP_TYPE, reset_response); + + if (input_rep_type == RESET_RESPONSE) { + dev_dbg(qsdev->dev, "RESET_RESPONSE received\n"); + } else { + dev_err_once(qsdev->dev, + "Unexpected input report type: %d, expect RESET_RESPONSE\n", + input_rep_type); + return -EINVAL; + } + + qsdev->state = QUICKSPI_RESETED; + + ret = quickspi_get_device_descriptor(qsdev); + if (ret) + return ret; + + return 0; +} + +int quickspi_get_report(struct quickspi_device *qsdev, + u8 report_type, unsigned int report_id, void *buf) +{ + int rep_type; + int ret; + + if (report_type == HID_INPUT_REPORT) { + rep_type = GET_INPUT_REPORT; + } else if (report_type == HID_FEATURE_REPORT) { + rep_type = GET_FEATURE; + } else { + dev_err_once(qsdev->dev, "Unsupported report type for GET REPORT: %d\n", + report_type); + return -EINVAL; + } + + ret = write_cmd_to_txdma(qsdev, rep_type, report_id, NULL, 0); + if (ret) { + dev_err_once(qsdev->dev, "Write GET_REPORT command failed, ret = %d\n", ret); + return ret; + } + + ret = wait_event_interruptible_timeout(qsdev->get_report_cmpl_wq, + qsdev->get_report_cmpl, + QUICKSPI_ACK_WAIT_TIMEOUT * HZ); + if (ret <= 0 || !qsdev->get_report_cmpl) { + dev_err_once(qsdev->dev, "Wait Get Report Response timeout, ret:%d\n", ret); + return -ETIMEDOUT; + } + qsdev->get_report_cmpl = false; + + memcpy(buf, qsdev->report_buf, qsdev->report_len); + + return qsdev->report_len; +} + +int quickspi_set_report(struct quickspi_device *qsdev, + u8 report_type, unsigned int report_id, + void *buf, u32 buf_len) +{ + int rep_type; + int ret; + + if (report_type == HID_OUTPUT_REPORT) { + rep_type = OUTPUT_REPORT; + } else if (report_type == HID_FEATURE_REPORT) { + rep_type = SET_FEATURE; + } else { + dev_err_once(qsdev->dev, "Unsupported report type for SET REPORT: %d\n", + report_type); + return -EINVAL; + } + + ret = write_cmd_to_txdma(qsdev, rep_type, report_id, buf + 1, buf_len - 1); + if (ret) { + dev_err_once(qsdev->dev, "Write SET_REPORT command failed, ret = %d\n", ret); + return ret; + } + + ret = wait_event_interruptible_timeout(qsdev->set_report_cmpl_wq, + qsdev->set_report_cmpl, + QUICKSPI_ACK_WAIT_TIMEOUT * HZ); + if (ret <= 0 || !qsdev->set_report_cmpl) { + dev_err_once(qsdev->dev, "Wait Set Report Response timeout, ret:%d\n", ret); + return -ETIMEDOUT; + } + qsdev->set_report_cmpl = false; + + return buf_len; +} diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h new file mode 100644 index 000000000000..775e29c1ed13 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-protocol.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#ifndef _QUICKSPI_PROTOCOL_H_ +#define _QUICKSPI_PROTOCOL_H_ + +#include <linux/hid-over-spi.h> + +#define QUICKSPI_ACK_WAIT_TIMEOUT 5 + +struct quickspi_device; + +void quickspi_handle_input_data(struct quickspi_device *qsdev, u32 buf_len); +int quickspi_get_report(struct quickspi_device *qsdev, u8 report_type, + unsigned int report_id, void *buf); +int quickspi_set_report(struct quickspi_device *qsdev, u8 report_type, + unsigned int report_id, void *buf, u32 buf_len); +int quickspi_get_report_descriptor(struct quickspi_device *qsdev); + +int quickspi_set_power(struct quickspi_device *qsdev, + enum hidspi_power_state power_state); + +int reset_tic(struct quickspi_device *qsdev); + +#endif /* _QUICKSPI_PROTOCOL_H_ */ diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c new file mode 100644 index 000000000000..4fc78b5a04b5 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c @@ -0,0 +1,1578 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#include <linux/bitfield.h> +#include <linux/regmap.h> + +#include "intel-thc-dev.h" +#include "intel-thc-hw.h" + +static int thc_regmap_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct thc_device *thc_ctx = context; + void __iomem *base = thc_ctx->mmio_addr; + + *val = ioread32(base + reg); + return 0; +} + +static int thc_regmap_write(void *context, unsigned int reg, + unsigned int val) +{ + struct thc_device *thc_ctx = context; + void __iomem *base = thc_ctx->mmio_addr; + + iowrite32(val, base + reg); + return 0; +} + +static const struct regmap_range thc_rw_ranges[] = { + regmap_reg_range(0x10, 0x14), + regmap_reg_range(0x1000, 0x1320), +}; + +static const struct regmap_access_table thc_rw_table = { + .yes_ranges = thc_rw_ranges, + .n_yes_ranges = ARRAY_SIZE(thc_rw_ranges), +}; + +static const struct regmap_config thc_regmap_cfg = { + .name = "thc_regmap_common", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x1320, + .reg_read = thc_regmap_read, + .reg_write = thc_regmap_write, + .cache_type = REGCACHE_NONE, + .fast_io = true, + .rd_table = &thc_rw_table, + .wr_table = &thc_rw_table, + .volatile_table = &thc_rw_table, +}; + +/** + * thc_clear_state - Clear THC hardware state + * + * @dev: The pointer of THC device structure + */ +static void thc_clear_state(const struct thc_device *dev) +{ + u32 val; + + /* Clear interrupt cause register */ + val = THC_M_PRT_ERR_CAUSE_INVLD_DEV_ENTRY | + THC_M_PRT_ERR_CAUSE_FRAME_BABBLE_ERR | + THC_M_PRT_ERR_CAUSE_BUF_OVRRUN_ERR | + THC_M_PRT_ERR_CAUSE_PRD_ENTRY_ERR; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_ERR_CAUSE_OFFSET, val, val); + + /* Clear interrupt error state */ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_1_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_IE_STALL, + THC_M_PRT_READ_DMA_CNTRL_IE_STALL); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_2_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_IE_STALL, + THC_M_PRT_READ_DMA_CNTRL_IE_STALL); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_TXN_ERR_INT_STS, + THC_M_PRT_INT_STATUS_TXN_ERR_INT_STS); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_FATAL_ERR_INT_STS, + THC_M_PRT_INT_STATUS_FATAL_ERR_INT_STS); + + val = THC_M_PRT_INT_EN_TXN_ERR_INT_EN | + THC_M_PRT_INT_EN_FATAL_ERR_INT_EN | + THC_M_PRT_INT_EN_BUF_OVRRUN_ERR_INT_EN; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_INT_EN_OFFSET, val, val); + + val = THC_M_PRT_SW_SEQ_STS_THC_SS_ERR | + THC_M_PRT_SW_SEQ_STS_TSSDONE; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, val, val); + + /* Clear RxDMA state */ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_1_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_IE_EOF, 0); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_2_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_IE_EOF, 0); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_INT_STS_1_OFFSET, + THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS, + THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_INT_STS_2_OFFSET, + THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS, + THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_INT_STS_1_OFFSET, + THC_M_PRT_READ_DMA_INT_STS_NONDMA_INT_STS, + THC_M_PRT_READ_DMA_INT_STS_NONDMA_INT_STS); + + /* Clear TxDMA state */ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_WRITE_DMA_CNTRL_OFFSET, + THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC_DMACPL, + THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC_DMACPL); + + val = THC_M_PRT_WRITE_INT_STS_THC_WRDMA_ERROR_STS | + THC_M_PRT_WRITE_INT_STS_THC_WRDMA_IOC_STS | + THC_M_PRT_WRITE_INT_STS_THC_WRDMA_CMPL_STATUS; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_WRITE_INT_STS_OFFSET, val, val); + + /* Reset all DMAs count */ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_DB_CNT_1_OFFSET, + THC_M_PRT_DB_CNT_1_THC_M_PRT_DB_CNT_RST, + THC_M_PRT_DB_CNT_1_THC_M_PRT_DB_CNT_RST); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_DEVINT_CNT_OFFSET, + THC_M_PRT_DEVINT_CNT_THC_M_PRT_DEVINT_CNT_RST, + THC_M_PRT_DEVINT_CNT_THC_M_PRT_DEVINT_CNT_RST); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_1_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_TPCPR, + THC_M_PRT_READ_DMA_CNTRL_TPCPR); + + /* Reset THC hardware sequence state */ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_FRAME_DROP_CNT_1_OFFSET, + THC_M_PRT_FRAME_DROP_CNT_1_RFDC, + THC_M_PRT_FRAME_DROP_CNT_1_RFDC); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_FRAME_DROP_CNT_2_OFFSET, + THC_M_PRT_FRAME_DROP_CNT_2_RFDC, + THC_M_PRT_FRAME_DROP_CNT_2_RFDC); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_FRM_CNT_1_OFFSET, + THC_M_PRT_FRM_CNT_1_THC_M_PRT_FRM_CNT_RST, + THC_M_PRT_FRM_CNT_1_THC_M_PRT_FRM_CNT_RST); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_FRM_CNT_2_OFFSET, + THC_M_PRT_FRM_CNT_2_THC_M_PRT_FRM_CNT_RST, + THC_M_PRT_FRM_CNT_2_THC_M_PRT_FRM_CNT_RST); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_RXDMA_PKT_CNT_1_OFFSET, + THC_M_PRT_RXDMA_PKT_CNT_1_THC_M_PRT_RXDMA_PKT_CNT_RST, + THC_M_PRT_RXDMA_PKT_CNT_1_THC_M_PRT_RXDMA_PKT_CNT_RST); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_RXDMA_PKT_CNT_2_OFFSET, + THC_M_PRT_RXDMA_PKT_CNT_2_THC_M_PRT_RXDMA_PKT_CNT_RST, + THC_M_PRT_RXDMA_PKT_CNT_2_THC_M_PRT_RXDMA_PKT_CNT_RST); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SWINT_CNT_1_OFFSET, + THC_M_PRT_SWINT_CNT_1_THC_M_PRT_SWINT_CNT_RST, + THC_M_PRT_SWINT_CNT_1_THC_M_PRT_SWINT_CNT_RST); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SWINT_CNT_1_OFFSET, + THC_M_PRT_SWINT_CNT_1_THC_M_PRT_SWINT_CNT_RST, + THC_M_PRT_SWINT_CNT_1_THC_M_PRT_SWINT_CNT_RST); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_TX_FRM_CNT_OFFSET, + THC_M_PRT_TX_FRM_CNT_THC_M_PRT_TX_FRM_CNT_RST, + THC_M_PRT_TX_FRM_CNT_THC_M_PRT_TX_FRM_CNT_RST); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_TXDMA_PKT_CNT_OFFSET, + THC_M_PRT_TXDMA_PKT_CNT_THC_M_PRT_TXDMA_PKT_CNT_RST, + THC_M_PRT_TXDMA_PKT_CNT_THC_M_PRT_TXDMA_PKT_CNT_RST); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_UFRM_CNT_1_OFFSET, + THC_M_PRT_UFRM_CNT_1_THC_M_PRT_UFRM_CNT_RST, + THC_M_PRT_UFRM_CNT_1_THC_M_PRT_UFRM_CNT_RST); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_UFRM_CNT_2_OFFSET, + THC_M_PRT_UFRM_CNT_2_THC_M_PRT_UFRM_CNT_RST, + THC_M_PRT_UFRM_CNT_2_THC_M_PRT_UFRM_CNT_RST); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_PRD_EMPTY_CNT_1_OFFSET, + THC_M_PRT_PRD_EMPTY_CNT_1_RPTEC, + THC_M_PRT_PRD_EMPTY_CNT_1_RPTEC); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_PRD_EMPTY_CNT_2_OFFSET, + THC_M_PRT_PRD_EMPTY_CNT_2_RPTEC, + THC_M_PRT_PRD_EMPTY_CNT_2_RPTEC); +} + +/** + * thc_dev_init - Allocate and initialize the THC device structure + * + * @device: The pointer of device structure + * @mem_addr: The pointer of MMIO memory address + * + * Return: The thc_device pointer on success, NULL on failed. + */ +struct thc_device *thc_dev_init(struct device *device, void __iomem *mem_addr) +{ + struct thc_device *thc_dev; + int ret; + + thc_dev = devm_kzalloc(device, sizeof(*thc_dev), GFP_KERNEL); + if (!thc_dev) + return ERR_PTR(-ENOMEM); + + thc_dev->dev = device; + thc_dev->mmio_addr = mem_addr; + thc_dev->thc_regmap = devm_regmap_init(device, NULL, thc_dev, &thc_regmap_cfg); + if (IS_ERR(thc_dev->thc_regmap)) { + ret = PTR_ERR(thc_dev->thc_regmap); + dev_err_once(device, "Failed to init thc_regmap: %d\n", ret); + return ERR_PTR(ret); + } + + thc_clear_state(thc_dev); + + mutex_init(&thc_dev->thc_bus_lock); + init_waitqueue_head(&thc_dev->write_complete_wait); + init_waitqueue_head(&thc_dev->swdma_complete_wait); + + thc_dev->dma_ctx = thc_dma_init(thc_dev); + if (!thc_dev->dma_ctx) { + dev_err_once(device, "DMA context init failed\n"); + return ERR_PTR(-ENOMEM); + } + + return thc_dev; +} +EXPORT_SYMBOL_NS_GPL(thc_dev_init, "INTEL_THC"); + +static int prepare_pio(const struct thc_device *dev, const u8 pio_op, + const u32 address, const u32 size) +{ + u32 sts, ctrl, addr, mask; + + regmap_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, &sts); + + /* Check if THC previous PIO still in progress */ + if (sts & THC_M_PRT_SW_SEQ_STS_THC_SS_CIP) { + dev_err_once(dev->dev, "THC PIO is still busy!\n"); + return -EBUSY; + } + + /* Clear error bit and complete bit in state register */ + sts |= THC_M_PRT_SW_SEQ_STS_THC_SS_ERR | + THC_M_PRT_SW_SEQ_STS_TSSDONE; + regmap_write(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, sts); + + /* Set PIO data size, opcode and interrupt capability */ + ctrl = FIELD_PREP(THC_M_PRT_SW_SEQ_CNTRL_THC_SS_BC, size) | + FIELD_PREP(THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CMD, pio_op); + if (dev->pio_int_supported) + ctrl |= THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CD_IE; + + mask = THC_M_PRT_SW_SEQ_CNTRL_THC_SS_BC | + THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CMD | + THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CD_IE; + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_SW_SEQ_CNTRL_OFFSET, mask, ctrl); + + /* Set PIO target address */ + addr = FIELD_PREP(THC_M_PRT_SW_SEQ_DATA0_ADDR_THC_SW_SEQ_DATA0_ADDR, address); + mask = THC_M_PRT_SW_SEQ_DATA0_ADDR_THC_SW_SEQ_DATA0_ADDR; + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_SW_SEQ_DATA0_ADDR_OFFSET, mask, addr); + return 0; +} + +static void pio_start(const struct thc_device *dev, + u32 size_in_bytes, const u32 *buffer) +{ + if (size_in_bytes && buffer) + regmap_bulk_write(dev->thc_regmap, THC_M_PRT_SW_SEQ_DATA1_OFFSET, + buffer, size_in_bytes / sizeof(u32)); + + /* Enable Start bit */ + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_SW_SEQ_CNTRL_OFFSET, + THC_M_PRT_SW_SEQ_CNTRL_TSSGO, + THC_M_PRT_SW_SEQ_CNTRL_TSSGO); +} + +static int pio_complete(const struct thc_device *dev, + u32 *buffer, u32 *size) +{ + u32 sts, ctrl; + + regmap_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, &sts); + if (sts & THC_M_PRT_SW_SEQ_STS_THC_SS_ERR) { + dev_err_once(dev->dev, "PIO operation error\n"); + return -EBUSY; + } + + if (buffer && size) { + regmap_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_CNTRL_OFFSET, &ctrl); + *size = FIELD_GET(THC_M_PRT_SW_SEQ_CNTRL_THC_SS_BC, ctrl); + + regmap_bulk_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_DATA1_OFFSET, + buffer, *size / sizeof(u32)); + } + + sts |= THC_M_PRT_SW_SEQ_STS_THC_SS_ERR | THC_M_PRT_SW_SEQ_STS_TSSDONE; + regmap_write(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, sts); + return 0; +} + +static int pio_wait(const struct thc_device *dev) +{ + u32 sts = 0; + int ret; + + ret = regmap_read_poll_timeout(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, sts, + !(sts & THC_M_PRT_SW_SEQ_STS_THC_SS_CIP || + !(sts & THC_M_PRT_SW_SEQ_STS_TSSDONE)), + THC_REGMAP_POLLING_INTERVAL_US, THC_PIO_DONE_TIMEOUT_US); + if (ret) + dev_err_once(dev->dev, "Timeout while polling PIO operation done\n"); + + return ret; +} + +/** + * thc_tic_pio_read - Read data from touch device by PIO + * + * @dev: The pointer of THC private device context + * @address: Slave address for the PIO operation + * @size: Expected read data size + * @actual_size: The pointer of the actual data size read from touch device + * @buffer: The pointer of data buffer to store the data read from touch device + * + * Return: 0 on success, other error codes on failed. + */ +int thc_tic_pio_read(struct thc_device *dev, const u32 address, + const u32 size, u32 *actual_size, u32 *buffer) +{ + u8 opcode; + int ret; + + if (size <= 0 || !actual_size || !buffer) { + dev_err(dev->dev, "Invalid input parameters, size %u, actual_size %p, buffer %p\n", + size, actual_size, buffer); + return -EINVAL; + } + + if (mutex_lock_interruptible(&dev->thc_bus_lock)) + return -EINTR; + + opcode = (dev->port_type == THC_PORT_TYPE_SPI) ? + THC_PIO_OP_SPI_TIC_READ : THC_PIO_OP_I2C_TIC_READ; + + ret = prepare_pio(dev, opcode, address, size); + if (ret < 0) + goto end; + + pio_start(dev, 0, NULL); + + ret = pio_wait(dev); + if (ret < 0) + goto end; + + ret = pio_complete(dev, buffer, actual_size); + +end: + mutex_unlock(&dev->thc_bus_lock); + return ret; +} +EXPORT_SYMBOL_NS_GPL(thc_tic_pio_read, "INTEL_THC"); + +/** + * thc_tic_pio_write - Write data to touch device by PIO + * + * @dev: The pointer of THC private device context + * @address: Slave address for the PIO operation + * @size: PIO write data size + * @buffer: The pointer of the write data buffer + * + * Return: 0 on success, other error codes on failed. + */ +int thc_tic_pio_write(struct thc_device *dev, const u32 address, + const u32 size, const u32 *buffer) +{ + u8 opcode; + int ret; + + if (size <= 0 || !buffer) { + dev_err(dev->dev, "Invalid input parameters, size %u, buffer %p\n", + size, buffer); + return -EINVAL; + } + + if (mutex_lock_interruptible(&dev->thc_bus_lock)) + return -EINTR; + + opcode = (dev->port_type == THC_PORT_TYPE_SPI) ? + THC_PIO_OP_SPI_TIC_WRITE : THC_PIO_OP_I2C_TIC_WRITE; + + ret = prepare_pio(dev, opcode, address, size); + if (ret < 0) + goto end; + + pio_start(dev, size, buffer); + + ret = pio_wait(dev); + if (ret < 0) + goto end; + + ret = pio_complete(dev, NULL, NULL); + +end: + mutex_unlock(&dev->thc_bus_lock); + return ret; +} +EXPORT_SYMBOL_NS_GPL(thc_tic_pio_write, "INTEL_THC"); + +/** + * thc_tic_pio_write_and_read - Write data followed by read data by PIO + * + * @dev: The pointer of THC private device context + * @address: Slave address for the PIO operation + * @write_size: PIO write data size + * @write_buffer: The pointer of the write data buffer + * @read_size: Expected PIO read data size + * @actual_size: The pointer of the actual read data size + * @read_buffer: The pointer of PIO read data buffer + * + * Return: 0 on success, other error codes on failed. + */ +int thc_tic_pio_write_and_read(struct thc_device *dev, const u32 address, + const u32 write_size, const u32 *write_buffer, + const u32 read_size, u32 *actual_size, u32 *read_buffer) +{ + u32 i2c_ctrl, mask; + int ret; + + if (dev->port_type == THC_PORT_TYPE_SPI) { + dev_err(dev->dev, "SPI port type doesn't support pio write and read!"); + return -EINVAL; + } + + if (mutex_lock_interruptible(&dev->thc_bus_lock)) + return -EINTR; + + /* Config i2c PIO write and read sequence */ + i2c_ctrl = FIELD_PREP(THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_PIO_I2C_WBC, write_size); + mask = THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_PIO_I2C_WBC; + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_OFFSET, + mask, i2c_ctrl); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_OFFSET, + THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_I2C_RW_PIO_EN, + THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_I2C_RW_PIO_EN); + + ret = prepare_pio(dev, THC_PIO_OP_I2C_TIC_WRITE_AND_READ, address, read_size); + if (ret < 0) + goto end; + + pio_start(dev, write_size, write_buffer); + + ret = pio_wait(dev); + if (ret < 0) + goto end; + + ret = pio_complete(dev, read_buffer, actual_size); + +end: + mutex_unlock(&dev->thc_bus_lock); + return ret; +} +EXPORT_SYMBOL_NS_GPL(thc_tic_pio_write_and_read, "INTEL_THC"); + +/** + * thc_interrupt_config - Configure THC interrupts + * + * @dev: The pointer of THC private device context + */ +void thc_interrupt_config(struct thc_device *dev) +{ + u32 mbits, mask, r_dma_ctrl_1; + + /* Clear Error reporting interrupt status bits */ + mbits = THC_M_PRT_INT_STATUS_TXN_ERR_INT_STS | + THC_M_PRT_INT_STATUS_FATAL_ERR_INT_STS; + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_INT_STATUS_OFFSET, + mbits, mbits); + + /* Enable Error Reporting Interrupts */ + mbits = THC_M_PRT_INT_EN_TXN_ERR_INT_EN | + THC_M_PRT_INT_EN_FATAL_ERR_INT_EN | + THC_M_PRT_INT_EN_BUF_OVRRUN_ERR_INT_EN; + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_INT_EN_OFFSET, + mbits, mbits); + + /* Clear PIO Interrupt status bits */ + mbits = THC_M_PRT_SW_SEQ_STS_THC_SS_ERR | + THC_M_PRT_SW_SEQ_STS_TSSDONE; + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_SW_SEQ_STS_OFFSET, + mbits, mbits); + + /* Read Interrupts */ + regmap_read(dev->thc_regmap, + THC_M_PRT_READ_DMA_CNTRL_1_OFFSET, + &r_dma_ctrl_1); + /* Disable RxDMA1 */ + r_dma_ctrl_1 &= ~THC_M_PRT_READ_DMA_CNTRL_IE_EOF; + regmap_write(dev->thc_regmap, + THC_M_PRT_READ_DMA_CNTRL_1_OFFSET, + r_dma_ctrl_1); + + /* Ack EOF Interrupt RxDMA1 */ + mbits = THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS; + /* Ack NonDMA Interrupt */ + mbits |= THC_M_PRT_READ_DMA_INT_STS_NONDMA_INT_STS; + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_READ_DMA_INT_STS_1_OFFSET, + mbits, mbits); + + /* Ack EOF Interrupt RxDMA2 */ + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_READ_DMA_INT_STS_2_OFFSET, + THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS, + THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS); + + /* Write Interrupts */ + /* Disable TxDMA */ + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_WRITE_DMA_CNTRL_OFFSET, + THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC_DMACPL, + 0); + + /* Clear TxDMA interrupt status bits */ + mbits = THC_M_PRT_WRITE_INT_STS_THC_WRDMA_ERROR_STS; + mbits |= THC_M_PRT_WRITE_INT_STS_THC_WRDMA_IOC_STS; + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_WRITE_INT_STS_OFFSET, + mbits, mbits); + + /* Enable Non-DMA device inband interrupt */ + r_dma_ctrl_1 |= THC_M_PRT_READ_DMA_CNTRL_IE_NDDI; + regmap_write(dev->thc_regmap, + THC_M_PRT_READ_DMA_CNTRL_1_OFFSET, + r_dma_ctrl_1); + + if (dev->port_type == THC_PORT_TYPE_SPI) { + /* Edge triggered interrupt */ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_TSEQ_CNTRL_1_OFFSET, + THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN, + THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN); + } else { + /* Level triggered interrupt */ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_TSEQ_CNTRL_1_OFFSET, + THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN, 0); + + mbits = THC_M_PRT_INT_EN_THC_I2C_IC_MST_ON_HOLD_INT_EN | + THC_M_PRT_INT_EN_THC_I2C_IC_SCL_STUCK_AT_LOW_DET_INT_EN | + THC_M_PRT_INT_EN_THC_I2C_IC_TX_ABRT_INT_EN | + THC_M_PRT_INT_EN_THC_I2C_IC_TX_OVER_INT_EN | + THC_M_PRT_INT_EN_THC_I2C_IC_RX_FULL_INT_EN | + THC_M_PRT_INT_EN_THC_I2C_IC_RX_OVER_INT_EN | + THC_M_PRT_INT_EN_THC_I2C_IC_RX_UNDER_INT_EN; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_INT_EN_OFFSET, + mbits, mbits); + } + + thc_set_pio_interrupt_support(dev, false); + + /* HIDSPI specific settings */ + if (dev->port_type == THC_PORT_TYPE_SPI) { + mbits = FIELD_PREP(THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_OFFSET, + THC_BIT_OFFSET_INTERRUPT_TYPE) | + FIELD_PREP(THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_LEN, + THC_BIT_LENGTH_INTERRUPT_TYPE) | + FIELD_PREP(THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_EOF_OFFSET, + THC_BIT_OFFSET_LAST_FRAGMENT_FLAG) | + FIELD_PREP(THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_DATA_VAL, + THC_BITMASK_INVALID_TYPE_DATA); + mask = THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_OFFSET | + THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_LEN | + THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_EOF_OFFSET | + THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_DATA_VAL; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_DEVINT_CFG_1_OFFSET, + mask, mbits); + + mbits = FIELD_PREP(THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_OFFSET, + THC_BIT_OFFSET_MICROFRAME_SIZE) | + FIELD_PREP(THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_LEN, + THC_BIT_LENGTH_MICROFRAME_SIZE) | + FIELD_PREP(THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_UNIT, + THC_UNIT_MICROFRAME_SIZE) | + THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_IGNORE | + THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_VAL; + mask = THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_OFFSET | + THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_LEN | + THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_UNIT | + THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_IGNORE | + THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_VAL; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_DEVINT_CFG_2_OFFSET, + mask, mbits); + } +} +EXPORT_SYMBOL_NS_GPL(thc_interrupt_config, "INTEL_THC"); + +/** + * thc_int_trigger_type_select - Select THC interrupt trigger type + * + * @dev: the pointer of THC private device context + * @edge_trigger: determine the interrupt is edge triggered or level triggered + */ +void thc_int_trigger_type_select(struct thc_device *dev, bool edge_trigger) +{ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_TSEQ_CNTRL_1_OFFSET, + THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN, + edge_trigger ? THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN : 0); +} +EXPORT_SYMBOL_NS_GPL(thc_int_trigger_type_select, "INTEL_THC"); + +/** + * thc_interrupt_enable - Enable or disable THC interrupt + * + * @dev: the pointer of THC private device context + * @int_enable: the flag to control THC interrupt enable or disable + */ +void thc_interrupt_enable(struct thc_device *dev, bool int_enable) +{ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_INT_EN_OFFSET, + THC_M_PRT_INT_EN_GBL_INT_EN, + int_enable ? THC_M_PRT_INT_EN_GBL_INT_EN : 0); +} +EXPORT_SYMBOL_NS_GPL(thc_interrupt_enable, "INTEL_THC"); + +/** + * thc_interrupt_quiesce - Quiesce or unquiesce external touch device interrupt + * + * @dev: the pointer of THC private device context + * @int_quiesce: the flag to determine quiesce or unquiesce device interrupt + * + * Return: 0 on success, other error codes on failed + */ +int thc_interrupt_quiesce(const struct thc_device *dev, bool int_quiesce) +{ + u32 ctrl; + int ret; + + regmap_read(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, &ctrl); + if (!(ctrl & THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN) && !int_quiesce) { + dev_warn(dev->dev, "THC interrupt already unquiesce\n"); + return 0; + } + + if ((ctrl & THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN) && int_quiesce) { + dev_warn(dev->dev, "THC interrupt already quiesce\n"); + return 0; + } + + /* Quiesce device interrupt - Set quiesce bit and waiting for THC HW to ACK */ + if (int_quiesce) + regmap_write_bits(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, + THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN, + THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN); + + ret = regmap_read_poll_timeout(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, ctrl, + ctrl & THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_HW_STS, + THC_REGMAP_POLLING_INTERVAL_US, THC_QUIESCE_EN_TIMEOUT_US); + if (ret) { + dev_err_once(dev->dev, + "Timeout while waiting THC idle, target quiesce state = %s\n", + int_quiesce ? "true" : "false"); + return ret; + } + + /* Unquiesce device interrupt - Clear the quiesce bit */ + if (!int_quiesce) + regmap_write_bits(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, + THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN, 0); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_interrupt_quiesce, "INTEL_THC"); + +/** + * thc_set_pio_interrupt_support - Determine PIO interrupt is supported or not + * + * @dev: The pointer of THC private device context + * @supported: The flag to determine enabling PIO interrupt or not + */ +void thc_set_pio_interrupt_support(struct thc_device *dev, bool supported) +{ + dev->pio_int_supported = supported; +} +EXPORT_SYMBOL_NS_GPL(thc_set_pio_interrupt_support, "INTEL_THC"); + +/** + * thc_ltr_config - Configure THC Latency Tolerance Reporting(LTR) settings + * + * @dev: The pointer of THC private device context + * @active_ltr_us: active LTR value, unit is us + * @lp_ltr_us: low power LTR value, unit is us + */ +void thc_ltr_config(struct thc_device *dev, u32 active_ltr_us, u32 lp_ltr_us) +{ + u32 active_ltr_scale, lp_ltr_scale, ltr_ctrl, ltr_mask, orig, tmp; + + if (active_ltr_us >= THC_LTR_MIN_VAL_SCALE_3 && + active_ltr_us < THC_LTR_MAX_VAL_SCALE_3) { + active_ltr_scale = THC_LTR_SCALE_3; + active_ltr_us = active_ltr_us >> 5; + } else if (active_ltr_us >= THC_LTR_MIN_VAL_SCALE_4 && + active_ltr_us < THC_LTR_MAX_VAL_SCALE_4) { + active_ltr_scale = THC_LTR_SCALE_4; + active_ltr_us = active_ltr_us >> 10; + } else if (active_ltr_us >= THC_LTR_MIN_VAL_SCALE_5 && + active_ltr_us < THC_LTR_MAX_VAL_SCALE_5) { + active_ltr_scale = THC_LTR_SCALE_5; + active_ltr_us = active_ltr_us >> 15; + } else { + active_ltr_scale = THC_LTR_SCALE_2; + } + + if (lp_ltr_us >= THC_LTR_MIN_VAL_SCALE_3 && + lp_ltr_us < THC_LTR_MAX_VAL_SCALE_3) { + lp_ltr_scale = THC_LTR_SCALE_3; + lp_ltr_us = lp_ltr_us >> 5; + } else if (lp_ltr_us >= THC_LTR_MIN_VAL_SCALE_4 && + lp_ltr_us < THC_LTR_MAX_VAL_SCALE_4) { + lp_ltr_scale = THC_LTR_SCALE_4; + lp_ltr_us = lp_ltr_us >> 10; + } else if (lp_ltr_us >= THC_LTR_MIN_VAL_SCALE_5 && + lp_ltr_us < THC_LTR_MAX_VAL_SCALE_5) { + lp_ltr_scale = THC_LTR_SCALE_5; + lp_ltr_us = lp_ltr_us >> 15; + } else { + lp_ltr_scale = THC_LTR_SCALE_2; + } + + regmap_read(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, &orig); + ltr_ctrl = FIELD_PREP(THC_M_CMN_LTR_CTRL_ACT_LTR_VAL, active_ltr_us) | + FIELD_PREP(THC_M_CMN_LTR_CTRL_ACT_LTR_SCALE, active_ltr_scale) | + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_REQ | + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN | + FIELD_PREP(THC_M_CMN_LTR_CTRL_LP_LTR_VAL, lp_ltr_us) | + FIELD_PREP(THC_M_CMN_LTR_CTRL_LP_LTR_SCALE, lp_ltr_scale) | + THC_M_CMN_LTR_CTRL_LP_LTR_REQ; + + ltr_mask = THC_M_CMN_LTR_CTRL_ACT_LTR_VAL | + THC_M_CMN_LTR_CTRL_ACT_LTR_SCALE | + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_REQ | + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN | + THC_M_CMN_LTR_CTRL_LP_LTR_VAL | + THC_M_CMN_LTR_CTRL_LP_LTR_SCALE | + THC_M_CMN_LTR_CTRL_LP_LTR_REQ | + THC_M_CMN_LTR_CTRL_LP_LTR_EN; + + tmp = orig & ~ltr_mask; + tmp |= ltr_ctrl & ltr_mask; + + regmap_write(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, tmp); +} +EXPORT_SYMBOL_NS_GPL(thc_ltr_config, "INTEL_THC"); + +/** + * thc_change_ltr_mode - Change THC LTR mode + * + * @dev: The pointer of THC private device context + * @ltr_mode: LTR mode(active or low power) + */ +void thc_change_ltr_mode(struct thc_device *dev, u32 ltr_mode) +{ + if (ltr_mode == THC_LTR_MODE_ACTIVE) { + regmap_write_bits(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, + THC_M_CMN_LTR_CTRL_LP_LTR_EN, 0); + regmap_write_bits(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN, + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN); + return; + } + + regmap_write_bits(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN, 0); + regmap_write_bits(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, + THC_M_CMN_LTR_CTRL_LP_LTR_EN, + THC_M_CMN_LTR_CTRL_LP_LTR_EN); +} +EXPORT_SYMBOL_NS_GPL(thc_change_ltr_mode, "INTEL_THC"); + +/** + * thc_ltr_unconfig - Unconfigure THC Latency Tolerance Reporting(LTR) settings + * + * @dev: The pointer of THC private device context + */ +void thc_ltr_unconfig(struct thc_device *dev) +{ + u32 ltr_ctrl, bits_clear; + + regmap_read(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, <r_ctrl); + bits_clear = THC_M_CMN_LTR_CTRL_LP_LTR_EN | + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN | + THC_M_CMN_LTR_CTRL_LP_LTR_REQ | + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_REQ; + + ltr_ctrl &= ~bits_clear; + + regmap_write(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, ltr_ctrl); +} +EXPORT_SYMBOL_NS_GPL(thc_ltr_unconfig, "INTEL_THC"); + +/** + * thc_int_cause_read - Read interrupt cause register value + * + * @dev: The pointer of THC private device context + * + * Return: The interrupt cause register value + */ +u32 thc_int_cause_read(struct thc_device *dev) +{ + u32 int_cause; + + regmap_read(dev->thc_regmap, + THC_M_PRT_DEV_INT_CAUSE_REG_VAL_OFFSET, &int_cause); + + return int_cause; +} +EXPORT_SYMBOL_NS_GPL(thc_int_cause_read, "INTEL_THC"); + +static void thc_print_txn_error_cause(const struct thc_device *dev) +{ + bool known_error = false; + u32 cause = 0; + + regmap_read(dev->thc_regmap, THC_M_PRT_ERR_CAUSE_OFFSET, &cause); + + if (cause & THC_M_PRT_ERR_CAUSE_PRD_ENTRY_ERR) { + dev_err(dev->dev, "TXN Error: Invalid PRD Entry\n"); + known_error = true; + } + if (cause & THC_M_PRT_ERR_CAUSE_BUF_OVRRUN_ERR) { + dev_err(dev->dev, "TXN Error: THC Buffer Overrun\n"); + known_error = true; + } + if (cause & THC_M_PRT_ERR_CAUSE_FRAME_BABBLE_ERR) { + dev_err(dev->dev, "TXN Error: Frame Babble\n"); + known_error = true; + } + if (cause & THC_M_PRT_ERR_CAUSE_INVLD_DEV_ENTRY) { + dev_err(dev->dev, "TXN Error: Invalid Device Register Setting\n"); + known_error = true; + } + + /* Clear interrupt status bits */ + regmap_write(dev->thc_regmap, THC_M_PRT_ERR_CAUSE_OFFSET, cause); + + if (!known_error) + dev_err(dev->dev, "TXN Error does not match any known value: 0x%X\n", + cause); +} + +/** + * thc_interrupt_handler - Handle THC interrupts + * + * THC interrupts include several types: external touch device (TIC) non-DMA + * interrupts, PIO completion interrupts, DMA interrtups, I2C subIP raw + * interrupts and error interrupts. + * + * This is a help function for interrupt processing, it detects interrupt + * type, clear the interrupt status bit and return the interrupt type to caller + * for future processing. + * + * @dev: The pointer of THC private device context + * + * Return: The combined flag for interrupt type + */ +int thc_interrupt_handler(struct thc_device *dev) +{ + u32 read_sts_1, read_sts_2, read_sts_sw, write_sts; + u32 int_sts, err_cause, seq_cntrl, seq_sts; + int interrupt_type = 0; + + regmap_read(dev->thc_regmap, + THC_M_PRT_READ_DMA_INT_STS_1_OFFSET, &read_sts_1); + + if (read_sts_1 & THC_M_PRT_READ_DMA_INT_STS_NONDMA_INT_STS) { + dev_dbg(dev->dev, "THC non-DMA device interrupt\n"); + + regmap_write(dev->thc_regmap, THC_M_PRT_READ_DMA_INT_STS_1_OFFSET, + NONDMA_INT_STS_BIT); + + interrupt_type |= BIT(THC_NONDMA_INT); + + return interrupt_type; + } + + regmap_read(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, &int_sts); + + if (int_sts & THC_M_PRT_INT_STATUS_TXN_ERR_INT_STS) { + dev_err(dev->dev, "THC transaction error, int_sts: 0x%08X\n", int_sts); + thc_print_txn_error_cause(dev); + + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + TXN_ERR_INT_STS_BIT); + + interrupt_type |= BIT(THC_TXN_ERR_INT); + + return interrupt_type; + } + + regmap_read(dev->thc_regmap, THC_M_PRT_ERR_CAUSE_OFFSET, &err_cause); + regmap_read(dev->thc_regmap, + THC_M_PRT_READ_DMA_INT_STS_2_OFFSET, &read_sts_2); + + if (err_cause & THC_M_PRT_ERR_CAUSE_BUF_OVRRUN_ERR || + read_sts_1 & THC_M_PRT_READ_DMA_INT_STS_STALL_STS || + read_sts_2 & THC_M_PRT_READ_DMA_INT_STS_STALL_STS) { + dev_err(dev->dev, "Buffer overrun or RxDMA engine stalled!\n"); + thc_print_txn_error_cause(dev); + + regmap_write(dev->thc_regmap, THC_M_PRT_READ_DMA_INT_STS_2_OFFSET, + THC_M_PRT_READ_DMA_INT_STS_STALL_STS); + regmap_write(dev->thc_regmap, THC_M_PRT_READ_DMA_INT_STS_1_OFFSET, + THC_M_PRT_READ_DMA_INT_STS_STALL_STS); + regmap_write(dev->thc_regmap, THC_M_PRT_ERR_CAUSE_OFFSET, + THC_M_PRT_ERR_CAUSE_BUF_OVRRUN_ERR); + + interrupt_type |= BIT(THC_TXN_ERR_INT); + + return interrupt_type; + } + + if (int_sts & THC_M_PRT_INT_STATUS_FATAL_ERR_INT_STS) { + dev_err_once(dev->dev, "THC FATAL error, int_sts: 0x%08X\n", int_sts); + + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + TXN_FATAL_INT_STS_BIT); + + interrupt_type |= BIT(THC_FATAL_ERR_INT); + + return interrupt_type; + } + + regmap_read(dev->thc_regmap, + THC_M_PRT_SW_SEQ_CNTRL_OFFSET, &seq_cntrl); + regmap_read(dev->thc_regmap, + THC_M_PRT_SW_SEQ_STS_OFFSET, &seq_sts); + + if (seq_cntrl & THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CD_IE && + seq_sts & THC_M_PRT_SW_SEQ_STS_TSSDONE) { + dev_dbg(dev->dev, "THC_SS_CD_IE and TSSDONE are set\n"); + interrupt_type |= BIT(THC_PIO_DONE_INT); + } + + if (read_sts_1 & THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS) { + dev_dbg(dev->dev, "Got RxDMA1 Read Interrupt\n"); + + regmap_write(dev->thc_regmap, + THC_M_PRT_READ_DMA_INT_STS_1_OFFSET, read_sts_1); + + interrupt_type |= BIT(THC_RXDMA1_INT); + } + + if (read_sts_2 & THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS) { + dev_dbg(dev->dev, "Got RxDMA2 Read Interrupt\n"); + + regmap_write(dev->thc_regmap, + THC_M_PRT_READ_DMA_INT_STS_2_OFFSET, read_sts_2); + + interrupt_type |= BIT(THC_RXDMA2_INT); + } + + regmap_read(dev->thc_regmap, + THC_M_PRT_READ_DMA_INT_STS_SW_OFFSET, &read_sts_sw); + + if (read_sts_sw & THC_M_PRT_READ_DMA_INT_STS_DMACPL_STS) { + dev_dbg(dev->dev, "Got SwDMA Read Interrupt\n"); + + regmap_write(dev->thc_regmap, + THC_M_PRT_READ_DMA_INT_STS_SW_OFFSET, read_sts_sw); + + dev->swdma_done = true; + wake_up_interruptible(&dev->swdma_complete_wait); + + interrupt_type |= BIT(THC_SWDMA_INT); + } + + regmap_read(dev->thc_regmap, + THC_M_PRT_WRITE_INT_STS_OFFSET, &write_sts); + + if (write_sts & THC_M_PRT_WRITE_INT_STS_THC_WRDMA_CMPL_STATUS) { + dev_dbg(dev->dev, "Got TxDMA Write complete Interrupt\n"); + + regmap_write(dev->thc_regmap, + THC_M_PRT_WRITE_INT_STS_OFFSET, write_sts); + + dev->write_done = true; + wake_up_interruptible(&dev->write_complete_wait); + + interrupt_type |= BIT(THC_TXDMA_INT); + } + + if (int_sts & THC_M_PRT_INT_STATUS_DEV_RAW_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_DEV_RAW_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_UNDER_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_UNDER_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_OVER_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_OVER_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_FULL_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_FULL_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_OVER_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_OVER_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_EMPTY_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_EMPTY_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_ABRT_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_ABRT_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_ACTIVITY_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_ACTIVITY_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_SCL_STUCK_AT_LOW_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_SCL_STUCK_AT_LOW_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_STOP_DET_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_STOP_DET_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_START_DET_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_START_DET_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_MST_ON_HOLD_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_MST_ON_HOLD_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + + if (!interrupt_type) + interrupt_type |= BIT(THC_UNKNOWN_INT); + + return interrupt_type; +} +EXPORT_SYMBOL_NS_GPL(thc_interrupt_handler, "INTEL_THC"); + +/** + * thc_port_select - Set THC port type + * + * @dev: The pointer of THC private device context + * @port_type: THC port type to use for current device + * + * Return: 0 on success, other error codes on failed. + */ +int thc_port_select(struct thc_device *dev, enum thc_port_type port_type) +{ + u32 ctrl, mask; + + if (port_type == THC_PORT_TYPE_SPI) { + dev_dbg(dev->dev, "Set THC port type to SPI\n"); + dev->port_type = THC_PORT_TYPE_SPI; + + /* Enable delay of CS assertion and set to default value */ + ctrl = THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_EN | + FIELD_PREP(THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_VAL, + THC_CSA_CK_DELAY_VAL_DEFAULT); + mask = THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_EN | + THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_VAL; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPI_DUTYC_CFG_OFFSET, + mask, ctrl); + } else if (port_type == THC_PORT_TYPE_I2C) { + dev_dbg(dev->dev, "Set THC port type to I2C\n"); + dev->port_type = THC_PORT_TYPE_I2C; + + /* Set THC transition arbitration policy to frame boundary for I2C */ + ctrl = FIELD_PREP(THC_M_PRT_CONTROL_THC_ARB_POLICY, + THC_ARB_POLICY_FRAME_BOUNDARY); + mask = THC_M_PRT_CONTROL_THC_ARB_POLICY; + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, mask, ctrl); + } else { + dev_err(dev->dev, "unsupported THC port type: %d\n", port_type); + return -EINVAL; + } + + ctrl = FIELD_PREP(THC_M_PRT_CONTROL_PORT_TYPE, port_type); + mask = THC_M_PRT_CONTROL_PORT_TYPE; + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, mask, ctrl); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_port_select, "INTEL_THC"); + +#define THC_SPI_FREQUENCY_7M 7812500 +#define THC_SPI_FREQUENCY_15M 15625000 +#define THC_SPI_FREQUENCY_17M 17857100 +#define THC_SPI_FREQUENCY_20M 20833000 +#define THC_SPI_FREQUENCY_25M 25000000 +#define THC_SPI_FREQUENCY_31M 31250000 +#define THC_SPI_FREQUENCY_41M 41666700 + +#define THC_SPI_LOW_FREQUENCY THC_SPI_FREQUENCY_17M + +static u8 thc_get_spi_freq_div_val(struct thc_device *dev, u32 spi_freq_val) +{ + int frequency[] = { + THC_SPI_FREQUENCY_7M, + THC_SPI_FREQUENCY_15M, + THC_SPI_FREQUENCY_17M, + THC_SPI_FREQUENCY_20M, + THC_SPI_FREQUENCY_25M, + THC_SPI_FREQUENCY_31M, + THC_SPI_FREQUENCY_41M, + }; + u8 frequency_div[] = { + THC_SPI_FRQ_DIV_2, + THC_SPI_FRQ_DIV_1, + THC_SPI_FRQ_DIV_7, + THC_SPI_FRQ_DIV_6, + THC_SPI_FRQ_DIV_5, + THC_SPI_FRQ_DIV_4, + THC_SPI_FRQ_DIV_3, + }; + int size = ARRAY_SIZE(frequency); + u32 closest_freq; + u8 freq_div; + int i; + + for (i = size - 1; i >= 0; i--) + if ((int)spi_freq_val - frequency[i] >= 0) + break; + + if (i < 0) { + dev_err_once(dev->dev, "Not supported SPI frequency %d\n", spi_freq_val); + return THC_SPI_FRQ_RESERVED; + } + + closest_freq = frequency[i]; + freq_div = frequency_div[i]; + + dev_dbg(dev->dev, + "Setting SPI frequency: spi_freq_val = %u, Closest freq = %u\n", + spi_freq_val, closest_freq); + + return freq_div; +} + +/** + * thc_spi_read_config - Configure SPI bus read attributes + * + * @dev: The pointer of THC private device context + * @spi_freq_val: SPI read frequecy value + * @io_mode: SPI read IO mode + * @opcode: Read opcode + * @spi_rd_mps: SPI read max packet size + * + * Return: 0 on success, other error codes on failed. + */ +int thc_spi_read_config(struct thc_device *dev, u32 spi_freq_val, + u32 io_mode, u32 opcode, u32 spi_rd_mps) +{ + bool is_low_freq = false; + u32 cfg, mask; + u8 freq_div; + + freq_div = thc_get_spi_freq_div_val(dev, spi_freq_val); + if (freq_div == THC_SPI_FRQ_RESERVED) + return -EINVAL; + + if (spi_freq_val < THC_SPI_LOW_FREQUENCY) + is_low_freq = true; + + cfg = FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_TCRF, freq_div) | + FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_TRMODE, io_mode) | + (is_low_freq ? THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN : 0) | + FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_RD_MPS, spi_rd_mps); + mask = THC_M_PRT_SPI_CFG_SPI_TCRF | + THC_M_PRT_SPI_CFG_SPI_TRMODE | + THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN | + THC_M_PRT_SPI_CFG_SPI_RD_MPS; + + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_SPI_CFG_OFFSET, mask, cfg); + + if (io_mode == THC_QUAD_IO) + opcode = FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_SPI_QIO, opcode); + else if (io_mode == THC_DUAL_IO) + opcode = FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_SPI_DIO, opcode); + else + opcode = FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_SPI_SIO, opcode); + + regmap_write(dev->thc_regmap, THC_M_PRT_SPI_ICRRD_OPCODE_OFFSET, opcode); + regmap_write(dev->thc_regmap, THC_M_PRT_SPI_DMARD_OPCODE_OFFSET, opcode); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_spi_read_config, "INTEL_THC"); + +/** + * thc_spi_write_config - Configure SPI bus write attributes + * + * @dev: The pointer of THC private device context + * @spi_freq_val: SPI write frequecy value + * @io_mode: SPI write IO mode + * @opcode: Write opcode + * @spi_wr_mps: SPI write max packet size + * @perf_limit: Performance limitation in unit of 10us + * + * Return: 0 on success, other error codes on failed. + */ +int thc_spi_write_config(struct thc_device *dev, u32 spi_freq_val, + u32 io_mode, u32 opcode, u32 spi_wr_mps, + u32 perf_limit) +{ + bool is_low_freq = false; + u32 cfg, mask; + u8 freq_div; + + freq_div = thc_get_spi_freq_div_val(dev, spi_freq_val); + if (freq_div == THC_SPI_FRQ_RESERVED) + return -EINVAL; + + if (spi_freq_val < THC_SPI_LOW_FREQUENCY) + is_low_freq = true; + + cfg = FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_TCWF, freq_div) | + FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_TWMODE, io_mode) | + (is_low_freq ? THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN : 0) | + FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_WR_MPS, spi_wr_mps); + mask = THC_M_PRT_SPI_CFG_SPI_TCWF | + THC_M_PRT_SPI_CFG_SPI_TWMODE | + THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN | + THC_M_PRT_SPI_CFG_SPI_WR_MPS; + + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_SPI_CFG_OFFSET, mask, cfg); + + if (io_mode == THC_QUAD_IO) + opcode = FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_SPI_QIO, opcode); + else if (io_mode == THC_DUAL_IO) + opcode = FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_SPI_DIO, opcode); + else + opcode = FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_SPI_SIO, opcode); + + regmap_write(dev->thc_regmap, THC_M_PRT_SPI_WR_OPCODE_OFFSET, opcode); + + dev->perf_limit = perf_limit; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_spi_write_config, "INTEL_THC"); + +/** + * thc_spi_input_output_address_config - Configure SPI input and output addresses + * + * @dev: the pointer of THC private device context + * @input_hdr_addr: input report header address + * @input_bdy_addr: input report body address + * @output_addr: output report address + */ +void thc_spi_input_output_address_config(struct thc_device *dev, u32 input_hdr_addr, + u32 input_bdy_addr, u32 output_addr) +{ + regmap_write(dev->thc_regmap, + THC_M_PRT_DEV_INT_CAUSE_ADDR_OFFSET, input_hdr_addr); + regmap_write(dev->thc_regmap, + THC_M_PRT_RD_BULK_ADDR_1_OFFSET, input_bdy_addr); + regmap_write(dev->thc_regmap, + THC_M_PRT_RD_BULK_ADDR_2_OFFSET, input_bdy_addr); + regmap_write(dev->thc_regmap, + THC_M_PRT_WR_BULK_ADDR_OFFSET, output_addr); +} +EXPORT_SYMBOL_NS_GPL(thc_spi_input_output_address_config, "INTEL_THC"); + +static int thc_i2c_subip_pio_read(struct thc_device *dev, const u32 address, + u32 *size, u32 *buffer) +{ + int ret; + + if (!size || *size == 0 || !buffer) { + dev_err(dev->dev, "Invalid input parameters, size %p, buffer %p\n", + size, buffer); + return -EINVAL; + } + + if (mutex_lock_interruptible(&dev->thc_bus_lock)) + return -EINTR; + + ret = prepare_pio(dev, THC_PIO_OP_I2C_SUBSYSTEM_READ, address, *size); + if (ret < 0) + goto end; + + pio_start(dev, 0, NULL); + + ret = pio_wait(dev); + if (ret < 0) + goto end; + + ret = pio_complete(dev, buffer, size); + if (ret < 0) + goto end; + +end: + mutex_unlock(&dev->thc_bus_lock); + + if (ret) + dev_err_once(dev->dev, "Read THC I2C SubIP register failed %d, offset %u\n", + ret, address); + + return ret; +} + +static int thc_i2c_subip_pio_write(struct thc_device *dev, const u32 address, + const u32 size, const u32 *buffer) +{ + int ret; + + if (size == 0 || !buffer) { + dev_err(dev->dev, "Invalid input parameters, size %u, buffer %p\n", + size, buffer); + return -EINVAL; + } + + if (mutex_lock_interruptible(&dev->thc_bus_lock)) + return -EINTR; + + ret = prepare_pio(dev, THC_PIO_OP_I2C_SUBSYSTEM_WRITE, address, size); + if (ret < 0) + goto end; + + pio_start(dev, size, buffer); + + ret = pio_wait(dev); + if (ret < 0) + goto end; + + ret = pio_complete(dev, NULL, NULL); + if (ret < 0) + goto end; + +end: + mutex_unlock(&dev->thc_bus_lock); + + if (ret) + dev_err_once(dev->dev, "Write THC I2C SubIP register failed %d, offset %u\n", + ret, address); + + return ret; +} + +#define I2C_SUBIP_CON_DEFAULT 0x663 +#define I2C_SUBIP_INT_MASK_DEFAULT 0x7FFF +#define I2C_SUBIP_RX_TL_DEFAULT 62 +#define I2C_SUBIP_TX_TL_DEFAULT 0 +#define I2C_SUBIP_DMA_TDLR_DEFAULT 7 +#define I2C_SUBIP_DMA_RDLR_DEFAULT 7 + +static int thc_i2c_subip_set_speed(struct thc_device *dev, const u32 speed, + const u32 hcnt, const u32 lcnt) +{ + u32 hcnt_offset, lcnt_offset; + u32 val; + int ret; + + switch (speed) { + case THC_I2C_STANDARD: + hcnt_offset = THC_I2C_IC_SS_SCL_HCNT_OFFSET; + lcnt_offset = THC_I2C_IC_SS_SCL_LCNT_OFFSET; + break; + + case THC_I2C_FAST_AND_PLUS: + hcnt_offset = THC_I2C_IC_FS_SCL_HCNT_OFFSET; + lcnt_offset = THC_I2C_IC_FS_SCL_LCNT_OFFSET; + break; + + case THC_I2C_HIGH_SPEED: + hcnt_offset = THC_I2C_IC_HS_SCL_HCNT_OFFSET; + lcnt_offset = THC_I2C_IC_HS_SCL_LCNT_OFFSET; + break; + + default: + dev_err_once(dev->dev, "Unsupported i2c speed %d\n", speed); + ret = -EINVAL; + return ret; + } + + ret = thc_i2c_subip_pio_write(dev, hcnt_offset, sizeof(u32), &hcnt); + if (ret < 0) + return ret; + + ret = thc_i2c_subip_pio_write(dev, lcnt_offset, sizeof(u32), &lcnt); + if (ret < 0) + return ret; + + val = I2C_SUBIP_CON_DEFAULT & ~THC_I2C_IC_CON_SPEED; + val |= FIELD_PREP(THC_I2C_IC_CON_SPEED, speed); + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_CON_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + return 0; +} + +static u32 i2c_subip_regs[] = { + THC_I2C_IC_CON_OFFSET, + THC_I2C_IC_TAR_OFFSET, + THC_I2C_IC_INTR_MASK_OFFSET, + THC_I2C_IC_RX_TL_OFFSET, + THC_I2C_IC_TX_TL_OFFSET, + THC_I2C_IC_DMA_CR_OFFSET, + THC_I2C_IC_DMA_TDLR_OFFSET, + THC_I2C_IC_DMA_RDLR_OFFSET, + THC_I2C_IC_SS_SCL_HCNT_OFFSET, + THC_I2C_IC_SS_SCL_LCNT_OFFSET, + THC_I2C_IC_FS_SCL_HCNT_OFFSET, + THC_I2C_IC_FS_SCL_LCNT_OFFSET, + THC_I2C_IC_HS_SCL_HCNT_OFFSET, + THC_I2C_IC_HS_SCL_LCNT_OFFSET, + THC_I2C_IC_ENABLE_OFFSET, +}; + +/** + * thc_i2c_subip_init - Initialize and configure THC I2C subsystem + * + * @dev: The pointer of THC private device context + * @target_address: Slave address of touch device (TIC) + * @speed: I2C bus frequency speed mode + * @hcnt: I2C clock SCL high count + * @lcnt: I2C clock SCL low count + * + * Return: 0 on success, other error codes on failed. + */ +int thc_i2c_subip_init(struct thc_device *dev, const u32 target_address, + const u32 speed, const u32 hcnt, const u32 lcnt) +{ + u32 read_size = sizeof(u32); + u32 val; + int ret; + + ret = thc_i2c_subip_pio_read(dev, THC_I2C_IC_ENABLE_OFFSET, &read_size, &val); + if (ret < 0) + return ret; + + val &= ~THC_I2C_IC_ENABLE_ENABLE; + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_ENABLE_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + ret = thc_i2c_subip_pio_read(dev, THC_I2C_IC_TAR_OFFSET, &read_size, &val); + if (ret < 0) + return ret; + + val &= ~THC_I2C_IC_TAR_IC_TAR; + val |= FIELD_PREP(THC_I2C_IC_TAR_IC_TAR, target_address); + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_TAR_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + ret = thc_i2c_subip_set_speed(dev, speed, hcnt, lcnt); + if (ret < 0) + return ret; + + val = I2C_SUBIP_INT_MASK_DEFAULT; + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_INTR_MASK_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + val = I2C_SUBIP_RX_TL_DEFAULT; + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_RX_TL_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + val = I2C_SUBIP_TX_TL_DEFAULT; + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_TX_TL_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + val = THC_I2C_IC_DMA_CR_RDMAE | THC_I2C_IC_DMA_CR_TDMAE; + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_DMA_CR_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + val = I2C_SUBIP_DMA_TDLR_DEFAULT; + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_DMA_TDLR_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + val = I2C_SUBIP_DMA_RDLR_DEFAULT; + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_DMA_RDLR_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + ret = thc_i2c_subip_pio_read(dev, THC_I2C_IC_ENABLE_OFFSET, &read_size, &val); + if (ret < 0) + return ret; + + val |= THC_I2C_IC_ENABLE_ENABLE; + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_ENABLE_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + dev->i2c_subip_regs = devm_kzalloc(dev->dev, sizeof(i2c_subip_regs), GFP_KERNEL); + if (!dev->i2c_subip_regs) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_i2c_subip_init, "INTEL_THC"); + +/** + * thc_i2c_subip_regs_save - Save THC I2C sub-subsystem register values to THC device context + * + * @dev: The pointer of THC private device context + * + * Return: 0 on success, other error codes on failed. + */ +int thc_i2c_subip_regs_save(struct thc_device *dev) +{ + int ret; + u32 read_size = sizeof(u32); + + for (int i = 0; i < ARRAY_SIZE(i2c_subip_regs); i++) { + ret = thc_i2c_subip_pio_read(dev, i2c_subip_regs[i], + &read_size, (u32 *)&dev->i2c_subip_regs + i); + if (ret < 0) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_i2c_subip_regs_save, "INTEL_THC"); + +/** + * thc_i2c_subip_regs_restore - Restore THC I2C subsystem registers from THC device context + * + * @dev: The pointer of THC private device context + * + * Return: 0 on success, other error codes on failed. + */ +int thc_i2c_subip_regs_restore(struct thc_device *dev) +{ + int ret; + u32 write_size = sizeof(u32); + + for (int i = 0; i < ARRAY_SIZE(i2c_subip_regs); i++) { + ret = thc_i2c_subip_pio_write(dev, i2c_subip_regs[i], + write_size, (u32 *)&dev->i2c_subip_regs + i); + if (ret < 0) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_i2c_subip_regs_restore, "INTEL_THC"); + +MODULE_AUTHOR("Xinpeng Sun <xinpeng.sun@intel.com>"); +MODULE_AUTHOR("Even Xu <even.xu@intel.com>"); + +MODULE_DESCRIPTION("Intel(R) Intel THC Hardware Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h new file mode 100644 index 000000000000..0517fee2c668 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#ifndef _INTEL_THC_DEV_H_ +#define _INTEL_THC_DEV_H_ + +#include <linux/cdev.h> +#include <linux/mutex.h> +#include <linux/workqueue.h> + +#include "intel-thc-dma.h" + +#define THC_REGMAP_COMMON_OFFSET 0x10 +#define THC_REGMAP_MMIO_OFFSET 0x1000 + +/* + * THC Port type + * @THC_PORT_TYPE_SPI: This port is used for HIDSPI + * @THC_PORT_TYPE_I2C: This port is used for HIDI2C + */ +enum thc_port_type { + THC_PORT_TYPE_SPI = 0, + THC_PORT_TYPE_I2C = 1, +}; + +/** + * THC interrupt flag + * @THC_NONDMA_INT: THC non-DMA interrupt + * @THC_RXDMA1_INT: THC RxDMA1 interrupt + * @THC_RXDMA2_INT: THC RxDMA2 interrupt + * @THC_SWDMA_INT: THC SWDMA interrupt + * @THC_TXDMA_INT: THC TXDMA interrupt + * @THC_PIO_DONE_INT: THC PIO complete interrupt + * @THC_I2CSUBIP_INT: THC I2C subsystem interrupt + * @THC_TXN_ERR_INT: THC transfer error interrupt + * @THC_FATAL_ERR_INT: THC fatal error interrupt + */ +enum thc_int_type { + THC_NONDMA_INT = 0, + THC_RXDMA1_INT = 1, + THC_RXDMA2_INT = 2, + THC_SWDMA_INT = 3, + THC_TXDMA_INT = 4, + THC_PIO_DONE_INT = 5, + THC_I2CSUBIP_INT = 6, + THC_TXN_ERR_INT = 7, + THC_FATAL_ERR_INT = 8, + THC_UNKNOWN_INT +}; + +/** + * struct thc_device - THC private device struct + * @thc_regmap: MMIO regmap structure for accessing THC registers + * @mmio_addr: MMIO registers address + * @thc_bus_lock: mutex locker for THC config + * @port_type: port type of THC port instance + * @pio_int_supported: PIO interrupt supported flag + * @dma_ctx: DMA specific data + * @write_complete_wait: signal event for DMA write complete + * @swdma_complete_wait: signal event for SWDMA sequence complete + * @write_done: bool value that indicates if DMA write is done + * @swdma_done: bool value that indicates if SWDMA swquence is done + * @perf_limit: the delay between read operation and write operation + * @i2c_subip_regs: the copy of THC I2C sub-system registers for resuming restore + */ +struct thc_device { + struct device *dev; + struct regmap *thc_regmap; + void __iomem *mmio_addr; + struct mutex thc_bus_lock; + enum thc_port_type port_type; + bool pio_int_supported; + + struct thc_dma_context *dma_ctx; + + wait_queue_head_t write_complete_wait; + wait_queue_head_t swdma_complete_wait; + bool write_done; + bool swdma_done; + + u32 perf_limit; + + u32 *i2c_subip_regs; +}; + +struct thc_device *thc_dev_init(struct device *device, void __iomem *mem_addr); +int thc_tic_pio_read(struct thc_device *dev, const u32 address, + const u32 size, u32 *actual_size, u32 *buffer); +int thc_tic_pio_write(struct thc_device *dev, const u32 address, + const u32 size, const u32 *buffer); +int thc_tic_pio_write_and_read(struct thc_device *dev, const u32 address, + const u32 write_size, const u32 *write_buffer, + const u32 read_size, u32 *actual_size, u32 *read_buffer); +void thc_interrupt_config(struct thc_device *dev); +void thc_int_trigger_type_select(struct thc_device *dev, bool edge_trigger); +void thc_interrupt_enable(struct thc_device *dev, bool int_enable); +void thc_set_pio_interrupt_support(struct thc_device *dev, bool supported); +int thc_interrupt_quiesce(const struct thc_device *dev, bool int_quiesce); +void thc_ltr_config(struct thc_device *dev, u32 active_ltr_us, u32 lp_ltr_us); +void thc_change_ltr_mode(struct thc_device *dev, u32 ltr_mode); +void thc_ltr_unconfig(struct thc_device *dev); +u32 thc_int_cause_read(struct thc_device *dev); +int thc_interrupt_handler(struct thc_device *dev); +int thc_port_select(struct thc_device *dev, enum thc_port_type port_type); +int thc_spi_read_config(struct thc_device *dev, u32 spi_freq_val, + u32 io_mode, u32 opcode, u32 spi_rd_mps); +int thc_spi_write_config(struct thc_device *dev, u32 spi_freq_val, + u32 io_mode, u32 opcode, u32 spi_wr_mps, u32 perf_limit); +void thc_spi_input_output_address_config(struct thc_device *dev, u32 input_hdr_addr, + u32 input_bdy_addr, u32 output_addr); +int thc_i2c_subip_init(struct thc_device *dev, const u32 target_address, + const u32 speed, const u32 hcnt, const u32 lcnt); +int thc_i2c_subip_regs_save(struct thc_device *dev); +int thc_i2c_subip_regs_restore(struct thc_device *dev); + +#endif /* _INTEL_THC_DEV_H_ */ diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c new file mode 100644 index 000000000000..eb23bea77686 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c @@ -0,0 +1,969 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/overflow.h> +#include <linux/regmap.h> +#include <linux/scatterlist.h> + +#include "intel-thc-dev.h" +#include "intel-thc-dma.h" +#include "intel-thc-hw.h" + +static void dma_set_prd_base_addr(struct thc_device *dev, u64 physical_addr, + struct thc_dma_configuration *dma_config) +{ + u32 addr_high, addr_low; + + if (!dma_config->is_enabled) + return; + + addr_high = upper_32_bits(physical_addr); + addr_low = lower_32_bits(physical_addr); + + regmap_write(dev->thc_regmap, dma_config->prd_base_addr_high, addr_high); + regmap_write(dev->thc_regmap, dma_config->prd_base_addr_low, addr_low); +} + +static void dma_set_start_bit(struct thc_device *dev, + struct thc_dma_configuration *dma_config) +{ + u32 ctrl, mask, mbits, data, offset; + + if (!dma_config->is_enabled) + return; + + switch (dma_config->dma_channel) { + case THC_RXDMA1: + case THC_RXDMA2: + if (dma_config->dma_channel == THC_RXDMA2) { + mbits = FIELD_PREP(THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_DATA_VAL, + THC_BITMASK_INTERRUPT_TYPE_DATA); + mask = THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_DATA_VAL; + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_DEVINT_CFG_1_OFFSET, mask, mbits); + } + + mbits = THC_M_PRT_READ_DMA_CNTRL_IE_EOF | + THC_M_PRT_READ_DMA_CNTRL_SOO | + THC_M_PRT_READ_DMA_CNTRL_IE_STALL | + THC_M_PRT_READ_DMA_CNTRL_IE_ERROR | + THC_M_PRT_READ_DMA_CNTRL_START; + + mask = THC_M_PRT_READ_DMA_CNTRL_TPCWP | mbits; + mask |= THC_M_PRT_READ_DMA_CNTRL_INT_SW_DMA_EN; + ctrl = FIELD_PREP(THC_M_PRT_READ_DMA_CNTRL_TPCWP, THC_POINTER_WRAPAROUND) | mbits; + offset = dma_config->dma_channel == THC_RXDMA1 ? + THC_M_PRT_READ_DMA_CNTRL_1_OFFSET : THC_M_PRT_READ_DMA_CNTRL_2_OFFSET; + regmap_write_bits(dev->thc_regmap, offset, mask, ctrl); + break; + + case THC_SWDMA: + mbits = THC_M_PRT_READ_DMA_CNTRL_IE_DMACPL | + THC_M_PRT_READ_DMA_CNTRL_IE_IOC | + THC_M_PRT_READ_DMA_CNTRL_SOO | + THC_M_PRT_READ_DMA_CNTRL_START; + + mask = THC_M_PRT_READ_DMA_CNTRL_TPCWP | mbits; + ctrl = FIELD_PREP(THC_M_PRT_READ_DMA_CNTRL_TPCWP, THC_POINTER_WRAPAROUND) | mbits; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_SW_OFFSET, + mask, ctrl); + break; + + case THC_TXDMA: + regmap_write_bits(dev->thc_regmap, THC_M_PRT_WRITE_INT_STS_OFFSET, + THC_M_PRT_WRITE_INT_STS_THC_WRDMA_CMPL_STATUS, + THC_M_PRT_WRITE_INT_STS_THC_WRDMA_CMPL_STATUS); + + /* Select interrupt or polling method upon Write completion */ + if (dev->dma_ctx->use_write_interrupts) + data = THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC_DMACPL; + else + data = 0; + + data |= THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_START; + mask = THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC_DMACPL | + THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_START; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_WRITE_DMA_CNTRL_OFFSET, + mask, data); + break; + + default: + break; + } +} + +static void dma_set_prd_control(struct thc_device *dev, u8 entry_count, u8 cb_depth, + struct thc_dma_configuration *dma_config) +{ + u32 ctrl, mask; + + if (!dma_config->is_enabled) + return; + + if (dma_config->dma_channel == THC_TXDMA) { + mask = THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_PTEC; + ctrl = FIELD_PREP(THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_PTEC, entry_count); + } else { + mask = THC_M_PRT_RPRD_CNTRL_PTEC | THC_M_PRT_RPRD_CNTRL_PCD; + ctrl = FIELD_PREP(THC_M_PRT_RPRD_CNTRL_PTEC, entry_count) | + FIELD_PREP(THC_M_PRT_RPRD_CNTRL_PCD, cb_depth); + } + + regmap_write_bits(dev->thc_regmap, dma_config->prd_cntrl, mask, ctrl); +} + +static void dma_clear_prd_control(struct thc_device *dev, + struct thc_dma_configuration *dma_config) +{ + u32 mask; + + if (!dma_config->is_enabled) + return; + + if (dma_config->dma_channel == THC_TXDMA) + mask = THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_PTEC; + else + mask = THC_M_PRT_RPRD_CNTRL_PTEC | THC_M_PRT_RPRD_CNTRL_PCD; + + regmap_write_bits(dev->thc_regmap, dma_config->prd_cntrl, mask, 0); +} + +static u8 dma_get_read_pointer(struct thc_device *dev, + struct thc_dma_configuration *dma_config) +{ + u32 ctrl, read_pointer; + + regmap_read(dev->thc_regmap, dma_config->dma_cntrl, &ctrl); + read_pointer = FIELD_GET(THC_M_PRT_READ_DMA_CNTRL_TPCRP, ctrl); + + dev_dbg(dev->dev, "THC_M_PRT_READ_DMA_CNTRL 0x%x offset 0x%x TPCRP 0x%x\n", + ctrl, dma_config->dma_cntrl, read_pointer); + + return read_pointer; +} + +static u8 dma_get_write_pointer(struct thc_device *dev, + struct thc_dma_configuration *dma_config) +{ + u32 ctrl, write_pointer; + + regmap_read(dev->thc_regmap, dma_config->dma_cntrl, &ctrl); + write_pointer = FIELD_GET(THC_M_PRT_READ_DMA_CNTRL_TPCWP, ctrl); + + dev_dbg(dev->dev, "THC_M_PRT_READ_DMA_CNTRL 0x%x offset 0x%x TPCWP 0x%x\n", + ctrl, dma_config->dma_cntrl, write_pointer); + + return write_pointer; +} + +static void dma_set_write_pointer(struct thc_device *dev, u8 value, + struct thc_dma_configuration *dma_config) +{ + u32 ctrl, mask; + + mask = THC_M_PRT_READ_DMA_CNTRL_TPCWP; + ctrl = FIELD_PREP(THC_M_PRT_READ_DMA_CNTRL_TPCWP, value); + regmap_write_bits(dev->thc_regmap, dma_config->dma_cntrl, mask, ctrl); +} + +static size_t dma_get_max_packet_size(struct thc_device *dev, + struct thc_dma_configuration *dma_config) +{ + return dma_config->max_packet_size; +} + +static void dma_set_max_packet_size(struct thc_device *dev, size_t size, + struct thc_dma_configuration *dma_config) +{ + if (size) { + dma_config->max_packet_size = ALIGN(size, SZ_4K); + dma_config->is_enabled = true; + } +} + +static void thc_copy_one_sgl_to_prd(struct thc_device *dev, + struct thc_dma_configuration *config, + unsigned int ind) +{ + struct thc_prd_table *prd_tbl; + struct scatterlist *sg; + int j; + + prd_tbl = &config->prd_tbls[ind]; + + for_each_sg(config->sgls[ind], sg, config->sgls_nent[ind], j) { + prd_tbl->entries[j].dest_addr = + sg_dma_address(sg) >> THC_ADDRESS_SHIFT; + prd_tbl->entries[j].len = sg_dma_len(sg); + prd_tbl->entries[j].hw_status = 0; + prd_tbl->entries[j].end_of_prd = 0; + } + + /* Set the end_of_prd flag in the last filled entry */ + if (j > 0) + prd_tbl->entries[j - 1].end_of_prd = 1; +} + +static void thc_copy_sgls_to_prd(struct thc_device *dev, + struct thc_dma_configuration *config) +{ + unsigned int i; + + memset(config->prd_tbls, 0, array_size(PRD_TABLE_SIZE, config->prd_tbl_num)); + + for (i = 0; i < config->prd_tbl_num; i++) + thc_copy_one_sgl_to_prd(dev, config, i); +} + +static int setup_dma_buffers(struct thc_device *dev, + struct thc_dma_configuration *config, + enum dma_data_direction dir) +{ + size_t prd_tbls_size = array_size(PRD_TABLE_SIZE, config->prd_tbl_num); + unsigned int i, nent = PRD_ENTRIES_NUM; + dma_addr_t dma_handle; + void *cpu_addr; + size_t buf_sz; + int count; + + if (!config->is_enabled) + return 0; + + memset(config->sgls, 0, sizeof(config->sgls)); + memset(config->sgls_nent, 0, sizeof(config->sgls_nent)); + + cpu_addr = dma_alloc_coherent(dev->dev, prd_tbls_size, + &dma_handle, GFP_KERNEL); + if (!cpu_addr) + return -ENOMEM; + + config->prd_tbls = cpu_addr; + config->prd_tbls_dma_handle = dma_handle; + + buf_sz = dma_get_max_packet_size(dev, config); + + /* Allocate and map the scatter-gather lists, one for each PRD table */ + for (i = 0; i < config->prd_tbl_num; i++) { + config->sgls[i] = sgl_alloc(buf_sz, GFP_KERNEL, &nent); + if (!config->sgls[i] || nent > PRD_ENTRIES_NUM) { + dev_err_once(dev->dev, "sgl_alloc (%uth) failed, nent %u\n", + i, nent); + return -ENOMEM; + } + count = dma_map_sg(dev->dev, config->sgls[i], nent, dir); + + config->sgls_nent[i] = count; + } + + thc_copy_sgls_to_prd(dev, config); + + return 0; +} + +static void thc_reset_dma_settings(struct thc_device *dev) +{ + /* Stop all DMA channels and reset DMA read pointers */ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_1_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_START, 0); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_2_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_START, 0); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_SW_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_START, 0); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_WRITE_DMA_CNTRL_OFFSET, + THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_START, 0); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_1_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_TPCPR, + THC_M_PRT_READ_DMA_CNTRL_TPCPR); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_2_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_TPCPR, + THC_M_PRT_READ_DMA_CNTRL_TPCPR); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_SW_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_TPCPR, + THC_M_PRT_READ_DMA_CNTRL_TPCPR); +} + +static void release_dma_buffers(struct thc_device *dev, + struct thc_dma_configuration *config) +{ + size_t prd_tbls_size = array_size(PRD_TABLE_SIZE, config->prd_tbl_num); + unsigned int i; + + if (!config->is_enabled) + return; + + for (i = 0; i < config->prd_tbl_num; i++) { + if (!config->sgls[i] | !config->sgls_nent[i]) + continue; + + dma_unmap_sg(dev->dev, config->sgls[i], + config->sgls_nent[i], + config->dir); + + sgl_free(config->sgls[i]); + config->sgls[i] = NULL; + } + + memset(config->prd_tbls, 0, prd_tbls_size); + + if (config->prd_tbls) { + dma_free_coherent(dev->dev, prd_tbls_size, config->prd_tbls, + config->prd_tbls_dma_handle); + config->prd_tbls = NULL; + config->prd_tbls_dma_handle = 0; + } +} + +struct thc_dma_context *thc_dma_init(struct thc_device *dev) +{ + struct thc_dma_context *dma_ctx; + + dma_ctx = devm_kzalloc(dev->dev, sizeof(*dma_ctx), GFP_KERNEL); + if (!dma_ctx) + return NULL; + + dev->dma_ctx = dma_ctx; + + dma_ctx->dma_config[THC_RXDMA1].dma_channel = THC_RXDMA1; + dma_ctx->dma_config[THC_RXDMA2].dma_channel = THC_RXDMA2; + dma_ctx->dma_config[THC_TXDMA].dma_channel = THC_TXDMA; + dma_ctx->dma_config[THC_SWDMA].dma_channel = THC_SWDMA; + + dma_ctx->dma_config[THC_RXDMA1].dir = DMA_FROM_DEVICE; + dma_ctx->dma_config[THC_RXDMA2].dir = DMA_FROM_DEVICE; + dma_ctx->dma_config[THC_TXDMA].dir = DMA_TO_DEVICE; + dma_ctx->dma_config[THC_SWDMA].dir = DMA_FROM_DEVICE; + + dma_ctx->dma_config[THC_RXDMA1].prd_tbl_num = PRD_TABLES_NUM; + dma_ctx->dma_config[THC_RXDMA2].prd_tbl_num = PRD_TABLES_NUM; + dma_ctx->dma_config[THC_TXDMA].prd_tbl_num = 1; + dma_ctx->dma_config[THC_SWDMA].prd_tbl_num = 1; + + dma_ctx->dma_config[THC_RXDMA1].prd_base_addr_high = THC_M_PRT_RPRD_BA_HI_1_OFFSET; + dma_ctx->dma_config[THC_RXDMA2].prd_base_addr_high = THC_M_PRT_RPRD_BA_HI_2_OFFSET; + dma_ctx->dma_config[THC_TXDMA].prd_base_addr_high = THC_M_PRT_WPRD_BA_HI_OFFSET; + dma_ctx->dma_config[THC_SWDMA].prd_base_addr_high = THC_M_PRT_RPRD_BA_HI_SW_OFFSET; + + dma_ctx->dma_config[THC_RXDMA1].prd_base_addr_low = THC_M_PRT_RPRD_BA_LOW_1_OFFSET; + dma_ctx->dma_config[THC_RXDMA2].prd_base_addr_low = THC_M_PRT_RPRD_BA_LOW_2_OFFSET; + dma_ctx->dma_config[THC_TXDMA].prd_base_addr_low = THC_M_PRT_WPRD_BA_LOW_OFFSET; + dma_ctx->dma_config[THC_SWDMA].prd_base_addr_low = THC_M_PRT_RPRD_BA_LOW_SW_OFFSET; + + dma_ctx->dma_config[THC_RXDMA1].prd_cntrl = THC_M_PRT_RPRD_CNTRL_1_OFFSET; + dma_ctx->dma_config[THC_RXDMA2].prd_cntrl = THC_M_PRT_RPRD_CNTRL_2_OFFSET; + dma_ctx->dma_config[THC_TXDMA].prd_cntrl = THC_M_PRT_WRITE_DMA_CNTRL_OFFSET; + dma_ctx->dma_config[THC_SWDMA].prd_cntrl = THC_M_PRT_RPRD_CNTRL_SW_OFFSET; + + dma_ctx->dma_config[THC_RXDMA1].dma_cntrl = THC_M_PRT_READ_DMA_CNTRL_1_OFFSET; + dma_ctx->dma_config[THC_RXDMA2].dma_cntrl = THC_M_PRT_READ_DMA_CNTRL_2_OFFSET; + dma_ctx->dma_config[THC_TXDMA].dma_cntrl = THC_M_PRT_WRITE_DMA_CNTRL_OFFSET; + dma_ctx->dma_config[THC_SWDMA].dma_cntrl = THC_M_PRT_READ_DMA_CNTRL_SW_OFFSET; + + /* Enable write DMA completion interrupt by default */ + dma_ctx->use_write_interrupts = 1; + + return dma_ctx; +} + +/** + * thc_dma_set_max_packet_sizes - Set max packet sizes for all DMA engines + * + * @dev: The pointer of THC private device context + * @mps_read1: RxDMA1 max packet size + * @mps_read2: RxDMA2 max packet size + * @mps_write: TxDMA max packet size + * @mps_swdma: Software DMA max packet size + * + * If mps is not 0, it means the corresponding DMA channel is used, then set + * the flag to turn on this channel. + * + * Return: 0 on success, other error codes on failed. + */ +int thc_dma_set_max_packet_sizes(struct thc_device *dev, size_t mps_read1, + size_t mps_read2, size_t mps_write, + size_t mps_swdma) +{ + if (!dev->dma_ctx) { + dev_err_once(dev->dev, + "Cannot set max packet sizes because DMA context is NULL!\n"); + return -EINVAL; + } + + dma_set_max_packet_size(dev, mps_read1, &dev->dma_ctx->dma_config[THC_RXDMA1]); + dma_set_max_packet_size(dev, mps_read2, &dev->dma_ctx->dma_config[THC_RXDMA2]); + dma_set_max_packet_size(dev, mps_write, &dev->dma_ctx->dma_config[THC_TXDMA]); + dma_set_max_packet_size(dev, mps_swdma, &dev->dma_ctx->dma_config[THC_SWDMA]); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_dma_set_max_packet_sizes, "INTEL_THC"); + +/** + * thc_dma_allocate - Allocate DMA buffers for all DMA engines + * + * @dev: The pointer of THC private device context + * + * Return: 0 on success, other error codes on failed. + */ +int thc_dma_allocate(struct thc_device *dev) +{ + int ret, chan; + + for (chan = 0; chan < MAX_THC_DMA_CHANNEL; chan++) { + ret = setup_dma_buffers(dev, &dev->dma_ctx->dma_config[chan], + dev->dma_ctx->dma_config[chan].dir); + if (ret < 0) { + dev_err_once(dev->dev, "DMA setup failed for DMA channel %d\n", chan); + goto release_bufs; + } + } + + return 0; + +release_bufs: + while (chan--) + release_dma_buffers(dev, &dev->dma_ctx->dma_config[chan]); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(thc_dma_allocate, "INTEL_THC"); + +/** + * thc_dma_release - Release DMA buffers for all DMA engines + * + * @dev: The pointer of THC private device context + */ +void thc_dma_release(struct thc_device *dev) +{ + int chan; + + for (chan = 0; chan < MAX_THC_DMA_CHANNEL; chan++) + release_dma_buffers(dev, &dev->dma_ctx->dma_config[chan]); +} +EXPORT_SYMBOL_NS_GPL(thc_dma_release, "INTEL_THC"); + +static int calc_prd_entries_num(struct thc_prd_table *prd_tbl, + size_t mes_len, u8 *nent) +{ + *nent = DIV_ROUND_UP(mes_len, THC_MIN_BYTES_PER_SG_LIST_ENTRY); + if (*nent > PRD_ENTRIES_NUM) + return -EMSGSIZE; + + return 0; +} + +static size_t calc_message_len(struct thc_prd_table *prd_tbl, u8 *nent) +{ + size_t mes_len = 0; + unsigned int j; + + for (j = 0; j < PRD_ENTRIES_NUM; j++) { + mes_len += prd_tbl->entries[j].len; + if (prd_tbl->entries[j].end_of_prd) + break; + } + + *nent = j + 1; + + return mes_len; +} + +/** + * thc_dma_configure - Configure DMA settings for all DMA engines + * + * @dev: The pointer of THC private device context + * + * Return: 0 on success, other error codes on failed. + */ +int thc_dma_configure(struct thc_device *dev) +{ + struct thc_dma_context *dma_ctx = dev->dma_ctx; + int chan; + + thc_reset_dma_settings(dev); + + if (!dma_ctx) { + dev_err_once(dev->dev, "Cannot do DMA configure because DMA context is NULL\n"); + return -EINVAL; + } + + for (chan = 0; chan < MAX_THC_DMA_CHANNEL; chan++) { + dma_set_prd_base_addr(dev, + dma_ctx->dma_config[chan].prd_tbls_dma_handle, + &dma_ctx->dma_config[chan]); + + dma_set_prd_control(dev, PRD_ENTRIES_NUM - 1, + dma_ctx->dma_config[chan].prd_tbl_num - 1, + &dma_ctx->dma_config[chan]); + } + + /* Start read2 DMA engine */ + dma_set_start_bit(dev, &dma_ctx->dma_config[THC_RXDMA2]); + + dev_dbg(dev->dev, "DMA configured successfully!\n"); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_dma_configure, "INTEL_THC"); + +/** + * thc_dma_unconfigure - Unconfigure DMA settings for all DMA engines + * + * @dev: The pointer of THC private device context + */ +void thc_dma_unconfigure(struct thc_device *dev) +{ + int chan; + + for (chan = 0; chan < MAX_THC_DMA_CHANNEL; chan++) { + dma_set_prd_base_addr(dev, 0, &dev->dma_ctx->dma_config[chan]); + dma_clear_prd_control(dev, &dev->dma_ctx->dma_config[chan]); + } + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_1_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_START, 0); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_2_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_START, 0); +} +EXPORT_SYMBOL_NS_GPL(thc_dma_unconfigure, "INTEL_THC"); + +static int thc_wait_for_dma_pause(struct thc_device *dev, enum thc_dma_channel channel) +{ + u32 ctrl_reg, sts_reg, sts; + int ret; + + ctrl_reg = (channel == THC_RXDMA1) ? THC_M_PRT_READ_DMA_CNTRL_1_OFFSET : + ((channel == THC_RXDMA2) ? THC_M_PRT_READ_DMA_CNTRL_2_OFFSET : + THC_M_PRT_READ_DMA_CNTRL_SW_OFFSET); + + regmap_write_bits(dev->thc_regmap, ctrl_reg, THC_M_PRT_READ_DMA_CNTRL_START, 0); + + sts_reg = (channel == THC_RXDMA1) ? THC_M_PRT_READ_DMA_INT_STS_1_OFFSET : + ((channel == THC_RXDMA2) ? THC_M_PRT_READ_DMA_INT_STS_2_OFFSET : + THC_M_PRT_READ_DMA_INT_STS_SW_OFFSET); + + ret = regmap_read_poll_timeout(dev->thc_regmap, sts_reg, sts, + !(sts & THC_M_PRT_READ_DMA_INT_STS_ACTIVE), + THC_DEFAULT_RXDMA_POLLING_US_INTERVAL, + THC_DEFAULT_RXDMA_POLLING_US_TIMEOUT); + + if (ret) { + dev_err_once(dev->dev, + "Timeout while waiting for DMA %d stop\n", channel); + return ret; + } + + return 0; +} + +static int read_dma_buffer(struct thc_device *dev, + struct thc_dma_configuration *read_config, + u8 prd_table_index, void *read_buff) +{ + struct thc_prd_table *prd_tbl; + struct scatterlist *sg; + size_t mes_len, ret; + u8 nent; + + if (prd_table_index >= read_config->prd_tbl_num) { + dev_err_once(dev->dev, "PRD table index %d too big\n", prd_table_index); + return -EINVAL; + } + + prd_tbl = &read_config->prd_tbls[prd_table_index]; + mes_len = calc_message_len(prd_tbl, &nent); + if (mes_len > read_config->max_packet_size) { + dev_err(dev->dev, + "Message length %zu is bigger than buffer length %lu\n", + mes_len, read_config->max_packet_size); + return -EMSGSIZE; + } + + sg = read_config->sgls[prd_table_index]; + ret = sg_copy_to_buffer(sg, nent, read_buff, mes_len); + if (ret != mes_len) { + dev_err_once(dev->dev, "Copied %zu bytes instead of requested %zu\n", + ret, mes_len); + return -EIO; + } + + return mes_len; +} + +static void update_write_pointer(struct thc_device *dev, + struct thc_dma_configuration *read_config) +{ + u8 write_ptr = dma_get_write_pointer(dev, read_config); + + if (write_ptr + 1 == THC_WRAPAROUND_VALUE_ODD) + dma_set_write_pointer(dev, THC_POINTER_WRAPAROUND, read_config); + else if (write_ptr + 1 == THC_WRAPAROUND_VALUE_EVEN) + dma_set_write_pointer(dev, 0, read_config); + else + dma_set_write_pointer(dev, write_ptr + 1, read_config); +} + +static int is_dma_buf_empty(struct thc_device *dev, + struct thc_dma_configuration *read_config, + u8 *read_ptr, u8 *write_ptr) +{ + *read_ptr = dma_get_read_pointer(dev, read_config); + *write_ptr = dma_get_write_pointer(dev, read_config); + + if ((*read_ptr & THC_POINTER_MASK) == (*write_ptr & THC_POINTER_MASK)) + if (*read_ptr != *write_ptr) + return true; + + return false; +} + +static int thc_dma_read(struct thc_device *dev, + struct thc_dma_configuration *read_config, + void *read_buff, size_t *read_len, int *read_finished) +{ + u8 read_ptr, write_ptr, prd_table_index; + int status; + + if (!is_dma_buf_empty(dev, read_config, &read_ptr, &write_ptr)) { + prd_table_index = write_ptr & THC_POINTER_MASK; + + status = read_dma_buffer(dev, read_config, prd_table_index, read_buff); + if (status <= 0) { + dev_err_once(dev->dev, "read DMA buffer failed %d\n", status); + return -EIO; + } + + *read_len = status; + + /* Clear the relevant PRD table */ + thc_copy_one_sgl_to_prd(dev, read_config, prd_table_index); + + /* Increment the write pointer to let the HW know we have processed this PRD */ + update_write_pointer(dev, read_config); + } + + /* + * This function only reads one frame from PRD table for each call, so we need to + * check if all DMAed data is read out and return the flag to the caller. Caller + * should repeatedly call thc_dma_read() until all DMAed data is handled. + */ + if (read_finished) + *read_finished = is_dma_buf_empty(dev, read_config, &read_ptr, &write_ptr) ? 1 : 0; + + return 0; +} + +/** + * thc_rxdma_read - Read data from RXDMA buffer + * + * @dev: The pointer of THC private device context + * @dma_channel: The RXDMA engine of read data source + * @read_buff: The pointer of the read data buffer + * @read_len: The pointer of the read data length + * @read_finished: The pointer of the flag indicating if all pending data has been read out + * + * Return: 0 on success, other error codes on failed. + */ +int thc_rxdma_read(struct thc_device *dev, enum thc_dma_channel dma_channel, + void *read_buff, size_t *read_len, int *read_finished) +{ + struct thc_dma_configuration *dma_config; + int ret; + + dma_config = &dev->dma_ctx->dma_config[dma_channel]; + + if (!dma_config->is_enabled) { + dev_err_once(dev->dev, "The DMA channel %d is not enabled", dma_channel); + return -EINVAL; + } + + if (!read_buff || !read_len) { + dev_err(dev->dev, "Invalid input parameters, read_buff %p, read_len %p\n", + read_buff, read_len); + return -EINVAL; + } + + if (dma_channel >= THC_TXDMA) { + dev_err(dev->dev, "Unsupported DMA channel for RxDMA read, %d\n", dma_channel); + return -EINVAL; + } + + ret = thc_dma_read(dev, dma_config, read_buff, read_len, read_finished); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(thc_rxdma_read, "INTEL_THC"); + +static int thc_swdma_read_start(struct thc_device *dev, void *write_buff, + size_t write_len, u32 *prd_tbl_len) +{ + u32 mask, val, data0 = 0, data1 = 0; + int ret; + + ret = thc_interrupt_quiesce(dev, true); + if (ret) + return ret; + + if (thc_wait_for_dma_pause(dev, THC_RXDMA1) || thc_wait_for_dma_pause(dev, THC_RXDMA2)) + return -EIO; + + thc_reset_dma_settings(dev); + + mask = THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_WBC | + THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_RX_DLEN_EN; + val = FIELD_PREP(THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_WBC, write_len) | + ((!prd_tbl_len) ? THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_RX_DLEN_EN : 0); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_RPRD_CNTRL_SW_OFFSET, + mask, val); + + if (prd_tbl_len) { + mask = THC_M_PRT_SW_DMA_PRD_TABLE_LEN_THC_M_PRT_SW_DMA_PRD_TABLE_LEN; + val = FIELD_PREP(THC_M_PRT_SW_DMA_PRD_TABLE_LEN_THC_M_PRT_SW_DMA_PRD_TABLE_LEN, + *prd_tbl_len); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SW_DMA_PRD_TABLE_LEN_OFFSET, + mask, val); + } + + if (write_len <= sizeof(u32)) { + for (int i = 0; i < write_len; i++) + data0 |= *(((u8 *)write_buff) + i) << (i * 8); + + regmap_write(dev->thc_regmap, THC_M_PRT_SW_SEQ_DATA0_ADDR_OFFSET, data0); + } else if (write_len <= 2 * sizeof(u32)) { + data0 = *(u32 *)write_buff; + regmap_write(dev->thc_regmap, THC_M_PRT_SW_SEQ_DATA0_ADDR_OFFSET, data0); + + for (int i = 0; i < write_len - sizeof(u32); i++) + data1 |= *(((u8 *)write_buff) + sizeof(u32) + i) << (i * 8); + + regmap_write(dev->thc_regmap, THC_M_PRT_SW_SEQ_DATA1_OFFSET, data1); + } + dma_set_start_bit(dev, &dev->dma_ctx->dma_config[THC_SWDMA]); + + return 0; +} + +static int thc_swdma_read_completion(struct thc_device *dev) +{ + int ret; + + ret = thc_wait_for_dma_pause(dev, THC_SWDMA); + if (ret) + return ret; + + thc_reset_dma_settings(dev); + + dma_set_start_bit(dev, &dev->dma_ctx->dma_config[THC_RXDMA2]); + + ret = thc_interrupt_quiesce(dev, false); + + return ret; +} + +/** + * thc_swdma_read - Use software DMA to read data from touch device + * + * @dev: The pointer of THC private device context + * @write_buff: The pointer of write buffer for SWDMA sequence + * @write_len: The write data length for SWDMA sequence + * @prd_tbl_len: The prd table length of SWDMA engine, can be set to NULL + * @read_buff: The pointer of the read data buffer + * @read_len: The pointer of the read data length + * + * Return: 0 on success, other error codes on failed. + */ +int thc_swdma_read(struct thc_device *dev, void *write_buff, size_t write_len, + u32 *prd_tbl_len, void *read_buff, size_t *read_len) +{ + int ret; + + if (!(&dev->dma_ctx->dma_config[THC_SWDMA])->is_enabled) { + dev_err_once(dev->dev, "The SWDMA channel is not enabled"); + return -EINVAL; + } + + if (!read_buff || !read_len) { + dev_err(dev->dev, "Invalid input parameters, read_buff %p, read_len %p\n", + read_buff, read_len); + return -EINVAL; + } + + if (mutex_lock_interruptible(&dev->thc_bus_lock)) + return -EINTR; + + dev->swdma_done = false; + + ret = thc_swdma_read_start(dev, write_buff, write_len, prd_tbl_len); + if (ret) + goto end; + + ret = wait_event_interruptible_timeout(dev->swdma_complete_wait, dev->swdma_done, 1 * HZ); + if (ret <= 0 || !dev->swdma_done) { + dev_err_once(dev->dev, "timeout for waiting SWDMA completion\n"); + ret = -ETIMEDOUT; + goto end; + } + + ret = thc_dma_read(dev, &dev->dma_ctx->dma_config[THC_SWDMA], read_buff, read_len, NULL); + if (ret) + goto end; + + ret = thc_swdma_read_completion(dev); + +end: + mutex_unlock(&dev->thc_bus_lock); + return ret; +} +EXPORT_SYMBOL_NS_GPL(thc_swdma_read, "INTEL_THC"); + +static int write_dma_buffer(struct thc_device *dev, + void *buffer, size_t buf_len) +{ + struct thc_dma_configuration *write_config = &dev->dma_ctx->dma_config[THC_TXDMA]; + struct thc_prd_table *prd_tbl; + struct scatterlist *sg; + unsigned long len_left; + size_t ret; + u8 nent; + int i; + + /* There is only one PRD table for write */ + prd_tbl = &write_config->prd_tbls[0]; + + if (calc_prd_entries_num(prd_tbl, buf_len, &nent) < 0) { + dev_err(dev->dev, "Tx message length too big (%zu)\n", buf_len); + return -EOVERFLOW; + } + + sg = write_config->sgls[0]; + ret = sg_copy_from_buffer(sg, nent, buffer, buf_len); + if (ret != buf_len) { + dev_err_once(dev->dev, "Copied %zu bytes instead of requested %zu\n", + ret, buf_len); + return -EIO; + } + + prd_tbl = &write_config->prd_tbls[0]; + len_left = buf_len; + + for_each_sg(write_config->sgls[0], sg, write_config->sgls_nent[0], i) { + if (sg_dma_address(sg) == 0 || sg_dma_len(sg) == 0) { + dev_err_once(dev->dev, "SGList: zero address or length\n"); + return -EINVAL; + } + + prd_tbl->entries[i].dest_addr = + sg_dma_address(sg) >> THC_ADDRESS_SHIFT; + + if (len_left < sg_dma_len(sg)) { + prd_tbl->entries[i].len = len_left; + prd_tbl->entries[i].end_of_prd = 1; + break; + } + + prd_tbl->entries[i].len = sg_dma_len(sg); + prd_tbl->entries[i].end_of_prd = 0; + + len_left -= sg_dma_len(sg); + } + + dma_set_prd_control(dev, i, 0, write_config); + + return 0; +} + +static void thc_ensure_performance_limitations(struct thc_device *dev) +{ + unsigned long delay_usec = 0; + /* + * Minimum amount of delay the THC / QUICKSPI driver must wait + * between end of write operation and begin of read operation. + * This value shall be in 10us multiples. + */ + if (dev->perf_limit > 0) { + delay_usec = dev->perf_limit * 10; + udelay(delay_usec); + } +} + +static void thc_dma_write_completion(struct thc_device *dev) +{ + thc_ensure_performance_limitations(dev); +} + +/** + * thc_dma_write - Use TXDMA to write data to touch device + * + * @dev: The pointer of THC private device context + * @buffer: The pointer of write data buffer + * @buf_len: The write data length + * + * Return: 0 on success, other error codes on failed. + */ +int thc_dma_write(struct thc_device *dev, void *buffer, size_t buf_len) +{ + bool restore_interrupts = false; + u32 sts, ctrl; + int ret; + + if (!(&dev->dma_ctx->dma_config[THC_TXDMA])->is_enabled) { + dev_err_once(dev->dev, "The TxDMA channel is not enabled\n"); + return -EINVAL; + } + + if (!buffer || buf_len <= 0) { + dev_err(dev->dev, "Invalid input parameters, buffer %p\n, buf_len %zu\n", + buffer, buf_len); + return -EINVAL; + } + + regmap_read(dev->thc_regmap, THC_M_PRT_WRITE_INT_STS_OFFSET, &sts); + if (sts & THC_M_PRT_WRITE_INT_STS_THC_WRDMA_ACTIVE) { + dev_err_once(dev->dev, "THC TxDMA is till active and can't start again\n"); + return -EBUSY; + } + + if (mutex_lock_interruptible(&dev->thc_bus_lock)) + return -EINTR; + + regmap_read(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, &ctrl); + + ret = write_dma_buffer(dev, buffer, buf_len); + if (ret) + goto end; + + if (dev->perf_limit && !(ctrl & THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_HW_STS)) { + ret = thc_interrupt_quiesce(dev, true); + if (ret) + goto end; + + restore_interrupts = true; + } + + dev->write_done = false; + + dma_set_start_bit(dev, &dev->dma_ctx->dma_config[THC_TXDMA]); + + ret = wait_event_interruptible_timeout(dev->write_complete_wait, dev->write_done, 1 * HZ); + if (ret <= 0 || !dev->write_done) { + dev_err_once(dev->dev, "timeout for waiting TxDMA completion\n"); + ret = -ETIMEDOUT; + goto end; + } + + thc_dma_write_completion(dev); + mutex_unlock(&dev->thc_bus_lock); + return 0; + +end: + mutex_unlock(&dev->thc_bus_lock); + + if (restore_interrupts) + ret = thc_interrupt_quiesce(dev, false); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(thc_dma_write, "INTEL_THC"); diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h new file mode 100644 index 000000000000..ca923ff2bef9 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#ifndef _INTEL_THC_DMA_H_ +#define _INTEL_THC_DMA_H_ + +#include <linux/bits.h> +#include <linux/dma-mapping.h> +#include <linux/sizes.h> +#include <linux/time64.h> +#include <linux/types.h> + +#define THC_POINTER_MASK GENMASK(6, 0) +#define THC_POINTER_WRAPAROUND 0x80 +#define THC_WRAPAROUND_VALUE_ODD 0x10 +#define THC_WRAPAROUND_VALUE_EVEN 0x90 +#define THC_MIN_BYTES_PER_SG_LIST_ENTRY SZ_4K + +#define THC_DEFAULT_RXDMA_POLLING_US_INTERVAL 100 +#define THC_DEFAULT_RXDMA_POLLING_US_TIMEOUT (10 * USEC_PER_MSEC) + +/* + * THC needs 1KB aligned address, dest_addr is 54 bits, not 64, + * so don't need to send the lower 10-bits of address. + */ +#define THC_ADDRESS_SHIFT 10 + +/** + * THC DMA channels: + * @THC_RXDMA1: legacy channel, reserved for raw data reading + * @THC_RXDMA2: DMA to read HID data from touch device + * @THC_TXDMA: DMA to write to touch device + * @THC_SWDMA: SW triggered DMA to write and read from touch device + */ +enum thc_dma_channel { + THC_RXDMA1 = 0, + THC_RXDMA2 = 1, + THC_TXDMA = 2, + THC_SWDMA = 3, + MAX_THC_DMA_CHANNEL +}; + +/** + * THC DMA Physical Memory Descriptor (PRD) + * @dest_addr: bit[53:0], destination address in system memory + * @int_on_completion: bit[63], if set, thc will trigger interrupt to driver + * @len: bit[87:64], length of this entry + * @end_of_prd: bit[88], if set, this entry is last one of current PRD table + * @hw_status: bit[90:89], hw status bits + */ +struct thc_prd_entry { + u64 dest_addr : 54; + u64 reserved1 : 9; + u64 int_on_completion : 1; + u64 len : 24; + u64 end_of_prd : 1; + u64 hw_status : 2; + u64 reserved2 : 37; +}; + +/* + * Max OS memory fragmentation will be at a 4KB boundary, thus to address 1MB + * of virtually contiguous memory 256 PRD entries are required for a single + * PRD Table. SW writes the number of PRD Entries for each PRD table in the + * THC_M_PRT_RPRD_CNTRL.PTEC register field. The PRD entry's length must be + * multiple of 4KB except for the last entry in a PRD table. + * This is the max possible number of etries supported by HW, in practise we + * there will be less entries in each prd table(the actual number will be + * given by scatter-gather list allocation). + */ +#define PRD_ENTRIES_NUM 16 + +/* + * Number of PRD tables equals to number of data buffers. + * The max number of PRD tables supported by the HW is 128, + * but we allocate only 16. + */ +#define PRD_TABLES_NUM 16 + +/* THC DMA Physical Memory Descriptor Table */ +struct thc_prd_table { + struct thc_prd_entry entries[PRD_ENTRIES_NUM]; +}; + +#define PRD_TABLE_SIZE sizeof(struct thc_prd_table) + +/** + * struct thc_dma_configuration - THC DMA configure + * @dma_channel: DMA channel for current DMA configuration + * @prd_tbls_dma_handle: DMA buffer handle + * @dir: direction of DMA for this config + * @prd_tbls: PRD tables for current DMA + * @sgls: array of pointers to scatter-gather lists + * @sgls_nent: actual number of entries per sg list + * @prd_tbl_num: actual number of PRD tables + * @max_packet_size: size of the buffer needed for 1 DMA message (1 PRD table) + * @prd_base_addr_high: High 32bits memory address where stores PRD table + * @prd_base_addr_low: low 32bits memory address where stores PRD table + * @prd_cntrl: PRD control register value + * @dma_cntrl: DMA control register value + */ +struct thc_dma_configuration { + enum thc_dma_channel dma_channel; + dma_addr_t prd_tbls_dma_handle; + enum dma_data_direction dir; + bool is_enabled; + + struct thc_prd_table *prd_tbls; + struct scatterlist *sgls[PRD_TABLES_NUM]; + u8 sgls_nent[PRD_TABLES_NUM]; + u8 prd_tbl_num; + + size_t max_packet_size; + u32 prd_base_addr_high; + u32 prd_base_addr_low; + u32 prd_cntrl; + u32 dma_cntrl; +}; + +/* + * THC DMA context + * Store all THC Channel configures + */ +struct thc_dma_context { + struct thc_dma_configuration dma_config[MAX_THC_DMA_CHANNEL]; + u8 use_write_interrupts; +}; + +struct thc_device; + +int thc_dma_set_max_packet_sizes(struct thc_device *dev, + size_t mps_read1, size_t mps_read2, + size_t mps_write, size_t mps_swdma); +int thc_dma_allocate(struct thc_device *dev); +int thc_dma_configure(struct thc_device *dev); +void thc_dma_unconfigure(struct thc_device *dev); +void thc_dma_release(struct thc_device *dev); +int thc_rxdma_read(struct thc_device *dev, enum thc_dma_channel dma_channel, + void *read_buff, size_t *read_len, int *read_finished); +int thc_swdma_read(struct thc_device *dev, void *write_buff, size_t write_len, + u32 *prd_tbl_len, void *read_buff, size_t *read_len); +int thc_dma_write(struct thc_device *dev, void *buffer, size_t buf_len); + +struct thc_dma_context *thc_dma_init(struct thc_device *dev); + +#endif /* _INTEL_THC_DMA_H_ */ diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h new file mode 100644 index 000000000000..6729c4c25dab --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h @@ -0,0 +1,881 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#ifndef _INTEL_THC_HW_H_ +#define _INTEL_THC_HW_H_ + +#include <linux/bits.h> + +/* THC registers offset */ +/* Touch Host Controller Control Register */ +#define THC_M_PRT_CONTROL_OFFSET 0x1008 +/* THC SPI Bus Configuration Register */ +#define THC_M_PRT_SPI_CFG_OFFSET 0x1010 +/* THC SPI Bus Read Opcode Register */ +#define THC_M_PRT_SPI_ICRRD_OPCODE_OFFSET 0x1014 +/* THC SPI Bus Read Opcode Register */ +#define THC_M_PRT_SPI_DMARD_OPCODE_OFFSET 0x1018 +/* THC SPI Bus Write Opcode Register */ +#define THC_M_PRT_SPI_WR_OPCODE_OFFSET 0x101C +/* THC Interrupt Enable Register */ +#define THC_M_PRT_INT_EN_OFFSET 0x1020 +/* THC Interrupt Status Register */ +#define THC_M_PRT_INT_STATUS_OFFSET 0x1024 +/* THC Error Cause Register */ +#define THC_M_PRT_ERR_CAUSE_OFFSET 0x1028 +/* THC SW sequencing Control */ +#define THC_M_PRT_SW_SEQ_CNTRL_OFFSET 0x1040 +/* THC SW sequencing Status */ +#define THC_M_PRT_SW_SEQ_STS_OFFSET 0x1044 +/* THC SW Sequencing Data DW0 or SPI Address Register */ +#define THC_M_PRT_SW_SEQ_DATA0_ADDR_OFFSET 0x1048 +/* THC SW sequencing Data DW1 */ +#define THC_M_PRT_SW_SEQ_DATA1_OFFSET 0x104C +/* THC SW sequencing Data DW2 */ +#define THC_M_PRT_SW_SEQ_DATA2_OFFSET 0x1050 +/* THC SW sequencing Data DW3 */ +#define THC_M_PRT_SW_SEQ_DATA3_OFFSET 0x1054 +/* THC SW sequencing Data DW4 */ +#define THC_M_PRT_SW_SEQ_DATA4_OFFSET 0x1058 +/* THC SW sequencing Data DW5 */ +#define THC_M_PRT_SW_SEQ_DATA5_OFFSET 0x105C +/* THC SW sequencing Data DW6 */ +#define THC_M_PRT_SW_SEQ_DATA6_OFFSET 0x1060 +/* THC SW sequencing Data DW7 */ +#define THC_M_PRT_SW_SEQ_DATA7_OFFSET 0x1064 +/* THC SW sequencing Data DW8 */ +#define THC_M_PRT_SW_SEQ_DATA8_OFFSET 0x1068 +/* THC SW sequencing Data DW9 */ +#define THC_M_PRT_SW_SEQ_DATA9_OFFSET 0x106C +/* THC SW sequencing Data DW10 */ +#define THC_M_PRT_SW_SEQ_DATA10_OFFSET 0x1070 +/* THC SW sequencing Data DW11 */ +#define THC_M_PRT_SW_SEQ_DATA11_OFFSET 0x1074 +/* THC SW sequencing Data DW12 */ +#define THC_M_PRT_SW_SEQ_DATA12_OFFSET 0x1078 +/* THC SW sequencing Data DW13 */ +#define THC_M_PRT_SW_SEQ_DATA13_OFFSET 0x107C +/* THC SW sequencing Data DW14 */ +#define THC_M_PRT_SW_SEQ_DATA14_OFFSET 0x1080 +/* THC SW sequencing Data DW15 */ +#define THC_M_PRT_SW_SEQ_DATA15_OFFSET 0x1084 +/* THC SW sequencing Data DW16 */ +#define THC_M_PRT_SW_SEQ_DATA16_OFFSET 0x1088 +/* THC Write PRD Base Address Register Low */ +#define THC_M_PRT_WPRD_BA_LOW_OFFSET 0x1090 +/* THC Write PRD Base Address Register High */ +#define THC_M_PRT_WPRD_BA_HI_OFFSET 0x1094 +/* THC Write DMA Control */ +#define THC_M_PRT_WRITE_DMA_CNTRL_OFFSET 0x1098 +/* THC Write Interrupt Status */ +#define THC_M_PRT_WRITE_INT_STS_OFFSET 0x109C +/* THC Write DMA Error Register */ +#define THC_M_PRT_WRITE_DMA_ERR_OFFSET 0x10A0 +/* THC device address for the bulk write */ +#define THC_M_PRT_WR_BULK_ADDR_OFFSET 0x10B4 +/* THC Device Interrupt Cause Register Address */ +#define THC_M_PRT_DEV_INT_CAUSE_ADDR_OFFSET 0x10B8 +/* THC Device Interrupt Cause Register Value */ +#define THC_M_PRT_DEV_INT_CAUSE_REG_VAL_OFFSET 0x10BC +/* THC TXDMA Frame Count */ +#define THC_M_PRT_TX_FRM_CNT_OFFSET 0x10E0 +/* THC TXDMA Packet Count */ +#define THC_M_PRT_TXDMA_PKT_CNT_OFFSET 0x10E4 +/* THC Device Interrupt Count on this port */ +#define THC_M_PRT_DEVINT_CNT_OFFSET 0x10E8 +/* Touch Device Interrupt Cause register Format Configuration Register 1 */ +#define THC_M_PRT_DEVINT_CFG_1_OFFSET 0x10EC +/* Touch Device Interrupt Cause register Format Configuration Register 2 */ +#define THC_M_PRT_DEVINT_CFG_2_OFFSET 0x10F0 +/* THC Read PRD Base Address Low for the 1st RXDMA */ +#define THC_M_PRT_RPRD_BA_LOW_1_OFFSET 0x1100 +/* THC Read PRD Base Address High for the 1st RXDMA */ +#define THC_M_PRT_RPRD_BA_HI_1_OFFSET 0x1104 +/* THC Read PRD Control for the 1st RXDMA */ +#define THC_M_PRT_RPRD_CNTRL_1_OFFSET 0x1108 +/* THC Read DMA Control for the 1st RXDMA */ +#define THC_M_PRT_READ_DMA_CNTRL_1_OFFSET 0x110C +/* THC Read Interrupt Status for the 1st RXDMA */ +#define THC_M_PRT_READ_DMA_INT_STS_1_OFFSET 0x1110 +/* THC Read DMA Error Register for the 1st RXDMA */ +#define THC_M_PRT_READ_DMA_ERR_1_OFFSET 0x1114 +/* Touch Sequencer GuC Tail Offset Address Low for the 1st RXDMA */ +#define THC_M_PRT_GUC_OFFSET_LOW_1_OFFSET 0x1118 +/* Touch Sequencer GuC Tail Offset Address High for the 1st RXDMA */ +#define THC_M_PRT_GUC_OFFSET_HI_1_OFFSET 0x111C +/* Touch Host Controller GuC Work Queue Item Size for the 1st RXDMA */ +#define THC_M_PRT_GUC_WORKQ_ITEM_SZ_1_OFFSET 0x1120 +/* Touch Host Controller GuC Control register for the 1st RXDMA */ +#define THC_M_PRT_GUC_WORKQ_SZ_1_OFFSET 0x1124 +/* Touch Sequencer Control for the 1st DMA */ +#define THC_M_PRT_TSEQ_CNTRL_1_OFFSET 0x1128 +/* Touch Sequencer GuC Doorbell Address Low for the 1st RXDMA */ +#define THC_M_PRT_GUC_DB_ADDR_LOW_1_OFFSET 0x1130 +/* Touch Sequencer GuC Doorbell Address High for the 1st RXDMA */ +#define THC_M_PRT_GUC_DB_ADDR_HI_1_OFFSET 0x1134 +/* Touch Sequencer GuC Doorbell Data */ +#define THC_M_PRT_GUC_DB_DATA_1_OFFSET 0x1138 +/* Touch Sequencer GuC Tail Offset Initial Value for the 1st RXDMA */ +#define THC_M_PRT_GUC_OFFSET_INITVAL_1_OFFSET 0x1140 +/* THC Device Address for the bulk/touch data read for the 1st RXDMA */ +#define THC_M_PRT_RD_BULK_ADDR_1_OFFSET 0x1170 +/* THC Gfx/SW Doorbell Count from the 1st Stream RXDMA on this port */ +#define THC_M_PRT_DB_CNT_1_OFFSET 0x11A0 +/* THC Frame Count from the 1st Stream RXDMA on this port */ +#define THC_M_PRT_FRM_CNT_1_OFFSET 0x11A4 +/* THC Micro Frame Count from the 1st Stream RXDMA on this port */ +#define THC_M_PRT_UFRM_CNT_1_OFFSET 0x11A8 +/* THC Packet Count from the 1st Stream RXDMA on this port */ +#define THC_M_PRT_RXDMA_PKT_CNT_1_OFFSET 0x11AC +/* + * THC Software Interrupt Count from the 1st Stream RXDMA + * on this port + */ +#define THC_M_PRT_SWINT_CNT_1_OFFSET 0x11B0 +/* Touch Sequencer Frame Drop Counter for the 1st RXDMA */ +#define THC_M_PRT_FRAME_DROP_CNT_1_OFFSET 0x11B4 +/* THC Coaescing 1 */ +#define THC_M_PRT_COALESCE_1_OFFSET 0x11B8 +/* THC Read PRD Base Address Low for the 2nd RXDMA */ +#define THC_M_PRT_RPRD_BA_LOW_2_OFFSET 0x1200 +/* THC Read PRD Base Address High for the 2nd RXDMA */ +#define THC_M_PRT_RPRD_BA_HI_2_OFFSET 0x1204 +/* THC Read PRD Control for the 2nd RXDMA */ +#define THC_M_PRT_RPRD_CNTRL_2_OFFSET 0x1208 +/* THC Read DMA Control for the 2nd RXDMA */ +#define THC_M_PRT_READ_DMA_CNTRL_2_OFFSET 0x120C +/* THC Read Interrupt Status for the 2nd RXDMA */ +#define THC_M_PRT_READ_DMA_INT_STS_2_OFFSET 0x1210 +/* THC Read DMA Error Register for the 2nd RXDMA */ +#define THC_M_PRT_READ_DMA_ERR_2_OFFSET 0x1214 +/* Touch Sequencer GuC Tail Offset Address Low for the 2nd RXDMA */ +#define THC_M_PRT_GUC_OFFSET_LOW_2_OFFSET 0x1218 +/* Touch Sequencer GuC Tail Offset Address High for the 2nd RXDMA */ +#define THC_M_PRT_GUC_OFFSET_HI_2_OFFSET 0x121C +/* Touch Host Controller GuC Work Queue Item Size for the 2nd RXDMA */ +#define THC_M_PRT_GUC_WORKQ_ITEM_SZ_2_OFFSET 0x1220 +/* Touch Host Controller GuC Control register for the 2nd RXDMA */ +#define THC_M_PRT_GUC_WORKQ_SZ_2_OFFSET 0x1224 +/* Touch Sequencer Control for the 2nd DMA */ +#define THC_M_PRT_TSEQ_CNTRL_2_OFFSET 0x1228 +/* Touch Sequencer GuC Doorbell Address Low for the 2nd RXDMA */ +#define THC_M_PRT_GUC_DB_ADDR_LOW_2_OFFSET 0x1230 +/* Touch Sequencer GuC Doorbell Address High for the 2nd RXDMA */ +#define THC_M_PRT_GUC_DB_ADDR_HI_2_OFFSET 0x1234 +/* Touch Sequencer GuC Doorbell Data for PRD2 */ +#define THC_M_PRT_GUC_DB_DATA_2_OFFSET 0x1238 +/* Touch Sequencer GuC Tail Offset Initial Value for the 2nd RXDMA */ +#define THC_M_PRT_GUC_OFFSET_INITVAL_2_OFFSET 0x1240 +/* THC Device Address for the bulk/touch data read for the 2nd RXDMA */ +#define THC_M_PRT_RD_BULK_ADDR_2_OFFSET 0x1270 +/* THC Gfx/SW Doorbell Count from the 2nd Stream RXDMA on this port */ +#define THC_M_PRT_DB_CNT_2_OFFSET 0x12A0 +/* THC Frame Count from the 2nd Stream RXDMA on this port */ +#define THC_M_PRT_FRM_CNT_2_OFFSET 0x12A4 +/* THC Micro Frame Count from the 2nd Stream RXDMA on this port */ +#define THC_M_PRT_UFRM_CNT_2_OFFSET 0x12A8 +/* THC Packet Count from the 2nd Stream RXDMA on this port */ +#define THC_M_PRT_RXDMA_PKT_CNT_2_OFFSET 0x12AC +/* + * THC Software Interrupt Count from the 2nd Stream RXDMA + * on this port + */ +#define THC_M_PRT_SWINT_CNT_2_OFFSET 0x12B0 +/* Touch Sequencer Frame Drop Counter for the 2nd RXDMA */ +#define THC_M_PRT_FRAME_DROP_CNT_2_OFFSET 0x12B4 +/* THC Coaescing 2 */ +#define THC_M_PRT_COALESCE_2_OFFSET 0x12B8 +/* THC SPARE REGISTER */ +#define THC_M_PRT_SPARE_REG_OFFSET 0x12BC +/* THC Read PRD Base Address Low for the SW RXDMA */ +#define THC_M_PRT_RPRD_BA_LOW_SW_OFFSET 0x12C0 +/* THC Read PRD Base Address High for the SW RXDMA */ +#define THC_M_PRT_RPRD_BA_HI_SW_OFFSET 0x12C4 +/* THC Read PRD Control for the SW RXDMA */ +#define THC_M_PRT_RPRD_CNTRL_SW_OFFSET 0x12C8 +/* THC Read DMA Control for the SW RXDMA */ +#define THC_M_PRT_READ_DMA_CNTRL_SW_OFFSET 0x12CC +/* THC Read Interrupt Status for the SW RXDMA */ +#define THC_M_PRT_READ_DMA_INT_STS_SW_OFFSET 0x12D0 +/* Touch Sequencer Control for the SW DMA */ +#define THC_M_PRT_TSEQ_CNTRL_SW_OFFSET 0x12D4 +/* Address for the bulk read for SW DMA engine */ +#define THC_M_PRT_RD_BULK_ADDR_SW_OFFSET 0x12D8 +/* THC Frame Count from the SW RXDMA on this port */ +#define THC_M_PRT_FRM_CNT_SW_OFFSET 0x12DC +/* THC Packet Count from the SW RXDMA on this port */ +#define THC_M_PRT_RXDMA_PKT_CNT_SW_OFFSET 0x12E0 +/* SW DMA PRD Table Length */ +#define THC_M_PRT_SW_DMA_PRD_TABLE_LEN_OFFSET 0x12E4 +/* THC timing based Frame/Interrupt caolescing control register for 1st RXDMA */ +#define THC_M_PRT_COALESCE_CNTRL_1_OFFSET 0x12E8 +/* THC timing based Frame/Interrupt caolescing control register for 2nd RXDMA */ +#define THC_M_PRT_COALESCE_CNTRL_2_OFFSET 0x12EC +/* Touch Sequencer PRD Table Empty Counter for the 1st RXDMA */ +#define THC_M_PRT_PRD_EMPTY_CNT_1_OFFSET 0x12F0 +/* Touch Sequencer PRD Table Empty Counter for the 2nd RXDM */ +#define THC_M_PRT_PRD_EMPTY_CNT_2_OFFSET 0x12F4 +/* THC coalescing status to reflect the current coalescing FSM state for 1st RXDMA */ +#define THC_M_PRT_COALESCE_STS_1_OFFSET 0x12F8 +/* THC coalescing status to reflect the current coalescing FSM state for 2nd RXDMA */ +#define THC_M_PRT_COALESCE_STS_2_OFFSET 0x12FC +/* THC Register for the SPI Port Duty Cycle Configuration */ +#define THC_M_PRT_SPI_DUTYC_CFG_OFFSET 0x1300 +/* THC Register for SW I2C Wtite Sequecning control */ +#define THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_OFFSET 0x1304 +/* THC current Timestamp Register for RXDMA1 */ +#define THC_M_PRT_TIMESTAMP_1_OFFSET 0x1308 +/* THC current Timestamp Register for RXDMA2 */ +#define THC_M_PRT_TIMESTAMP_2_OFFSET 0x130C +/* Current SYNC Event Timestamp Register */ +#define THC_M_PRT_SYNC_TIMESTAMP_OFFSET 0x1310 +/* THC Display Sync Register */ +#define THC_M_PRT_DISP_SYNC_OFFSET 0x1314 +/* THC Display Sync Register */ +#define THC_M_PRT_DISP_SYNC_2_OFFSET 0x1318 +/* THC Register for SW I2C Wtite Sequecning control */ +#define THC_M_PRT_I2C_CFG_OFFSET 0x131C + +/* THC register bits definition */ +#define TXN_ERR_INT_STS_BIT BIT(28) +#define TXN_FATAL_INT_STS_BIT BIT(30) + +#define NONDMA_INT_STS_BIT BIT(4) +#define EOF_INT_STS_BIT BIT(5) + +#define THC_CFG_DID_VID_VID GENMASK(15, 0) +#define THC_CFG_DID_VID_DID GENMASK(31, 16) + +#define THC_CFG_STS_CMD_IOSE BIT(0) +#define THC_CFG_STS_CMD_MSE BIT(1) +#define THC_CFG_STS_CMD_BME BIT(2) +#define THC_CFG_STS_CMD_SPCYC BIT(3) +#define THC_CFG_STS_CMD_MWRIEN BIT(4) +#define THC_CFG_STS_CMD_VGAPS BIT(5) +#define THC_CFG_STS_CMD_PERRR BIT(6) +#define THC_CFG_STS_CMD_SERREN BIT(8) +#define THC_CFG_STS_CMD_FBTBEN BIT(9) +#define THC_CFG_STS_CMD_INTD BIT(10) +#define THC_CFG_STS_CMD_INTS BIT(19) +#define THC_CFG_STS_CMD_CAPL BIT(20) +#define THC_CFG_STS_CMD_MCAP BIT(21) +#define THC_CFG_STS_CMD_FBTBC BIT(23) +#define THC_CFG_STS_CMD_MDPE BIT(24) +#define THC_CFG_STS_CMD_DEVT GENMASK(26, 25) +#define THC_CFG_STS_CMD_STA BIT(27) +#define THC_CFG_STS_CMD_RTA BIT(28) +#define THC_CFG_STS_CMD_RMA BIT(29) +#define THC_CFG_STS_CMD_SSE BIT(30) +#define THC_CFG_STS_CMD_DPE BIT(31) + +#define THC_CFG_CC_RID_RID GENMASK(7, 0) +#define THC_CFG_CC_RID_PI GENMASK(15, 8) +#define THC_CFG_CC_RID_SCC GENMASK(23, 16) +#define THC_CFG_CC_RID_BCC GENMASK(31, 24) + +#define THC_CFG_BIST_HTYPE_LT_CLS_CLSZ GENMASK(7, 0) +#define THC_CFG_BIST_HTYPE_LT_CLS_LT GENMASK(15, 8) +#define THC_CFG_BIST_HTYPE_LT_CLS_HTYPE GENMASK(22, 16) +#define THC_CFG_BIST_HTYPE_LT_CLS_MFD BIT(23) + +#define THC_CFG_BAR0_LOW_MEMSPACE BIT(0) +#define THC_CFG_BAR0_LOW_TYP GENMASK(2, 1) +#define THC_CFG_BAR0_LOW_PREFETCH BIT(3) +#define THC_CFG_BAR0_LOW_MEMSIZE GENMASK(14, 4) +#define THC_CFG_BAR0_LOW_MEMBAR GENMASK(31, 15) +#define THC_CFG_BAR0_HI_MEMBAR GENMASK(31, 0) + +#define THC_CFG_SID_SVID_SSVID GENMASK(15, 0) +#define THC_CFG_SID_SVID_SSID GENMASK(31, 16) + +#define THC_CFG_CAPP_CP GENMASK(7, 0) + +#define THC_CFG_INT_ILINE GENMASK(7, 0) +#define THC_CFG_INT_IPIN GENMASK(15, 8) + +#define THC_CFG_UR_STS_CTL_URRE BIT(0) +#define THC_CFG_UR_STS_CTL_URD BIT(1) +#define THC_CFG_UR_STS_CTL_FD BIT(2) + +#define THC_CFG_MSIMC_MSINP_MSICID_CAPID GENMASK(7, 0) +#define THC_CFG_MSIMC_MSINP_MSICID_NXTP GENMASK(15, 8) +#define THC_CFG_MSIMC_MSINP_MSICID_MSIE BIT(16) +#define THC_CFG_MSIMC_MSINP_MSICID_MMC GENMASK(19, 17) +#define THC_CFG_MSIMC_MSINP_MSICID_MMEN GENMASK(22, 20) +#define THC_CFG_MSIMC_MSINP_MSICID_XAC BIT(23) +#define THC_CFG_MSIMC_MSINP_MSICID_PVMC BIT(24) +#define THC_CFG_MSIMA_MADDR GENMASK(31, 2) +#define THC_CFG_MSIMUA_MAUDDR GENMASK(31, 0) +#define THC_CFG_MSIMD_MDAT GENMASK(15, 0) + +#define THC_CFG_PMCAP_PMNP_PMCID_CAPP GENMASK(7, 0) +#define THC_CFG_PMCAP_PMNP_PMCID_NXTP GENMASK(15, 8) +#define THC_CFG_PMCAP_PMNP_PMCID_VER GENMASK(18, 16) +#define THC_CFG_PMCAP_PMNP_PMCID_PMECLK BIT(19) +#define THC_CFG_PMCAP_PMNP_PMCID_DSI BIT(21) +#define THC_CFG_PMCAP_PMNP_PMCID_AUXC GENMASK(24, 22) +#define THC_CFG_PMCAP_PMNP_PMCID_D1S BIT(25) +#define THC_CFG_PMCAP_PMNP_PMCID_D2S BIT(26) +#define THC_CFG_PMCAP_PMNP_PMCID_PMES GENMASK(31, 27) + +#define THC_CFG_PMD_PMCSRBSE_PMCSR_PWRST GENMASK(1, 0) +#define THC_CFG_PMD_PMCSRBSE_PMCSR_NSR BIT(3) +#define THC_CFG_PMD_PMCSRBSE_PMCSR_PMEEN BIT(8) +#define THC_CFG_PMD_PMCSRBSE_PMCSR_DSEL GENMASK(12, 9) +#define THC_CFG_PMD_PMCSRBSE_PMCSR_DS GENMASK(14, 13) +#define THC_CFG_PMD_PMCSRBSE_PMCSR_PMESTS BIT(15) + +#define THC_CFG_DEVIDLE_CAPPID GENMASK(7, 0) +#define THC_CFG_DEVIDLE_NCAPPP GENMASK(15, 8) +#define THC_CFG_DEVIDLE_LENGTH GENMASK(23, 16) +#define THC_CFG_DEVIDLE_REV GENMASK(27, 24) +#define THC_CFG_DEVIDLE_VID GENMASK(31, 28) + +#define THC_CFG_VSHDR_VSECID GENMASK(15, 0) +#define THC_CFG_VSHDR_VSECR GENMASK(19, 16) +#define THC_CFG_VSHDR_VSECL GENMASK(31, 20) + +#define THC_CFG_SWLTRPTR_VALID BIT(0) +#define THC_CFG_SWLTRPTR_BARNUM GENMASK(3, 1) +#define THC_CFG_SWLTRPTR_SWLTRLOC GENMASK(31, 4) + +#define THC_CFG_DEVIDLEPTR_VALID BIT(0) +#define THC_CFG_DEVIDLEPTR_BARNUM GENMASK(3, 1) +#define THC_CFG_DEVIDLEPTR_DEVIDLELOC GENMASK(31, 4) +#define THC_CFG_DEVIDLEPOL_POLV GENMASK(9, 0) +#define THC_CFG_DEVIDLEPOL_POLS GENMASK(12, 10) + +#define THC_CFG_PCE_SPE BIT(0) +#define THC_CFG_PCE_I3E BIT(1) +#define THC_CFG_PCE_D3HE BIT(2) +#define THC_CFG_PCE_SE BIT(3) +#define THC_CFG_PCE_HAE BIT(5) + +#define THC_CFG_MANID_PROC GENMASK(7, 0) +#define THC_CFG_MANID_MID GENMASK(15, 8) +#define THC_CFG_MANID_MSID GENMASK(23, 16) +#define THC_CFG_MANID_DOT GENMASK(27, 24) + +#define THC_M_CMN_DEVIDLECTRL_CIP BIT(0) +#define THC_M_CMN_DEVIDLECTRL_IR BIT(1) +#define THC_M_CMN_DEVIDLECTRL_DEVIDLE BIT(2) +#define THC_M_CMN_DEVIDLECTRL_RR BIT(3) +#define THC_M_CMN_DEVIDLECTRL_IRC BIT(4) + +#define THC_M_CMN_LTR_CTRL_OFFSET 0x14 +#define THC_M_CMN_LTR_CTRL_ACTIVE_LTR_REQ BIT(0) +#define THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN BIT(1) +#define THC_M_CMN_LTR_CTRL_LP_LTR_REQ BIT(2) +#define THC_M_CMN_LTR_CTRL_LP_LTR_EN BIT(3) +#define THC_M_CMN_LTR_CTRL_LP_LTR_SCALE GENMASK(6, 4) +#define THC_M_CMN_LTR_CTRL_LP_LTR_VAL GENMASK(16, 7) +#define THC_M_CMN_LTR_CTRL_ACT_LTR_SCALE GENMASK(19, 17) +#define THC_M_CMN_LTR_CTRL_ACT_LTR_VAL GENMASK(29, 20) +#define THC_M_CMN_LTR_CTRL_LAST_LTR_SENT GENMASK(31, 30) + +#define THC_M_PRT_CONTROL_TSFTRST BIT(0) +#define THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN BIT(1) +#define THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_HW_STS BIT(2) +#define THC_M_PRT_CONTROL_DEVRST BIT(3) +#define THC_M_PRT_CONTROL_THC_DRV_LOCK_EN BIT(13) +#define THC_M_PRT_CONTROL_THC_INSTANCE_INDEX GENMASK(18, 16) +#define THC_M_PRT_CONTROL_PORT_INDEX GENMASK(22, 20) +#define THC_M_PRT_CONTROL_THC_ARB_POLICY GENMASK(25, 24) +#define THC_M_PRT_CONTROL_THC_BIOS_LOCK_EN BIT(27) +#define THC_M_PRT_CONTROL_PORT_SUPPORTED BIT(28) +#define THC_M_PRT_CONTROL_SPI_IO_RDY BIT(29) +#define THC_M_PRT_CONTROL_PORT_TYPE GENMASK(31, 30) + +#define THC_M_PRT_SPI_CFG_SPI_TRDC GENMASK(1, 0) +#define THC_M_PRT_SPI_CFG_SPI_TRMODE GENMASK(3, 2) +#define THC_M_PRT_SPI_CFG_SPI_TCRF GENMASK(6, 4) +#define THC_M_PRT_SPI_CFG_SPI_RD_MPS GENMASK(15, 7) +#define THC_M_PRT_SPI_CFG_SPI_TWMODE GENMASK(19, 18) +#define THC_M_PRT_SPI_CFG_SPI_TCWF GENMASK(22, 20) +#define THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN BIT(23) +#define THC_M_PRT_SPI_CFG_SPI_WR_MPS GENMASK(31, 24) + +#define THC_M_PRT_SPI_ICRRD_OPCODE_SPI_SIO GENMASK(31, 24) +#define THC_M_PRT_SPI_ICRRD_OPCODE_SPI_DIO GENMASK(23, 16) +#define THC_M_PRT_SPI_ICRRD_OPCODE_SPI_QIO GENMASK(15, 8) + +#define THC_M_PRT_INT_EN_SIPE BIT(0) +#define THC_M_PRT_INT_EN_SBO BIT(1) +#define THC_M_PRT_INT_EN_SIDR BIT(2) +#define THC_M_PRT_INT_EN_SOFB BIT(3) +#define THC_M_PRT_INT_EN_INVLD_DEV_ENTRY_INT_EN BIT(9) +#define THC_M_PRT_INT_EN_FRAME_BABBLE_ERR_INT_EN BIT(10) +#define THC_M_PRT_INT_EN_BUF_OVRRUN_ERR_INT_EN BIT(12) +#define THC_M_PRT_INT_EN_PRD_ENTRY_ERR_INT_EN BIT(13) +#define THC_M_PRT_INT_EN_DISP_SYNC_EVT_INT_EN BIT(14) +#define THC_M_PRT_INT_EN_DEV_RAW_INT_EN BIT(15) +#define THC_M_PRT_INT_EN_FATAL_ERR_INT_EN BIT(16) +#define THC_M_PRT_INT_EN_THC_I2C_IC_RX_UNDER_INT_EN BIT(17) +#define THC_M_PRT_INT_EN_THC_I2C_IC_RX_OVER_INT_EN BIT(18) +#define THC_M_PRT_INT_EN_THC_I2C_IC_RX_FULL_INT_EN BIT(19) +#define THC_M_PRT_INT_EN_THC_I2C_IC_TX_OVER_INT_EN BIT(20) +#define THC_M_PRT_INT_EN_THC_I2C_IC_TX_EMPTY_INT_EN BIT(21) +#define THC_M_PRT_INT_EN_THC_I2C_IC_TX_ABRT_INT_EN BIT(22) +#define THC_M_PRT_INT_EN_THC_I2C_IC_SCL_STUCK_AT_LOW_DET_INT_EN BIT(24) +#define THC_M_PRT_INT_EN_THC_I2C_IC_STOP_DET_INT_EN BIT(25) +#define THC_M_PRT_INT_EN_THC_I2C_IC_START_DET_INT_EN BIT(26) +#define THC_M_PRT_INT_EN_THC_I2C_IC_MST_ON_HOLD_INT_EN BIT(27) +#define THC_M_PRT_INT_EN_TXN_ERR_INT_EN BIT(29) +#define THC_M_PRT_INT_EN_GBL_INT_EN BIT(31) + +#define THC_M_PRT_INT_STATUS_DISP_SYNC_EVT_INT_STS BIT(14) +#define THC_M_PRT_INT_STATUS_DEV_RAW_INT_STS BIT(15) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_UNDER_INT_STS BIT(17) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_OVER_INT_STS BIT(18) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_FULL_INT_STS BIT(19) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_OVER_INT_STS BIT(20) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_EMPTY_INT_STS BIT(21) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_ABRT_INT_STS BIT(22) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_ACTIVITY_INT_STS BIT(23) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_SCL_STUCK_AT_LOW_INT_STS BIT(24) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_STOP_DET_INT_STS BIT(25) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_START_DET_INT_STS BIT(26) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_MST_ON_HOLD_INT_STS BIT(27) +#define THC_M_PRT_INT_STATUS_TXN_ERR_INT_STS BIT(28) +#define THC_M_PRT_INT_STATUS_FATAL_ERR_INT_STS BIT(30) + +#define THC_M_PRT_ERR_CAUSE_INVLD_DEV_ENTRY BIT(9) +#define THC_M_PRT_ERR_CAUSE_FRAME_BABBLE_ERR BIT(10) +#define THC_M_PRT_ERR_CAUSE_BUF_OVRRUN_ERR BIT(12) +#define THC_M_PRT_ERR_CAUSE_PRD_ENTRY_ERR BIT(13) +#define THC_M_PRT_ERR_CAUSE_FATAL_ERR_CAUSE GENMASK(23, 16) + +#define THC_M_PRT_SW_SEQ_CNTRL_TSSGO BIT(0) +#define THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CD_IE BIT(1) +#define THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CMD GENMASK(15, 8) +#define THC_M_PRT_SW_SEQ_CNTRL_THC_SS_BC GENMASK(31, 16) +#define THC_M_PRT_SW_SEQ_STS_TSSDONE BIT(0) +#define THC_M_PRT_SW_SEQ_STS_THC_SS_ERR BIT(1) +#define THC_M_PRT_SW_SEQ_STS_THC_SS_CIP BIT(3) +#define THC_M_PRT_SW_SEQ_DATA0_ADDR_THC_SW_SEQ_DATA0_ADDR GENMASK(31, 0) +#define THC_M_PRT_SW_SEQ_DATA1_THC_SW_SEQ_DATA1 GENMASK(31, 0) + +#define THC_M_PRT_WPRD_BA_LOW_THC_M_PRT_WPRD_BA_LOW GENMASK(31, 12) +#define THC_M_PRT_WPRD_BA_HI_THC_M_PRT_WPRD_BA_HI GENMASK(31, 0) + +#define THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_START BIT(0) +#define THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC_ERROR BIT(1) +#define THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC BIT(2) +#define THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC_DMACPL BIT(3) +#define THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_UHS BIT(23) +#define THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_PTEC GENMASK(31, 24) + +#define THC_M_PRT_WRITE_INT_STS_THC_WRDMA_CMPL_STATUS BIT(0) +#define THC_M_PRT_WRITE_INT_STS_THC_WRDMA_ERROR_STS BIT(1) +#define THC_M_PRT_WRITE_INT_STS_THC_WRDMA_IOC_STS BIT(2) +#define THC_M_PRT_WRITE_INT_STS_THC_WRDMA_ACTIVE BIT(3) + +#define THC_M_PRT_WR_BULK_ADDR_THC_M_PRT_WR_BULK_ADDR GENMASK(31, 0) + +#define THC_M_PRT_DEV_INT_CAUSE_ADDR_THC_M_PRT_DEV_INT_CAUSE_ADDR GENMASK(31, 0) +#define THC_M_PRT_DEV_INT_CAUSE_REG_VAL_INTERRUPT_TYPE GENMASK(3, 0) +#define THC_M_PRT_DEV_INT_CAUSE_REG_VAL_MICRO_FRAME_SIZE GENMASK(23, 4) +#define THC_M_PRT_DEV_INT_CAUSE_REG_VAL_BEGINNING_OF_FRAME BIT(29) +#define THC_M_PRT_DEV_INT_CAUSE_REG_VAL_END_OF_FRAME BIT(30) +#define THC_M_PRT_DEV_INT_CAUSE_REG_VAL_FRAME_TYPE BIT(31) + +#define THC_M_PRT_TX_FRM_CNT_THC_M_PRT_TX_FRM_CNT GENMASK(30, 0) +#define THC_M_PRT_TX_FRM_CNT_THC_M_PRT_TX_FRM_CNT_RST BIT(31) + +#define THC_M_PRT_TXDMA_PKT_CNT_THC_M_PRT_TXDMA_PKT_CNT GENMASK(30, 0) +#define THC_M_PRT_TXDMA_PKT_CNT_THC_M_PRT_TXDMA_PKT_CNT_RST BIT(31) + +#define THC_M_PRT_DEVINT_CNT_THC_M_PRT_DEVINT_CNT GENMASK(30, 0) +#define THC_M_PRT_DEVINT_CNT_THC_M_PRT_DEVINT_CNT_RST BIT(31) + +#define THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_OFFSET GENMASK(4, 0) +#define THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_LEN GENMASK(9, 5) +#define THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_EOF_OFFSET GENMASK(14, 10) +#define THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_SEND_ICR_US_EN BIT(15) +#define THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_DATA_VAL GENMASK(31, 16) + +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_OFFSET GENMASK(4, 0) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_LEN GENMASK(9, 5) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_UNIT GENMASK(15, 12) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_IGNORE BIT(16) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_VAL BIT(17) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_RXDMA_ADDRINC_DIS BIT(24) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_TXDMA_ADDRINC_DIS BIT(25) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_RXDMA_PKT_STRM_EN BIT(26) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_TXDMA_PKT_STRM_EN BIT(27) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_DEVINT_POL BIT(28) + +#define THC_M_PRT_RPRD_BA_LOW_1_THC_M_PRT_RPRD_BA_LOW GENMASK(31, 12) +#define THC_M_PRT_RPRD_BA_HI_1_THC_M_PRT_RPRD_BA_HI GENMASK(31, 0) + +#define THC_M_PRT_RPRD_CNTRL_PCD GENMASK(6, 0) +#define THC_M_PRT_RPRD_CNTRL_PTEC GENMASK(15, 8) +#define THC_M_PRT_RPRD_CNTRL_PREFETCH_WM GENMASK(19, 16) + +#define THC_M_PRT_READ_DMA_CNTRL_START BIT(0) +#define THC_M_PRT_READ_DMA_CNTRL_IE_ERROR BIT(1) +#define THC_M_PRT_READ_DMA_CNTRL_IE_IOC BIT(2) +#define THC_M_PRT_READ_DMA_CNTRL_IE_STALL BIT(3) +#define THC_M_PRT_READ_DMA_CNTRL_IE_NDDI BIT(4) +#define THC_M_PRT_READ_DMA_CNTRL_IE_EOF BIT(5) +#define THC_M_PRT_READ_DMA_CNTRL_IE_DMACPL BIT(7) +#define THC_M_PRT_READ_DMA_CNTRL_TPCRP GENMASK(15, 8) +#define THC_M_PRT_READ_DMA_CNTRL_TPCWP GENMASK(23, 16) +#define THC_M_PRT_READ_DMA_CNTRL_INT_SW_DMA_EN BIT(28) +#define THC_M_PRT_READ_DMA_CNTRL_SOO BIT(29) +#define THC_M_PRT_READ_DMA_CNTRL_UHS BIT(30) +#define THC_M_PRT_READ_DMA_CNTRL_TPCPR BIT(31) + +#define THC_M_PRT_READ_DMA_INT_STS_DMACPL_STS BIT(0) +#define THC_M_PRT_READ_DMA_INT_STS_ERROR_STS BIT(1) +#define THC_M_PRT_READ_DMA_INT_STS_IOC_STS BIT(2) +#define THC_M_PRT_READ_DMA_INT_STS_STALL_STS BIT(3) +#define THC_M_PRT_READ_DMA_INT_STS_NONDMA_INT_STS BIT(4) +#define THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS BIT(5) +#define THC_M_PRT_READ_DMA_INT_STS_ACTIVE BIT(8) + +#define THC_M_PRT_READ_DMA_ERR_1_DLERR BIT(0) + +#define THC_M_PRT_GUC_OFFSET_LOW_1_THC_M_PRT_GUC_OFFSET_LOW GENMASK(31, 3) +#define THC_M_PRT_GUC_OFFSET_HI_1_THC_M_PRT_GUC_OFFSET_HI GENMASK(31, 0) +#define THC_M_PRT_GUC_WORKQ_ITEM_SZ_1_WORKQ_ITEM_SZ GENMASK(23, 0) +#define THC_M_PRT_GUC_WORKQ_SZ_1_WORKQ_SZ GENMASK(23, 0) +#define THC_M_PRT_GUC_WORKQ_SZ_1_FCD GENMASK(27, 24) +#define THC_M_PRT_GUC_WORKQ_SZ_1_GIC GENMASK(31, 28) + +#define THC_M_PRT_TSEQ_CNTRL_1_RGD BIT(2) +#define THC_M_PRT_TSEQ_CNTRL_1_EGP BIT(3) +#define THC_M_PRT_TSEQ_CNTRL_1_RTO BIT(4) +#define THC_M_PRT_TSEQ_CNTRL_1_EWOG BIT(5) +#define THC_M_PRT_TSEQ_CNTRL_1_RWOGC BIT(6) +#define THC_M_PRT_TSEQ_CNTRL_1_RX_DATA_FIFO_WR_WM GENMASK(25, 16) +#define THC_M_PRT_TSEQ_CNTRL_1_RESET_PREP_CHICKEN BIT(30) +#define THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN BIT(31) + +#define THC_M_PRT_GUC_DB_ADDR_LOW_1_GUC_DB_ADDR_LOW GENMASK(31, 2) +#define THC_M_PRT_GUC_DB_ADDR_HI_1_GUC_DB_ADDR_HI GENMASK(31, 0) +#define THC_M_PRT_GUC_DB_DATA_1_GUC_DB_DATA GENMASK(31, 0) +#define THC_M_PRT_GUC_OFFSET_INITVAL_1_THC_M_PRT_GUC_OFFSET_INITVAL GENMASK(31, 0) + +#define THC_M_PRT_RD_BULK_ADDR_1_THC_M_PRT_RD_BULK_ADDR GENMASK(31, 0) + +#define THC_M_PRT_DB_CNT_1_THC_M_PRT_DB_CNT GENMASK(30, 0) +#define THC_M_PRT_DB_CNT_1_THC_M_PRT_DB_CNT_RST BIT(31) + +#define THC_M_PRT_FRM_CNT_1_THC_M_PRT_FRM_CNT GENMASK(30, 0) +#define THC_M_PRT_FRM_CNT_1_THC_M_PRT_FRM_CNT_RST BIT(31) + +#define THC_M_PRT_UFRM_CNT_1_THC_M_PRT_UFRM_CNT GENMASK(30, 0) +#define THC_M_PRT_UFRM_CNT_1_THC_M_PRT_UFRM_CNT_RST BIT(31) + +#define THC_M_PRT_RXDMA_PKT_CNT_1_THC_M_PRT_RXDMA_PKT_CNT GENMASK(30, 0) +#define THC_M_PRT_RXDMA_PKT_CNT_1_THC_M_PRT_RXDMA_PKT_CNT_RST BIT(31) + +#define THC_M_PRT_SWINT_CNT_1_THC_M_PRT_SWINT_CNT GENMASK(30, 0) +#define THC_M_PRT_SWINT_CNT_1_THC_M_PRT_SWINT_CNT_RST BIT(31) + +#define THC_M_PRT_FRAME_DROP_CNT_1_NOFD GENMASK(30, 0) +#define THC_M_PRT_FRAME_DROP_CNT_1_RFDC BIT(31) + +#define THC_M_PRT_COALESCE_1_COALESCE_TIMEOUT GENMASK(6, 0) + +#define THC_M_PRT_RPRD_BA_LOW_2_THC_M_PRT_RPRD_BA_LOW GENMASK(31, 12) +#define THC_M_PRT_RPRD_BA_HI_2_THC_M_PRT_RPRD_BA_HI GENMASK(31, 0) + +#define THC_M_PRT_READ_DMA_ERR_2_DLERR BIT(0) + +#define THC_M_PRT_GUC_OFFSET_LOW_2_THC_M_PRT_GUC_OFFSET_LOW GENMASK(31, 3) +#define THC_M_PRT_GUC_OFFSET_HI_2_THC_M_PRT_GUC_OFFSET_HI GENMASK(31, 0) + +#define THC_M_PRT_GUC_WORKQ_ITEM_SZ_2_WORKQ_ITEM_SZ GENMASK(23, 0) +#define THC_M_PRT_GUC_WORKQ_SZ_2_WORKQ_SZ GENMASK(23, 0) +#define THC_M_PRT_GUC_WORKQ_SZ_2_FCD GENMASK(27, 24) +#define THC_M_PRT_GUC_WORKQ_SZ_2_GIC GENMASK(31, 28) + +#define THC_M_PRT_TSEQ_CNTRL_2_RGD BIT(2) +#define THC_M_PRT_TSEQ_CNTRL_2_EGP BIT(3) +#define THC_M_PRT_TSEQ_CNTRL_2_RTO BIT(4) + +#define THC_M_PRT_GUC_DB_ADDR_LOW_2_GUC_DB_ADDR_LOW GENMASK(31, 2) +#define THC_M_PRT_GUC_DB_ADDR_HI_2_GUC_DB_ADDR_HI GENMASK(31, 0) + +#define THC_M_PRT_GUC_DB_DATA_2_GUC_DB_DATA GENMASK(31, 0) + +#define THC_M_PRT_GUC_OFFSET_INITVAL_2_THC_M_PRT_GUC_OFFSET_INITVAL GENMASK(31, 0) + +#define THC_M_PRT_RD_BULK_ADDR_2_THC_M_PRT_RD_BULK_ADDR GENMASK(31, 0) + +#define THC_M_PRT_DB_CNT_2_THC_M_PRT_DB_CNT GENMASK(30, 0) +#define THC_M_PRT_DB_CNT_2_THC_M_PRT_DB_CNT_RST BIT(31) + +#define THC_M_PRT_FRM_CNT_2_THC_M_PRT_FRM_CNT GENMASK(30, 0) +#define THC_M_PRT_FRM_CNT_2_THC_M_PRT_FRM_CNT_RST BIT(31) + +#define THC_M_PRT_UFRM_CNT_2_THC_M_PRT_UFRM_CNT GENMASK(30, 0) +#define THC_M_PRT_UFRM_CNT_2_THC_M_PRT_UFRM_CNT_RST BIT(31) + +#define THC_M_PRT_RXDMA_PKT_CNT_2_THC_M_PRT_RXDMA_PKT_CNT GENMASK(30, 0) +#define THC_M_PRT_RXDMA_PKT_CNT_2_THC_M_PRT_RXDMA_PKT_CNT_RST BIT(31) + +#define THC_M_PRT_SWINT_CNT_2_THC_M_PRT_SWINT_CNT GENMASK(30, 0) +#define THC_M_PRT_SWINT_CNT_2_THC_M_PRT_SWINT_CNT_RST BIT(31) + +#define THC_M_PRT_FRAME_DROP_CNT_2_NOFD GENMASK(30, 0) +#define THC_M_PRT_FRAME_DROP_CNT_2_RFDC BIT(31) + +#define THC_M_PRT_COALESCE_2_COALESCE_TIMEOUT GENMASK(6, 0) + +#define THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_I2C_RW_PIO_EN BIT(23) +#define THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_PIO_I2C_WBC GENMASK(31, 26) + +#define THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_RX_DLEN_EN BIT(23) +#define THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_WBC GENMASK(31, 26) + +#define THC_M_PRT_PRD_EMPTY_CNT_1_RPTEC BIT(31) +#define THC_M_PRT_PRD_EMPTY_CNT_2_RPTEC BIT(31) + +#define THC_M_PRT_SW_DMA_PRD_TABLE_LEN_THC_M_PRT_SW_DMA_PRD_TABLE_LEN GENMASK(23, 0) + +#define THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_VAL GENMASK(3, 0) +#define THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_EN BIT(25) + +/* CS Assertion delay default value */ +#define THC_CSA_CK_DELAY_VAL_DEFAULT 4 + +/* ARB policy definition */ +/* Arbiter switches on packet boundary */ +#define THC_ARB_POLICY_PACKET_BOUNDARY 0 +/* Arbiter switches on Micro Frame boundary */ +#define THC_ARB_POLICY_UFRAME_BOUNDARY 1 +/* Arbiter switches on Frame boundary */ +#define THC_ARB_POLICY_FRAME_BOUNDARY 2 + +#define THC_REGMAP_POLLING_INTERVAL_US 10 /* 10us */ +#define THC_PIO_DONE_TIMEOUT_US USEC_PER_SEC /* 1s */ + +/* Default configures for HIDSPI */ +#define THC_BIT_OFFSET_INTERRUPT_TYPE 4 +/* input_report_type is 4 bits for HIDSPI */ +#define THC_BIT_LENGTH_INTERRUPT_TYPE 4 +/* Last fragment indicator is bit 15 for HIDSPI */ +#define THC_BIT_OFFSET_LAST_FRAGMENT_FLAG 22 +#define THC_BIT_OFFSET_MICROFRAME_SIZE 8 +/* input_report_length is 14 bits for HIDSPI */ +#define THC_BIT_LENGTH_MICROFRAME_SIZE 14 +/* MFS unit in power of 2 */ +#define THC_UNIT_MICROFRAME_SIZE 2 +#define THC_BITMASK_INTERRUPT_TYPE_DATA 1 +#define THC_BITMASK_INVALID_TYPE_DATA 2 + +/* Interrupt Quiesce default timeout value */ +#define THC_QUIESCE_EN_TIMEOUT_US USEC_PER_SEC /* 1s */ + +/* LTR definition */ +/* + * THC uses scale to calculate final LTR value. + * Scale is geometric progression of 2^5 step, starting from 2^0. + * For example, THC_LTR_SCALE_2(2) means 2^(5 * 2) = 1024, unit is ns. + */ +#define THC_LTR_SCALE_0 0 +#define THC_LTR_SCALE_1 1 +#define THC_LTR_SCALE_2 2 +#define THC_LTR_SCALE_3 3 +#define THC_LTR_SCALE_4 4 +#define THC_LTR_SCALE_5 5 +#define THC_LTR_MODE_ACTIVE 0 +#define THC_LTR_MODE_LP 1 +#define THC_LTR_MIN_VAL_SCALE_3 BIT(10) +#define THC_LTR_MAX_VAL_SCALE_3 BIT(15) +#define THC_LTR_MIN_VAL_SCALE_4 BIT(15) +#define THC_LTR_MAX_VAL_SCALE_4 BIT(20) +#define THC_LTR_MIN_VAL_SCALE_5 BIT(20) +#define THC_LTR_MAX_VAL_SCALE_5 BIT(25) + +/* + * THC PIO opcode default value + * @THC_PIO_OP_SPI_TIC_READ: THC opcode for SPI PIO read + * @THC_PIO_OP_SPI_TIC_WRITE: THC opcode for SPI PIO write + * @THC_PIO_OP_I2C_SUBSYSTEM_READ: THC opcode for read I2C subsystem registers + * @THC_PIO_OP_I2C_SUBSYSTEM_WRITE: THC opcode for write I2C subsystem registers + * @THC_PIO_OP_I2C_TIC_READ: THC opcode for read I2C device + * @THC_PIO_OP_I2C_TIC_WRITE: THC opcode for write I2C device + * @THC_PIO_OP_I2C_TIC_WRITE_AND_READ: THC opcode for write followed by read I2C device + */ +enum thc_pio_opcode { + THC_PIO_OP_SPI_TIC_READ = 0x4, + THC_PIO_OP_SPI_TIC_WRITE = 0x6, + THC_PIO_OP_I2C_SUBSYSTEM_READ = 0x12, + THC_PIO_OP_I2C_SUBSYSTEM_WRITE = 0x13, + THC_PIO_OP_I2C_TIC_READ = 0x14, + THC_PIO_OP_I2C_TIC_WRITE = 0x18, + THC_PIO_OP_I2C_TIC_WRITE_AND_READ = 0x1C, +}; + +/** + * THC SPI IO mode + * @THC_SINGLE_IO: single IO mode, 1(opcode) - 1(address) - 1(data) + * @THC_DUAL_IO: dual IO mode, 1(opcode) - 2(address) - 2(data) + * @THC_QUAD_IO: quad IO mode, 1(opcode) - 4(address) - 4(data) + * @THC_QUAD_PARALLEL_IO: parallel quad IO mode, 4(opcode) - 4(address) - 4(data) + */ +enum thc_spi_iomode { + THC_SINGLE_IO = 0, + THC_DUAL_IO = 1, + THC_QUAD_IO = 2, + THC_QUAD_PARALLEL_IO = 3, +}; + +/** + * THC SPI frequency divider + * + * This DIV final value is determined by THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN bit. + * If THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN isn't be set, THC takes the DIV value directly; + * If THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN is set, THC takes the DIV value multiply by 8. + * + * For example, if THC input clock is 125MHz: + * When THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN isn't set, THC_SPI_FRQ_DIV_3 means DIV is 3, + * THC final clock is 125 / 3 = 41.667MHz; + * When THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN is set, THC_SPI_FRQ_DIV_3 means DIV is 3 * 8, + * THC final clock is 125 / (3 * 8) = 5.208MHz; + */ +enum thc_spi_frq_div { + THC_SPI_FRQ_RESERVED = 0, + THC_SPI_FRQ_DIV_1 = 1, + THC_SPI_FRQ_DIV_2 = 2, + THC_SPI_FRQ_DIV_3 = 3, + THC_SPI_FRQ_DIV_4 = 4, + THC_SPI_FRQ_DIV_5 = 5, + THC_SPI_FRQ_DIV_6 = 6, + THC_SPI_FRQ_DIV_7 = 7, +}; + +/* THC I2C sub-system registers */ +#define THC_I2C_IC_CON_OFFSET 0x0 +#define THC_I2C_IC_TAR_OFFSET 0x4 +#define THC_I2C_IC_SAR_OFFSET 0x8 +#define THC_I2C_IC_HS_MADDR_OFFSET 0xC +#define THC_I2C_IC_DATA_CMD_OFFSET 0x10 +#define THC_I2C_IC_SS_SCL_HCNT_OFFSET 0x14 +#define THC_I2C_IC_UFM_SCL_HCNT_OFFSET 0x14 +#define THC_I2C_IC_SS_SCL_LCNT_OFFSET 0x18 +#define THC_I2C_IC_UFM_SCL_LCNT_OFFSET 0x18 +#define THC_I2C_IC_FS_SCL_HCNT_OFFSET 0x1C +#define THC_I2C_IC_UFM_TBUF_CNT_OFFSET 0x1C +#define THC_I2C_IC_FS_SCL_LCNT_OFFSET 0x20 +#define THC_I2C_IC_HS_SCL_HCNT_OFFSET 0x24 +#define THC_I2C_IC_HS_SCL_LCNT_OFFSET 0x28 +#define THC_I2C_IC_INTR_STAT_OFFSET 0x2C +#define THC_I2C_IC_INTR_MASK_OFFSET 0x30 +#define THC_I2C_IC_RAW_INTR_STAT_OFFSET 0x34 +#define THC_I2C_IC_RX_TL_OFFSET 0x38 +#define THC_I2C_IC_TX_TL_OFFSET 0x3C +#define THC_I2C_IC_CLR_INTR_OFFSET 0x40 +#define THC_I2C_IC_CLR_RX_UNDER_OFFSET 0x44 +#define THC_I2C_IC_CLR_RX_OVER_OFFSET 0x48 +#define THC_I2C_IC_CLR_TX_OVER_OFFSET 0x4C +#define THC_I2C_IC_CLR_RD_REQ_OFFSET 0x50 +#define THC_I2C_IC_CLR_TX_ABRT_OFFSET 0x54 +#define THC_I2C_IC_CLR_RX_DONE_OFFSET 0x58 +#define THC_I2C_IC_CLR_ACTIVITY_OFFSET 0x5C +#define THC_I2C_IC_CLR_STOP_DET_OFFSET 0x60 +#define THC_I2C_IC_CLR_START_DET_OFFSET 0x64 +#define THC_I2C_IC_CLR_GEN_CALL_OFFSET 0x68 +#define THC_I2C_IC_ENABLE_OFFSET 0x6C +#define THC_I2C_IC_STATUS_OFFSET 0x70 +#define THC_I2C_IC_TXFLR_OFFSET 0x74 +#define THC_I2C_IC_RXFLR_OFFSET 0x78 +#define THC_I2C_IC_SDA_HOLD_OFFSET 0x7C +#define THC_I2C_IC_TX_ABRT_SOURCE_OFFSET 0x80 +#define THC_I2C_IC_SLV_DATA_NACK_ONLY_OFFSET 0x84 +#define THC_I2C_IC_DMA_CR_OFFSET 0x88 +#define THC_I2C_IC_DMA_TDLR_OFFSET 0x8C +#define THC_I2C_IC_DMA_RDLR_OFFSET 0x90 +#define THC_I2C_IC_SDA_SETUP_OFFSET 0x94 +#define THC_I2C_IC_ACK_GENERAL_CALL_OFFSET 0x98 +#define THC_I2C_IC_ENABLE_STATUS_OFFSET 0x9C +#define THC_I2C_IC_FS_SPKLEN_OFFSET 0xA0 +#define THC_I2C_IC_UFM_SPKLEN_OFFSET 0xA0 +#define THC_I2C_IC_HS_SPKLEN_OFFSET 0xA4 +#define THC_I2C_IC_CLR_RESTART_DET_OFFSET 0xA8 +#define THC_I2C_IC_SCL_STUCK_AT_LOW_TIMEOUT_OFFSET 0xAC +#define THC_I2C_IC_SDA_STUCK_AT_LOW_TIMEOUT_OFFSET 0xB0 +#define THC_I2C_IC_CLR_SCL_STUCK_DET_OFFSET 0xB4 +#define THC_I2C_IC_DEVICE_ID_OFFSET 0xB8 +#define THC_I2C_IC_SMBUS_CLK_LOW_SEXT_OFFSET 0xBC +#define THC_I2C_IC_SMBUS_CLK_LOW_MEXT_OFFSET 0xC0 +#define THC_I2C_IC_SMBUS_THIGH_MAX_IDLE_COUNT_OFFSET 0xC4 +#define THC_I2C_IC_SMBUS_INTR_STAT_OFFSET 0xC8 +#define THC_I2C_IC_SMBUS_INTR_MASK_OFFSET 0xCC +#define THC_I2C_IC_SMBUS_RAW_INTR_STAT_OFFSET 0xD0 +#define THC_I2C_IC_CLR_SMBUS_INTR_OFFSET 0xD4 +#define THC_I2C_IC_OPTIONAL_SAR_OFFSET 0xD8 +#define THC_I2C_IC_SMBUS_UDID_LSB_OFFSET 0xDC +#define THC_I2C_IC_SMBUS_UDID_WORD0_OFFSET 0xDC +#define THC_I2C_IC_SMBUS_UDID_WORD1_OFFSET 0xE0 +#define THC_I2C_IC_SMBUS_UDID_WORD2_OFFSET 0xE4 +#define THC_I2C_IC_SMBUS_UDID_WORD3_OFFSET 0xE8 +#define THC_I2C_IC_COMP_PARAM_1_OFFSET 0xF4 +#define THC_I2C_IC_COMP_VERSION_OFFSET 0xF8 +#define THC_I2C_IC_COMP_TYPE_OFFSET 0xFC + +/** + * THC I2C sub-system supported speed mode + */ +enum THC_I2C_SPEED_MODE { + THC_I2C_STANDARD = 1, + THC_I2C_FAST_AND_PLUS = 2, + THC_I2C_HIGH_SPEED = 3, +}; + +/* THC I2C sub-system register bits definition */ +#define THC_I2C_IC_ENABLE_ENABLE BIT(0) +#define THC_I2C_IC_ENABLE_ABORT BIT(1) +#define THC_I2C_IC_ENABLE_TX_CMD_BLOCK BIT(2) +#define THC_I2C_IC_ENABLE_SDA_STUCK_RECOVERY_ENABLE BIT(3) +#define THC_I2C_IC_ENABLE_SMBUS_CLK_RESET BIT(16) +#define THC_I2C_IC_ENABLE_SMBUS_SUSPEND_EN BIT(17) +#define THC_I2C_IC_ENABLE_SMBUS_ALERT_EN BIT(18) + +#define THC_I2C_IC_CON_MASTER_MODE BIT(0) +#define THC_I2C_IC_CON_SPEED GENMASK(2, 1) +#define THC_I2C_IC_CON_IC_10BITADDR_SLAVE BIT(3) +#define THC_I2C_IC_CON_IC_10BITADDR_MASTER BIT(4) +#define THC_I2C_IC_CON_IC_RESTART_EN BIT(5) +#define THC_I2C_IC_CON_IC_SLAVE_DISABLE BIT(6) +#define THC_I2C_IC_CON_STOP_DET_IFADDRESSED BIT(7) +#define THC_I2C_IC_CON_TX_EMPTY_CTRL BIT(8) +#define THC_I2C_IC_CON_RX_FIFO_FULL_HLD_CTRL BIT(9) +#define THC_I2C_IC_CON_STOP_DET_IF_MASTER_ACTIVE BIT(10) +#define THC_I2C_IC_CON_BUS_CLEAR_FEATURE_CTRL BIT(11) +#define THC_I2C_IC_CON_OPTIONAL_SAR_CTRL BIT(16) +#define THC_I2C_IC_CON_SMBUS_SLAVE_QUICK_EN BIT(17) +#define THC_I2C_IC_CON_SMBUS_ARP_EN BIT(18) +#define THC_I2C_IC_CON_SMBUS_PERSISTENT_SLV_ADDR_EN BIT(19) + +#define THC_I2C_IC_TAR_IC_TAR GENMASK(9, 0) +#define THC_I2C_IC_TAR_GC_OR_START BIT(10) +#define THC_I2C_IC_TAR_SPECIAL BIT(11) +#define THC_I2C_IC_TAR_IC_10BITADDR_MASTER BIT(12) +#define THC_I2C_IC_TAR_DEVICE_ID BIT(13) +#define THC_I2C_IC_TAR_SMBUS_QUICK_CMD BIT(16) + +#define THC_I2C_IC_INTR_MASK_M_RX_UNDER BIT(0) +#define THC_I2C_IC_INTR_MASK_M_RX_OVER BIT(1) +#define THC_I2C_IC_INTR_MASK_M_RX_FULL BIT(2) +#define THC_I2C_IC_INTR_MASK_M_TX_OVER BIT(3) +#define THC_I2C_IC_INTR_MASK_M_TX_EMPTY BIT(4) +#define THC_I2C_IC_INTR_MASK_M_RD_REQ BIT(5) +#define THC_I2C_IC_INTR_MASK_M_TX_ABRT BIT(6) +#define THC_I2C_IC_INTR_MASK_M_RX_DONE BIT(7) +#define THC_I2C_IC_INTR_MASK_M_ACTIVITY BIT(8) +#define THC_I2C_IC_INTR_MASK_M_STOP_DET BIT(9) +#define THC_I2C_IC_INTR_MASK_M_START_DET BIT(10) +#define THC_I2C_IC_INTR_MASK_M_GEN_CALL BIT(11) +#define THC_I2C_IC_INTR_MASK_M_RESTART_DET BIT(12) +#define THC_I2C_IC_INTR_MASK_M_MASTER_ON_HOLD BIT(13) +#define THC_I2C_IC_INTR_MASK_M_SCL_STUCK_AT_LOW BIT(14) + +#define THC_I2C_IC_DMA_CR_RDMAE BIT(0) +#define THC_I2C_IC_DMA_CR_TDMAE BIT(1) + +#endif /* _INTEL_THC_HW_H_ */ diff --git a/drivers/hid/surface-hid/Kconfig b/drivers/hid/surface-hid/Kconfig index 7ce9b5d641eb..d0cfd0d29926 100644 --- a/drivers/hid/surface-hid/Kconfig +++ b/drivers/hid/surface-hid/Kconfig @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0+ menu "Surface System Aggregator Module HID support" depends on SURFACE_AGGREGATOR - depends on INPUT config SURFACE_HID tristate "HID transport driver for Surface System Aggregator Module" @@ -39,4 +38,3 @@ endmenu config SURFACE_HID_CORE tristate - select HID diff --git a/drivers/hid/surface-hid/surface_hid.c b/drivers/hid/surface-hid/surface_hid.c index 61e5814b0ad7..eae47e0d95ed 100644 --- a/drivers/hid/surface-hid/surface_hid.c +++ b/drivers/hid/surface-hid/surface_hid.c @@ -8,7 +8,7 @@ * Maximilian Luz <luzmaximilian@gmail.com> */ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/hid.h> #include <linux/kernel.h> #include <linux/module.h> diff --git a/drivers/hid/surface-hid/surface_hid_core.c b/drivers/hid/surface-hid/surface_hid_core.c index a3e9cceddfac..6690c24f28f0 100644 --- a/drivers/hid/surface-hid/surface_hid_core.c +++ b/drivers/hid/surface-hid/surface_hid_core.c @@ -7,7 +7,7 @@ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com> */ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/hid.h> #include <linux/kernel.h> #include <linux/module.h> diff --git a/drivers/hid/surface-hid/surface_kbd.c b/drivers/hid/surface-hid/surface_kbd.c index 4fbce201db6a..0be01b5e7425 100644 --- a/drivers/hid/surface-hid/surface_kbd.c +++ b/drivers/hid/surface-hid/surface_kbd.c @@ -7,7 +7,7 @@ * Copyright (C) 2019-2021 Maximilian Luz <luzmaximilian@gmail.com> */ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/hid.h> #include <linux/kernel.h> #include <linux/module.h> @@ -271,10 +271,9 @@ static int surface_kbd_probe(struct platform_device *pdev) return surface_hid_device_add(shid); } -static int surface_kbd_remove(struct platform_device *pdev) +static void surface_kbd_remove(struct platform_device *pdev) { surface_hid_device_destroy(platform_get_drvdata(pdev)); - return 0; } static const struct acpi_device_id surface_kbd_match[] = { diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index a54c7995b9be..21a70420151e 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -803,7 +803,6 @@ static const struct file_operations uhid_fops = { .read = uhid_char_read, .write = uhid_char_write, .poll = uhid_char_poll, - .llseek = no_llseek, }; static struct miscdevice uhid_misc = { diff --git a/drivers/hid/usbhid/Kconfig b/drivers/hid/usbhid/Kconfig index 7c2032f7f44d..f3194767a45e 100644 --- a/drivers/hid/usbhid/Kconfig +++ b/drivers/hid/usbhid/Kconfig @@ -5,8 +5,7 @@ menu "USB HID support" config USB_HID tristate "USB HID transport layer" default y - depends on USB && INPUT - select HID + depends on HID help Say Y here if you want to connect USB keyboards, mice, joysticks, graphic tablets, or any other HID based devices diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index a90ed2ceae84..a6eb6fe6130d 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -19,8 +19,9 @@ #include <linux/list.h> #include <linux/mm.h> #include <linux/mutex.h> +#include <linux/property.h> #include <linux/spinlock.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <asm/byteorder.h> #include <linux/input.h> #include <linux/wait.h> @@ -1099,7 +1100,7 @@ static int usbhid_start(struct hid_device *hid) interval = endpoint->bInterval; - /* Some vendors give fullspeed interval on highspeed devides */ + /* Some vendors give fullspeed interval on highspeed devices */ if (hid->quirks & HID_QUIRK_FULLSPEED_INTERVAL && dev->speed == USB_SPEED_HIGH) { interval = fls(endpoint->bInterval*8); @@ -1374,6 +1375,7 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id * hid->hiddev_report_event = hiddev_report_event; #endif hid->dev.parent = &intf->dev; + device_set_node(&hid->dev, dev_fwnode(&intf->dev)); hid->bus = BUS_USB; hid->vendor = le16_to_cpu(dev->descriptor.idVendor); hid->product = le16_to_cpu(dev->descriptor.idProduct); diff --git a/drivers/hid/wacom.h b/drivers/hid/wacom.h index 77c5fb26cd14..1deacb4568cb 100644 --- a/drivers/hid/wacom.h +++ b/drivers/hid/wacom.h @@ -89,7 +89,7 @@ #include <linux/usb/input.h> #include <linux/power_supply.h> #include <linux/timer.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> /* * Version Information @@ -218,6 +218,14 @@ static inline __u32 wacom_s32tou(s32 value, __u8 n) return value & (1 << (n - 1)) ? value & (~(~0U << n)) : value; } +static inline u32 wacom_rescale(u32 value, u32 in_max, u32 out_max) +{ + if (in_max == 0 || out_max == 0) + return 0; + value = clamp(value, 0, in_max); + return DIV_ROUND_CLOSEST(value * out_max, in_max); +} + extern const struct hid_device_id wacom_ids[]; void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len); diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 2bc45b24075c..8125383932ec 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -1084,6 +1084,17 @@ static ssize_t wacom_luminance_store(struct wacom *wacom, u8 *dest, mutex_lock(&wacom->lock); *dest = value & 0x7f; + for (unsigned int i = 0; i < wacom->led.count; i++) { + struct wacom_group_leds *group = &wacom->led.groups[i]; + + for (unsigned int j = 0; j < group->count; j++) { + if (dest == &wacom->led.llv) + group->leds[j].llv = *dest; + else if (dest == &wacom->led.hlv) + group->leds[j].hlv = *dest; + } + } + err = wacom_led_control(wacom); mutex_unlock(&wacom->lock); @@ -1302,10 +1313,10 @@ enum led_brightness wacom_leds_brightness_get(struct wacom_led *led) struct wacom *wacom = led->wacom; if (wacom->led.max_hlv) - return led->hlv * LED_FULL / wacom->led.max_hlv; + return wacom_rescale(led->hlv, wacom->led.max_hlv, LED_FULL); if (wacom->led.max_llv) - return led->llv * LED_FULL / wacom->led.max_llv; + return wacom_rescale(led->llv, wacom->led.max_llv, LED_FULL); /* device doesn't support brightness tuning */ return LED_FULL; @@ -1337,8 +1348,8 @@ static int wacom_led_brightness_set(struct led_classdev *cdev, goto out; } - led->llv = wacom->led.llv = wacom->led.max_llv * brightness / LED_FULL; - led->hlv = wacom->led.hlv = wacom->led.max_hlv * brightness / LED_FULL; + led->llv = wacom->led.llv = wacom_rescale(brightness, LED_FULL, wacom->led.max_llv); + led->hlv = wacom->led.hlv = wacom_rescale(brightness, LED_FULL, wacom->led.max_hlv); wacom->led.groups[led->group].select = led->id; @@ -1370,17 +1381,6 @@ static int wacom_led_register_one(struct device *dev, struct wacom *wacom, if (!name) return -ENOMEM; - if (!read_only) { - led->trigger.name = name; - error = devm_led_trigger_register(dev, &led->trigger); - if (error) { - hid_err(wacom->hdev, - "failed to register LED trigger %s: %d\n", - led->cdev.name, error); - return error; - } - } - led->group = group; led->id = id; led->wacom = wacom; @@ -1397,6 +1397,19 @@ static int wacom_led_register_one(struct device *dev, struct wacom *wacom, led->cdev.brightness_set = wacom_led_readonly_brightness_set; } + if (!read_only) { + led->trigger.name = name; + if (id == wacom->led.groups[group].select) + led->trigger.brightness = wacom_leds_brightness_get(led); + error = devm_led_trigger_register(dev, &led->trigger); + if (error) { + hid_err(wacom->hdev, + "failed to register LED trigger %s: %d\n", + led->cdev.name, error); + return error; + } + } + error = devm_led_classdev_register(dev, &led->cdev); if (error) { hid_err(wacom->hdev, @@ -2241,7 +2254,8 @@ static void wacom_update_name(struct wacom *wacom, const char *suffix) if (hid_is_usb(wacom->hdev)) { struct usb_interface *intf = to_usb_interface(wacom->hdev->dev.parent); struct usb_device *dev = interface_to_usbdev(intf); - product_name = dev->product; + if (dev->product != NULL) + product_name = dev->product; } if (wacom->hdev->bus == BUS_I2C) { diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index a44367aef621..b60bfafc6a8f 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -692,78 +692,28 @@ static bool wacom_is_art_pen(int tool_id) static int wacom_intuos_get_tool_type(int tool_id) { - int tool_type = BTN_TOOL_PEN; - - if (wacom_is_art_pen(tool_id)) - return tool_type; - switch (tool_id) { case 0x812: /* Inking pen */ case 0x801: /* Intuos3 Inking pen */ case 0x12802: /* Intuos4/5 Inking Pen */ case 0x012: - tool_type = BTN_TOOL_PENCIL; - break; - - case 0x822: /* Pen */ - case 0x842: - case 0x852: - case 0x823: /* Intuos3 Grip Pen */ - case 0x813: /* Intuos3 Classic Pen */ - case 0x802: /* Intuos4/5 13HD/24HD General Pen */ - case 0x8e2: /* IntuosHT2 pen */ - case 0x022: - case 0x200: /* Pro Pen 3 */ - case 0x04200: /* Pro Pen 3 */ - case 0x10842: /* MobileStudio Pro Pro Pen slim */ - case 0x14802: /* Intuos4/5 13HD/24HD Classic Pen */ - case 0x16802: /* Cintiq 13HD Pro Pen */ - case 0x18802: /* DTH2242 Pen */ - case 0x10802: /* Intuos4/5 13HD/24HD General Pen */ - case 0x80842: /* Intuos Pro and Cintiq Pro 3D Pen */ - tool_type = BTN_TOOL_PEN; - break; + return BTN_TOOL_PENCIL; case 0x832: /* Stroke pen */ case 0x032: - tool_type = BTN_TOOL_BRUSH; - break; + return BTN_TOOL_BRUSH; case 0x007: /* Mouse 4D and 2D */ case 0x09c: case 0x094: case 0x017: /* Intuos3 2D Mouse */ case 0x806: /* Intuos4 Mouse */ - tool_type = BTN_TOOL_MOUSE; - break; + return BTN_TOOL_MOUSE; case 0x096: /* Lens cursor */ case 0x097: /* Intuos3 Lens cursor */ case 0x006: /* Intuos4 Lens cursor */ - tool_type = BTN_TOOL_LENS; - break; - - case 0x82a: /* Eraser */ - case 0x84a: - case 0x85a: - case 0x91a: - case 0xd1a: - case 0x0fa: - case 0x82b: /* Intuos3 Grip Pen Eraser */ - case 0x81b: /* Intuos3 Classic Pen Eraser */ - case 0x91b: /* Intuos3 Airbrush Eraser */ - case 0x80c: /* Intuos4/5 13HD/24HD Marker Pen Eraser */ - case 0x80a: /* Intuos4/5 13HD/24HD General Pen Eraser */ - case 0x90a: /* Intuos4/5 13HD/24HD Airbrush Eraser */ - case 0x1480a: /* Intuos4/5 13HD/24HD Classic Pen Eraser */ - case 0x1090a: /* Intuos4/5 13HD/24HD Airbrush Eraser */ - case 0x1080c: /* Intuos4/5 13HD/24HD Art Pen Eraser */ - case 0x1084a: /* MobileStudio Pro Pro Pen slim Eraser */ - case 0x1680a: /* Cintiq 13HD Pro Pen Eraser */ - case 0x1880a: /* DTH2242 Eraser */ - case 0x1080a: /* Intuos4/5 13HD/24HD General Pen Eraser */ - tool_type = BTN_TOOL_RUBBER; - break; + return BTN_TOOL_LENS; case 0xd12: case 0x912: @@ -771,10 +721,13 @@ static int wacom_intuos_get_tool_type(int tool_id) case 0x913: /* Intuos3 Airbrush */ case 0x902: /* Intuos4/5 13HD/24HD Airbrush */ case 0x10902: /* Intuos4/5 13HD/24HD Airbrush */ - tool_type = BTN_TOOL_AIRBRUSH; - break; + return BTN_TOOL_AIRBRUSH; + + default: + if (tool_id & 0x0008) + return BTN_TOOL_RUBBER; + return BTN_TOOL_PEN; } - return tool_type; } static void wacom_exit_report(struct wacom_wac *wacom) @@ -1400,9 +1353,9 @@ static void wacom_intuos_pro2_bt_pen(struct wacom_wac *wacom) rotation -= 1800; input_report_abs(pen_input, ABS_TILT_X, - (char)frame[7]); + (signed char)frame[7]); input_report_abs(pen_input, ABS_TILT_Y, - (char)frame[8]); + (signed char)frame[8]); input_report_abs(pen_input, ABS_Z, rotation); input_report_abs(pen_input, ABS_WHEEL, get_unaligned_le16(&frame[11])); @@ -1925,12 +1878,14 @@ static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage, int fmax = field->logical_maximum; unsigned int equivalent_usage = wacom_equivalent_usage(usage->hid); int resolution_code = code; - int resolution = hidinput_calc_abs_res(field, resolution_code); + int resolution; if (equivalent_usage == HID_DG_TWIST) { resolution_code = ABS_RZ; } + resolution = hidinput_calc_abs_res(field, resolution_code); + if (equivalent_usage == HID_GD_X) { fmin += features->offset_left; fmax -= features->offset_right; @@ -1951,11 +1906,12 @@ static void wacom_map_usage(struct input_dev *input, struct hid_usage *usage, if ((code == ABS_X || code == ABS_Y) && !resolution) { resolution = WACOM_INTUOS_RES; hid_warn(input, - "Wacom usage (%d) missing resolution \n", - code); + "Using default resolution for axis type 0x%x code 0x%x\n", + type, code); } input_abs_set_res(input, code, resolution); break; + case EV_REL: case EV_KEY: case EV_MSC: case EV_SW: @@ -2092,7 +2048,23 @@ static void wacom_wac_pad_usage_mapping(struct hid_device *hdev, features->device_type |= WACOM_DEVICETYPE_PAD; break; case WACOM_HID_WD_TOUCHRING: - wacom_map_usage(input, usage, field, EV_ABS, ABS_WHEEL, 0); + if (field->flags & HID_MAIN_ITEM_RELATIVE) { + wacom_wac->relring_count++; + if (wacom_wac->relring_count == 1) { + wacom_map_usage(input, usage, field, EV_REL, REL_WHEEL_HI_RES, 0); + set_bit(REL_WHEEL, input->relbit); + } + else if (wacom_wac->relring_count == 2) { + wacom_map_usage(input, usage, field, EV_REL, REL_HWHEEL_HI_RES, 0); + set_bit(REL_HWHEEL, input->relbit); + } + } else { + wacom_wac->absring_count++; + if (wacom_wac->absring_count == 1) + wacom_map_usage(input, usage, field, EV_ABS, ABS_WHEEL, 0); + else if (wacom_wac->absring_count == 2) + wacom_map_usage(input, usage, field, EV_ABS, ABS_THROTTLE, 0); + } features->device_type |= WACOM_DEVICETYPE_PAD; break; case WACOM_HID_WD_TOUCHRINGSTATUS: @@ -2157,7 +2129,10 @@ static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field return; if (wacom_equivalent_usage(field->physical) == HID_DG_TABLETFUNCTIONKEY) { - if (usage->hid != WACOM_HID_WD_TOUCHRING) + bool is_abs_touchring = usage->hid == WACOM_HID_WD_TOUCHRING && + !(field->flags & HID_MAIN_ITEM_RELATIVE); + + if (!is_abs_touchring) wacom_wac->hid_data.inrange_state |= value; } @@ -2210,6 +2185,52 @@ static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field hdev->product == 0x3AA) value = wacom_offset_rotation(input, usage, value, 1, 2); } + else if (field->flags & HID_MAIN_ITEM_RELATIVE) { + int hires_value = value * 120 / usage->resolution_multiplier; + int *ring_value; + int lowres_code; + + if (usage->code == REL_WHEEL_HI_RES) { + /* We must invert the sign for vertical + * relative scrolling. Clockwise + * rotation produces positive values + * from HW, but userspace treats + * positive REL_WHEEL as a scroll *up*! + */ + hires_value = -hires_value; + ring_value = &wacom_wac->hid_data.ring_value; + lowres_code = REL_WHEEL; + } + else if (usage->code == REL_HWHEEL_HI_RES) { + /* No need to invert the sign for + * horizontal relative scrolling. + * Clockwise rotation produces positive + * values from HW and userspace treats + * positive REL_HWHEEL as a scroll + * right. + */ + ring_value = &wacom_wac->hid_data.ring2_value; + lowres_code = REL_HWHEEL; + } + else { + hid_err(wacom->hdev, "unrecognized relative wheel with code %d\n", + usage->code); + break; + } + + value = hires_value; + *ring_value += hires_value; + + /* Emulate a legacy wheel click for every 120 + * units of hi-res travel. + */ + if (*ring_value >= 120 || *ring_value <= -120) { + int clicks = *ring_value / 120; + + input_event(input, usage->type, lowres_code, clicks); + *ring_value -= clicks * 120; + } + } else { value = wacom_offset_rotation(input, usage, value, 1, 4); } @@ -2367,6 +2388,9 @@ static void wacom_wac_pen_usage_mapping(struct hid_device *hdev, wacom_map_usage(input, usage, field, EV_KEY, BTN_STYLUS3, 0); features->quirks &= ~WACOM_QUIRK_PEN_BUTTON3; break; + case WACOM_HID_WD_SEQUENCENUMBER: + wacom_wac->hid_data.sequence_number = -1; + break; } } @@ -2398,9 +2422,11 @@ static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field wacom_wac->hid_data.sense_state = value; return; case HID_DG_INVERT: - wacom_wac->hid_data.invert_state = value; + wacom_wac->hid_data.eraser |= value; return; case HID_DG_ERASER: + wacom_wac->hid_data.eraser |= value; + fallthrough; case HID_DG_TIPSWITCH: wacom_wac->hid_data.tipswitch |= value; return; @@ -2491,9 +2517,15 @@ static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field wacom_wac->hid_data.barrelswitch3 = value; return; case WACOM_HID_WD_SEQUENCENUMBER: - if (wacom_wac->hid_data.sequence_number != value) - hid_warn(hdev, "Dropped %hu packets", (unsigned short)(value - wacom_wac->hid_data.sequence_number)); + if (wacom_wac->hid_data.sequence_number != value && + wacom_wac->hid_data.sequence_number >= 0) { + int sequence_size = field->logical_maximum - field->logical_minimum + 1; + int drop_count = (value - wacom_wac->hid_data.sequence_number) % sequence_size; + hid_warn(hdev, "Dropped %d packets", drop_count); + } wacom_wac->hid_data.sequence_number = value + 1; + if (wacom_wac->hid_data.sequence_number > field->logical_maximum) + wacom_wac->hid_data.sequence_number = field->logical_minimum; return; } @@ -2535,8 +2567,10 @@ static void wacom_wac_pen_report(struct hid_device *hdev, if (entering_range) { /* first in range */ /* Going into range select tool */ - if (wacom_wac->hid_data.invert_state) + if (wacom_wac->hid_data.eraser) wacom_wac->tool[0] = BTN_TOOL_RUBBER; + else if (wacom_wac->features.quirks & WACOM_QUIRK_AESPEN) + wacom_wac->tool[0] = BTN_TOOL_PEN; else if (wacom_wac->id[0]) wacom_wac->tool[0] = wacom_intuos_get_tool_type(wacom_wac->id[0]); else @@ -2587,6 +2621,7 @@ static void wacom_wac_pen_report(struct hid_device *hdev, } wacom_wac->hid_data.tipswitch = false; + wacom_wac->hid_data.eraser = false; input_sync(input); } @@ -4911,6 +4946,10 @@ static const struct wacom_features wacom_features_0x94 = HID_DEVICE(BUS_I2C, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\ .driver_data = (kernel_ulong_t)&wacom_features_##prod +#define PCI_DEVICE_WACOM(prod) \ + HID_DEVICE(BUS_PCI, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\ + .driver_data = (kernel_ulong_t)&wacom_features_##prod + #define USB_DEVICE_LENOVO(prod) \ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, prod), \ .driver_data = (kernel_ulong_t)&wacom_features_##prod @@ -5080,6 +5119,7 @@ const struct hid_device_id wacom_ids[] = { { USB_DEVICE_WACOM(HID_ANY_ID) }, { I2C_DEVICE_WACOM(HID_ANY_ID) }, + { PCI_DEVICE_WACOM(HID_ANY_ID) }, { BT_DEVICE_WACOM(HID_ANY_ID) }, { } }; diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index 6ec499841f70..0c3c6a6aaae9 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -300,7 +300,7 @@ struct hid_data { __s16 inputmode_index; /* InputMode HID feature index in the report */ bool sense_state; bool inrange_state; - bool invert_state; + bool eraser; bool tipswitch; bool barrelswitch; bool barrelswitch2; @@ -312,6 +312,8 @@ struct hid_data { int width; int height; int id; + int ring_value; + int ring2_value; int cc_report; int cc_index; int cc_value_index; @@ -324,7 +326,7 @@ struct hid_data { int bat_connected; int ps_connected; bool pad_input_event_flag; - unsigned short sequence_number; + int sequence_number; ktime_t time_delayed; }; @@ -355,6 +357,8 @@ struct wacom_wac { int num_contacts_left; u8 bt_features; u8 bt_high_speed; + u8 absring_count; + u8 relring_count; int mode_report; int mode_value; struct hid_data hid_data; |