diff options
Diffstat (limited to 'drivers/hid')
59 files changed, 2057 insertions, 323 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index f8a56d631242..4d2a89d65b65 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" @@ -465,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 @@ -1096,6 +1108,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 diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 496dab54c73a..24de45f3677d 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 @@ -70,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 diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_client.c b/drivers/hid/amd-sfh-hid/amd_sfh_client.c index 4b59687ff5d8..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; @@ -331,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++) { @@ -351,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/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index 8420c227e21b..961b7f35aa67 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -148,7 +148,7 @@ out: } EXPORT_SYMBOL_GPL(dispatch_hid_bpf_output_report); -u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, unsigned int *size) +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 = { @@ -183,7 +183,7 @@ u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, const u8 *rdesc, unsigned ignore_bpf: kfree(ctx_kern.data); - return kmemdup(rdesc, *size, GFP_KERNEL); + return rdesc; } EXPORT_SYMBOL_GPL(call_hid_bpf_rdesc_fixup); @@ -260,8 +260,11 @@ 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; } diff --git a/drivers/hid/bpf/hid_bpf_struct_ops.c b/drivers/hid/bpf/hid_bpf_struct_ops.c index cd696c59ba0f..702c22fae136 100644 --- a/drivers/hid/bpf/hid_bpf_struct_ops.c +++ b/drivers/hid/bpf/hid_bpf_struct_ops.c @@ -276,9 +276,23 @@ 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 = { diff --git a/drivers/hid/bpf/progs/Huion__Dial-2.bpf.c b/drivers/hid/bpf/progs/Huion__Dial-2.bpf.c index 2411dec6db08..9670e5ef8d54 100644 --- a/drivers/hid/bpf/progs/Huion__Dial-2.bpf.c +++ b/drivers/hid/bpf/progs/Huion__Dial-2.bpf.c @@ -214,7 +214,8 @@ static const __u8 fixed_rdesc_pad[] = { CollectionApplication( // -- Byte 0 in report ReportId(PAD_REPORT_ID) - LogicalRange_i8(0, 1) + LogicalMaximum_i8(0) + LogicalMaximum_i8(1) UsagePage_Digitizers Usage_Dig_TabletFunctionKeys CollectionPhysical( @@ -234,14 +235,17 @@ static const __u8 fixed_rdesc_pad[] = { Input(Var|Abs) // Byte 4 in report is the dial Usage_GD_Wheel - LogicalRange_i8(-1, 1) + LogicalMinimum_i8(-1) + LogicalMaximum_i8(1) ReportCount(1) ReportSize(8) Input(Var|Rel) // Byte 5 is the button state UsagePage_Button - UsageRange_i8(0x01, 0x8) - LogicalRange_i8(0x0, 0x1) + UsageMinimum_i8(0x01) + UsageMaximum_i8(0x08) + LogicalMinimum_i8(0x0) + LogicalMaximum_i8(0x1) ReportCount(7) ReportSize(1) Input(Var|Abs) @@ -265,7 +269,8 @@ static const __u8 fixed_rdesc_pen[] = { Usage_Dig_TipSwitch Usage_Dig_BarrelSwitch Usage_Dig_SecondaryBarrelSwitch // maps eraser to BTN_STYLUS2 - LogicalRange_i8(0, 1) + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) ReportSize(1) ReportCount(3) Input(Var|Abs) @@ -280,22 +285,28 @@ static const __u8 fixed_rdesc_pen[] = { UsagePage_GenericDesktop Unit(cm) UnitExponent(-1) - PhysicalRange_i16(0, 266) - LogicalRange_i16(0, 32767) + PhysicalMinimum_i16(0) + PhysicalMaximum_i16(266) + LogicalMinimum_i16(0) + LogicalMaximum_i16(32767) Usage_GD_X Input(Var|Abs) // Bytes 2+3 - PhysicalRange_i16(0, 166) - LogicalRange_i16(0, 32767) + 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 - LogicalRange_i16(0, 8191) + LogicalMinimum_i16(0) + LogicalMaximum_i16(8191) Input(Var|Abs) // Byte 6+7 ReportSize(8) ReportCount(2) - LogicalRange_i8(-60, 60) + LogicalMinimum_i8(-60) + LogicalMaximum_i8(60) Usage_Dig_XTilt Usage_Dig_YTilt Input(Var|Abs) // Byte 8+9 @@ -313,7 +324,8 @@ static const __u8 fixed_rdesc_vendor[] = { Usage_Dig_Pen CollectionPhysical( // Byte 1 are the buttons - LogicalRange_i8(0, 1) + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) ReportSize(1) Usage_Dig_TipSwitch Usage_Dig_BarrelSwitch @@ -333,25 +345,31 @@ static const __u8 fixed_rdesc_vendor[] = { UnitExponent(-1) // Note: reported logical range differs // from the pen report ID for x and y - LogicalRange_i16(0, 53340) - PhysicalRange_i16(0, 266) + LogicalMinimum_i16(0) + LogicalMaximum_i16(53340) + PhysicalMinimum_i16(0) + PhysicalMaximum_i16(266) // Bytes 2/3 in report Usage_GD_X Input(Var|Abs) - LogicalRange_i16(0, 33340) - PhysicalRange_i16(0, 166) + 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 - LogicalRange_i16(0, 8191) + LogicalMinimum_i16(0) + LogicalMaximum_i16(8191) Usage_Dig_TipPressure Input(Var|Abs) // Bytes 8/9 in report ReportCount(1) // Padding Input(Const) - LogicalRange_i8(-60, 60) + LogicalMinimum_i8(-60) + LogicalMaximum_i8(60) // Byte 10 in report Usage_Dig_XTilt // Byte 11 in report @@ -366,7 +384,8 @@ static const __u8 fixed_rdesc_vendor[] = { CollectionApplication( // Byte 0 ReportId(PAD_REPORT_ID) - LogicalRange_i8(0, 1) + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) UsagePage_Digitizers Usage_Dig_TabletFunctionKeys CollectionPhysical( @@ -386,15 +405,18 @@ static const __u8 fixed_rdesc_vendor[] = { Input(Var|Abs) // Byte 4 is the button state UsagePage_Button - UsageRange_i8(0x01, 0x8) - LogicalRange_i8(0x0, 0x1) + 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 - LogicalRange_i8(-1, 1) + LogicalMinimum_i8(-1) + LogicalMaximum_i8(1) ReportCount(1) ReportSize(8) Input(Var|Rel) diff --git a/drivers/hid/bpf/progs/Huion__Inspiroy-2-S.bpf.c b/drivers/hid/bpf/progs/Huion__Inspiroy-2-S.bpf.c index b09b80132368..13f64fb49800 100644 --- a/drivers/hid/bpf/progs/Huion__Inspiroy-2-S.bpf.c +++ b/drivers/hid/bpf/progs/Huion__Inspiroy-2-S.bpf.c @@ -170,7 +170,8 @@ static const __u8 fixed_rdesc_pad[] = { CollectionApplication( // -- Byte 0 in report ReportId(PAD_REPORT_ID) - LogicalRange_i8(0, 1) + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) UsagePage_Digitizers Usage_Dig_TabletFunctionKeys CollectionPhysical( @@ -190,14 +191,17 @@ static const __u8 fixed_rdesc_pad[] = { Input(Var|Abs) // Byte 4 in report is the wheel Usage_GD_Wheel - LogicalRange_i8(-1, 1) + LogicalMinimum_i8(-1) + LogicalMaximum_i8(1) ReportCount(1) ReportSize(8) Input(Var|Rel) // Byte 5 is the button state UsagePage_Button - UsageRange_i8(0x01, 0x6) - LogicalRange_i8(0x01, 0x6) + UsageMinimum_i8(0x1) + UsageMaximum_i8(0x6) + LogicalMinimum_i8(0x1) + LogicalMaximum_i8(0x6) ReportCount(1) ReportSize(8) Input(Arr|Abs) @@ -219,7 +223,8 @@ static const __u8 fixed_rdesc_pen[] = { Usage_Dig_TipSwitch Usage_Dig_BarrelSwitch Usage_Dig_SecondaryBarrelSwitch // maps eraser to BTN_STYLUS2 - LogicalRange_i8(0, 1) + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) ReportSize(1) ReportCount(3) Input(Var|Abs) @@ -234,18 +239,23 @@ static const __u8 fixed_rdesc_pen[] = { UsagePage_GenericDesktop Unit(cm) UnitExponent(-1) - PhysicalRange_i16(0, 160) - LogicalRange_i16(0, 32767) + PhysicalMinimum_i16(0) + PhysicalMaximum_i16(160) + LogicalMinimum_i16(0) + LogicalMaximum_i16(32767) Usage_GD_X Input(Var|Abs) // Bytes 2+3 - PhysicalRange_i16(0, 100) - LogicalRange_i16(0, 32767) + 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 - LogicalRange_i16(0, 8191) + 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) @@ -265,7 +275,8 @@ static const __u8 fixed_rdesc_vendor[] = { Usage_Dig_Pen CollectionPhysical( // Byte 1 are the buttons - LogicalRange_i8(0, 1) + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) ReportSize(1) Usage_Dig_TipSwitch Usage_Dig_BarrelSwitch @@ -285,19 +296,24 @@ static const __u8 fixed_rdesc_vendor[] = { UnitExponent(-1) // Note: reported logical range differs // from the pen report ID for x and y - LogicalRange_i16(0, 32000) - PhysicalRange_i16(0, 160) + LogicalMinimum_i16(0) + LogicalMaximum_i16(32000) + PhysicalMinimum_i16(0) + PhysicalMaximum_i16(160) // Bytes 2/3 in report Usage_GD_X Input(Var|Abs) - LogicalRange_i16(0, 20000) - PhysicalRange_i16(0, 100) + 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 - LogicalRange_i16(0, 8192) + LogicalMinimum_i16(0) + LogicalMaximum_i16(8192) Usage_Dig_TipPressure Input(Var|Abs) ) @@ -307,7 +323,8 @@ static const __u8 fixed_rdesc_vendor[] = { CollectionApplication( // Byte 0 ReportId(PAD_REPORT_ID) - LogicalRange_i8(0, 1) + LogicalMinimum_i8(0) + LogicalMaximum_i8(1) UsagePage_Digitizers Usage_Dig_TabletFunctionKeys CollectionPhysical( @@ -327,8 +344,10 @@ static const __u8 fixed_rdesc_vendor[] = { Input(Var|Abs) // Byte 4 is the button state UsagePage_Button - UsageRange_i8(0x01, 0x6) - LogicalRange_i8(0x0, 0x1) + UsageMinimum_i8(0x1) + UsageMaximum_i8(0x6) + LogicalMinimum_i8(0x0) + LogicalMaximum_i8(0x1) ReportCount(6) ReportSize(1) Input(Var|Abs) @@ -337,7 +356,8 @@ static const __u8 fixed_rdesc_vendor[] = { // Byte 5 is the wheel UsagePage_GenericDesktop Usage_GD_Wheel - LogicalRange_i8(-1, 1) + LogicalMinimum_i8(-1) + LogicalMaximum_i8(1) ReportCount(1) ReportSize(8) Input(Var|Rel) 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/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/hid_report_helpers.h b/drivers/hid/bpf/progs/hid_report_helpers.h index 0aa1df438eeb..9b2a48e4a311 100644 --- a/drivers/hid/bpf/progs/hid_report_helpers.h +++ b/drivers/hid/bpf/progs/hid_report_helpers.h @@ -52,7 +52,8 @@ * Usage_GD_Keyboard * CollectionApplication( ← Open the collection * ReportId(3) - * LogicalRange_i8(0, 1) + * LogicalMinimum_i8(0) + * LogicalMaximum_i8(1) * // other fields * ) ← End EndCollection * @@ -74,26 +75,43 @@ #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 LogicalRange_i8(min_, max_) 0x15, i8(min_), 0x25, i8(max_), -#define LogicalRange_i16(min_, max_) 0x16, LE16(min_), 0x26, LE16(max_), -#define LogicalRange_i32(min_, max_) 0x17, LE32(min_), 0x27, LE32(max_), +#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 PhysicalRange_i8(min_, max_) 0x35, i8(min_), 0x45, i8(max_), -#define PhysicalRange_i16(min_, max_) 0x36, LE16(min_), 0x46, LE16(max_), -#define PhysicalRange_i32(min_, max_) 0x37, LE32(min_), 0x47, LE32(max_), +#define UsageMinimum_i8(min_) 0x19, i8(min_), +#define UsageMinimum_i16(min_) 0x1a, LE16(min_), -#define UsageRange_i8(min_, max_) 0x19, i8(min_), 0x29, i8(max_), -#define UsageRange_i16(min_, max_) 0x1a, LE16(min_), 0x2a, LE16(max_), +#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_), 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-asus.c b/drivers/hid/hid-asus.c index a4b47319ad8e..506c6f377e7d 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -1183,7 +1183,7 @@ static const __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); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 30de92d0bf0f..98bef39642a9 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. */ @@ -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; @@ -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); } @@ -754,35 +798,29 @@ static const u8 *fetch_item(const __u8 *start, const __u8 *end, struct hid_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) @@ -1205,7 +1243,6 @@ int hid_open_report(struct hid_device *device) struct hid_item item; unsigned int size; const __u8 *start; - __u8 *buf; const __u8 *end; const __u8 *next; int ret; @@ -1221,25 +1258,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 +1362,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: @@ -1875,7 +1881,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); @@ -2674,9 +2680,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) @@ -2684,6 +2691,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; @@ -2940,9 +2959,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; } /** diff --git a/drivers/hid/hid-corsair-void.c b/drivers/hid/hid-corsair-void.c new file mode 100644 index 000000000000..6ece56b850fc --- /dev/null +++ b/drivers/hid/hid-corsair-void.c @@ -0,0 +1,829 @@ +// 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_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-cp2112.c b/drivers/hid/hid-cp2112.c index dae2b84a1490..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); diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index d5abfe652fb5..541d682af15a 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -3309,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", @@ -3409,8 +3409,7 @@ static const char *keys[KEY_MAX + 1] = { [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_DIGI] = "Digi", [BTN_STYLUS3] = "Stylus3", - [BTN_TOOL_QUINTTAP] = "ToolQuintTap", [BTN_WHEEL] = "Wheel", + [BTN_STYLUS3] = "Stylus3", [BTN_TOOL_QUINTTAP] = "ToolQuintTap", [KEY_10CHANNELSDOWN] = "10ChannelsDown", [KEY_10CHANNELSUP] = "10ChannelsUp", [KEY_3D_MODE] = "3DMode", [KEY_ADDRESSBOOK] = "Addressbook", @@ -3440,7 +3439,7 @@ static const char *keys[KEY_MAX + 1] = { [KEY_FN_RIGHT_SHIFT] = "FnRightShift", [KEY_FRAMEBACK] = "FrameBack", [KEY_FRAMEFORWARD] = "FrameForward", [KEY_FULL_SCREEN] = "FullScreen", [KEY_GAMES] = "Games", [KEY_GRAPHICSEDITOR] = "GraphicsEditor", - [KEY_HANGEUL] = "HanGeul", [KEY_HANGUP_PHONE] = "HangUpPhone", + [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", 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-goodix-spi.c b/drivers/hid/hid-goodix-spi.c index 0e59663814dd..80c0288a3a38 100644 --- a/drivers/hid/hid-goodix-spi.c +++ b/drivers/hid/hid-goodix-spi.c @@ -4,7 +4,7 @@ * * Copyright (C) 2024 Godix, Inc. */ -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/delay.h> #include <linux/hid.h> #include <linux/interrupt.h> @@ -19,6 +19,8 @@ #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 @@ -348,7 +350,7 @@ static int goodix_hid_check_ack_status(struct goodix_ts_data *ts, u32 *resp_len) * - byte 0: Ack flag, value of 1 for data ready * - bytes 1-2: Response data length */ - error = goodix_spi_read(ts, ts->hid_report_addr, + 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); @@ -356,7 +358,7 @@ static int goodix_hid_check_ack_status(struct goodix_ts_data *ts, u32 *resp_len) dev_err(ts->dev, "hrd.size too short: %d", len); return -EINVAL; } - *resp_len = len; + *resp_len = len - GOODIX_HID_PKG_LEN_SIZE; return 0; } @@ -431,7 +433,7 @@ static int goodix_hid_get_raw_report(struct hid_device *hid, tx_len += args_len; /* Step1: write report request info */ - error = goodix_spi_write(ts, ts->hid_report_addr, tmp_buf, tx_len); + 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; @@ -446,9 +448,12 @@ static int goodix_hid_get_raw_report(struct hid_device *hid, if (error) return error; - len = min(len, response_data_len - GOODIX_HID_PKG_LEN_SIZE); + /* 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, ts->hid_report_addr + + error = goodix_spi_read(ts, GOODIX_HID_CMD_ADDR + GOODIX_HID_ACK_HEADER_SIZE + GOODIX_HID_PKG_LEN_SIZE, buf, len); if (error) { @@ -518,7 +523,7 @@ static int goodix_hid_set_raw_report(struct hid_device *hid, memcpy(tmp_buf + tx_len, buf, len); tx_len += len; - error = goodix_spi_write(ts, ts->hid_report_addr, tmp_buf, tx_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; @@ -697,12 +702,7 @@ static int goodix_spi_probe(struct spi_device *spi) return dev_err_probe(dev, PTR_ERR(ts->reset_gpio), "failed to request reset gpio\n"); - error = device_property_read_u32(dev, "goodix,hid-report-addr", - &ts->hid_report_addr); - if (error) - return dev_err_probe(dev, error, - "failed get hid report addr\n"); - + ts->hid_report_addr = GOODIX_HID_REPORT_ADDR; error = goodix_dev_confirm(ts); if (error) return error; @@ -749,7 +749,7 @@ static int goodix_spi_set_power(struct goodix_ts_data *ts, int power_state) power_control_cmd[5] = power_state; guard(mutex)(&ts->hid_request_lock); - error = goodix_spi_write(ts, ts->hid_report_addr, power_control_cmd, + 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", @@ -786,6 +786,14 @@ static const struct acpi_device_id goodix_spi_acpi_match[] = { 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" }, { }, @@ -796,6 +804,7 @@ 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, diff --git a/drivers/hid/hid-google-hammer.c b/drivers/hid/hid-google-hammer.c index 4e79fafeeafa..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" @@ -284,7 +284,7 @@ MODULE_DEVICE_TABLE(of, cbas_ec_of_match); static struct platform_driver cbas_ec_driver = { .probe = cbas_ec_probe, - .remove_new = cbas_ec_remove, + .remove = cbas_ec_remove, .driver = { .name = "cbas_ec", .acpi_match_table = ACPI_PTR(cbas_ec_acpi_ids), 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 86820a3d9766..1f47fda809b9 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 @@ -509,6 +510,7 @@ #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 @@ -734,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 @@ -868,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 @@ -1036,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 diff --git a/drivers/hid/hid-kye.c b/drivers/hid/hid-kye.c index 32344331282f..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> 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-lenovo.c b/drivers/hid/hid-lenovo.c index 3b0c779ce8f7..f66194fde891 100644 --- a/drivers/hid/hid-lenovo.c +++ b/drivers/hid/hid-lenovo.c @@ -473,6 +473,7 @@ static int lenovo_input_mapping(struct hid_device *hdev, return lenovo_input_mapping_tp10_ultrabook_kbd(hdev, hi, field, usage, bit, max); 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; @@ -583,6 +584,7 @@ static ssize_t attr_fn_lock_store(struct device *dev, break; 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; @@ -776,6 +778,7 @@ static int lenovo_event(struct hid_device *hdev, struct hid_field *field, return lenovo_event_cptkbd(hdev, field, usage, value); 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; @@ -1056,6 +1059,7 @@ static int lenovo_led_brightness_set(struct led_classdev *led_cdev, break; 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; } @@ -1286,6 +1290,7 @@ static int lenovo_probe(struct hid_device *hdev, break; 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: @@ -1372,6 +1377,7 @@ static void lenovo_remove(struct hid_device *hdev) break; 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; } @@ -1421,6 +1427,8 @@ 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) }, { } }; diff --git a/drivers/hid/hid-letsketch.c b/drivers/hid/hid-letsketch.c index 229820fda960..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" 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 d9580bc3e19a..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 diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 0e33fa0eb8db..10a3bc5f931b 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -23,7 +23,7 @@ #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" @@ -928,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; @@ -945,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; } @@ -1012,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; @@ -1125,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; @@ -1133,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; @@ -1300,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; } @@ -1489,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; } @@ -1692,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; @@ -1700,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; } @@ -1834,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); } /* -------------------------------------------------------------------------- */ @@ -1952,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; @@ -2014,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; @@ -2049,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; @@ -2076,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; @@ -2111,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; } @@ -2522,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: @@ -3098,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; @@ -3296,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); @@ -3362,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; @@ -3439,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; @@ -3735,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"); diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 8a73b59e0827..ec110dea8772 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -227,7 +227,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 +261,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 +362,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 +372,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 +500,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 +554,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 @@ -621,7 +632,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 +673,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 +699,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 +737,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 +783,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 +853,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 +870,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); @@ -920,7 +941,8 @@ static const __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 +973,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); diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 638e36c6d0f1..785743036647 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 @@ -1493,8 +1501,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); @@ -1549,11 +1556,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; } @@ -1561,7 +1568,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; @@ -1586,8 +1593,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; } @@ -1830,7 +1836,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; } @@ -1842,9 +1848,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; } @@ -1852,7 +1858,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; } @@ -1864,7 +1870,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; } @@ -2026,6 +2032,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, @@ -2095,6 +2105,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, @@ -2137,6 +2152,10 @@ static const struct hid_device_id mt_devices[] = { 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, diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c index 58cd0506e431..55153a2f7988 100644 --- a/drivers/hid/hid-nintendo.c +++ b/drivers/hid/hid-nintendo.c @@ -29,7 +29,7 @@ */ #include "hid-ids.h" -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/kernel.h> diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c index 83e3409d170c..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); @@ -497,6 +497,10 @@ int picolcd_init_framebuffer(struct picolcd_data *data) #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 061a33ba7b1d..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 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-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 0d90d7ee693c..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" diff --git a/drivers/hid/hid-sensor-custom.c b/drivers/hid/hid-sensor-custom.c index 66f0675df24b..761760668f6d 100644 --- a/drivers/hid/hid-sensor-custom.c +++ b/drivers/hid/hid-sensor-custom.c @@ -946,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); @@ -1065,7 +1065,7 @@ static struct platform_driver hid_sensor_custom_platform_driver = { .name = KBUILD_MODNAME, }, .probe = hid_sensor_custom_probe, - .remove_new = hid_sensor_custom_remove, + .remove = hid_sensor_custom_remove, }; module_platform_driver(hid_sensor_custom_platform_driver); diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index df29c614e490..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" @@ -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; diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c index bf8b633114be..6439913372a8 100644 --- a/drivers/hid/hid-steam.c +++ b/drivers/hid/hid-steam.c @@ -253,7 +253,7 @@ enum ID_CONTROLLER_DECK_STATE = 9 }; -/* String attribute idenitifiers */ +/* String attribute identifiers */ enum { ATTRIB_STR_BOARD_SERIAL, ATTRIB_STR_UNIT_SERIAL, diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index 7e83fee1ffa0..f9ff5be94309 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -411,6 +411,15 @@ static void steelseries_headset_fetch_battery(struct hid_device *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, @@ -442,6 +451,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; @@ -469,6 +481,7 @@ static enum power_supply_property steelseries_headset_battery_props[] = { 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) @@ -603,8 +616,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; @@ -631,6 +647,7 @@ static int steelseries_headset_raw_event(struct hid_device *hdev, power_supply_changed(sd->battery); } +request_battery: spin_lock_irqsave(&sd->lock, flags); if (!sd->removed) schedule_delayed_work(&sd->battery_work, diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c index 87fd4eb76c70..ef26c7defcf6 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 diff --git a/drivers/hid/hid-uclogic-rdesc-test.c b/drivers/hid/hid-uclogic-rdesc-test.c index d6b18213f45f..066df622b6f2 100644 --- a/drivers/hid/hid-uclogic-rdesc-test.c +++ b/drivers/hid/hid-uclogic-rdesc-test.c @@ -9,7 +9,7 @@ #include <kunit/test.h> #include "./hid-uclogic-rdesc.h" -MODULE_IMPORT_NS(EXPORTED_FOR_KUNIT_TESTING); +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); struct uclogic_template_case { const char *name; diff --git a/drivers/hid/hid-uclogic-rdesc.c b/drivers/hid/hid-uclogic-rdesc.c index 964d17e08f26..9b9cbc2aae36 100644 --- a/drivers/hid/hid-uclogic-rdesc.c +++ b/drivers/hid/hid-uclogic-rdesc.c @@ -16,7 +16,7 @@ #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 */ diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c index 2f8a9d3f1e86..43664a24176f 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,7 @@ #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) /* Command opcodes */ #define I2C_HID_OPCODE_RESET 0x01 @@ -140,6 +141,8 @@ static const struct i2c_hid_quirks { { 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 } }; @@ -981,6 +984,13 @@ static int i2c_hid_core_resume(struct i2c_hid *ihid) return -ENXIO; } + /* 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 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/ipc/pci-ish.c b/drivers/hid/intel-ish-hid/ipc/pci-ish.c index aae0d965b47b..9e2401291a2f 100644 --- a/drivers/hid/intel-ish-hid/ipc/pci-ish.c +++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c @@ -381,6 +381,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, @@ -388,6 +432,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); 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/client.c b/drivers/hid/intel-ish-hid/ishtp/client.c index 8a7f2f6a4f86..e61b01e9902e 100644 --- a/drivers/hid/intel-ish-hid/ishtp/client.c +++ b/drivers/hid/intel-ish-hid/ishtp/client.c @@ -863,7 +863,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/ishtp-dev.h b/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h index cdacce0a4c9d..effbb442c727 100644 --- a/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h +++ b/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h @@ -140,6 +140,13 @@ 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 */ @@ -236,6 +243,11 @@ 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; diff --git a/drivers/hid/intel-ish-hid/ishtp/loader.c b/drivers/hid/intel-ish-hid/ishtp/loader.c index f76c4437a1f5..f34086b29cf0 100644 --- a/drivers/hid/intel-ish-hid/ishtp/loader.c +++ b/drivers/hid/intel-ish-hid/ishtp/loader.c @@ -308,6 +308,28 @@ static int request_ish_firmware(const struct firmware **firmware_p, 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 @@ -336,6 +358,7 @@ void ishtp_loader_work(struct work_struct *work) 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; @@ -372,7 +395,7 @@ void ishtp_loader_work(struct work_struct *work) if (rv) continue; /* try again if failed */ - dev_dbg(dev->devc, "ISH Version %u.%u.%u.%u\n", + 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, @@ -390,6 +413,16 @@ void ishtp_loader_work(struct work_struct *work) 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); diff --git a/drivers/hid/intel-ish-hid/ishtp/loader.h b/drivers/hid/intel-ish-hid/ishtp/loader.h index 308b96085a4d..4dda038b4947 100644 --- a/drivers/hid/intel-ish-hid/ishtp/loader.h +++ b/drivers/hid/intel-ish-hid/ishtp/loader.h @@ -10,6 +10,7 @@ #include <linux/bits.h> #include <linux/jiffies.h> +#include <linux/sizes.h> #include <linux/types.h> #include "ishtp-dev.h" @@ -228,4 +229,37 @@ struct ish_firmware_variant { */ 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/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 8c0cbb2deb11..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> @@ -284,7 +284,7 @@ MODULE_DEVICE_TABLE(acpi, surface_kbd_match); static struct platform_driver surface_kbd_driver = { .probe = surface_kbd_probe, - .remove_new = surface_kbd_remove, + .remove = surface_kbd_remove, .driver = { .name = "surface_keyboard", .acpi_match_table = surface_kbd_match, diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index cb687ea7325c..a6eb6fe6130d 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -21,7 +21,7 @@ #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> @@ -1100,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); diff --git a/drivers/hid/wacom.h b/drivers/hid/wacom.h index 77c5fb26cd14..6f1443999d1d 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 diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 59a13ad9371c..5501a560fb07 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1353,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])); @@ -2422,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; @@ -2565,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 @@ -2617,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); } diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index c8803d5c6a71..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; |