[PATCH RESEND 1/4] HID: winwing: factor out vendor SET_LEDX report builder

From: René Onier

Date: Wed Sep 30 2026 - 18:19:07 EST


The LED and the rumble paths each open-code the same 14-byte vendor
report byte by byte, leaving the meaning of every field to the reader.
Name the fields and build the report in one place.

A report is addressed to one of the controllers making up the product by
a (device, family) pair: the Orion 2 lighting controller answers on
0x60/0xbe, the two grip rumble motors on 0x01/0xbf and 0x03/0xbf.

No functional change: the bytes put on the wire are identical.

Signed-off-by: René Onier <f3nr1l@xxxxxx>
---
drivers/hid/hid-winwing.c | 123 +++++++++++++++++++-------------------
1 file changed, 60 insertions(+), 63 deletions(-)

diff --git a/drivers/hid/hid-winwing.c b/drivers/hid/hid-winwing.c
index cbfdb9c66e..a9bf81c19f 100644
--- a/drivers/hid/hid-winwing.c
+++ b/drivers/hid/hid-winwing.c
@@ -16,6 +16,30 @@

#define MAX_REPORT 16

+/*
+ * Vendor output report (report ID 2, 14 bytes):
+ *
+ * [0] report ID [1] device [2] family [3..4] zero
+ * [5] payload length [6] opcode [7..] arguments
+ *
+ * The device and family bytes select which of the controllers making up the
+ * product the report is addressed to; unknown reports are silently dropped.
+ */
+#define WINWING_REPORT_ID 0x02
+#define WINWING_REPORT_LEN 14
+
+#define WINWING_OP_SET_LEDX 0x49 /* arguments: LED index, value */
+#define WINWING_SET_LEDX_LEN 3
+
+/* Lighting controller of the Orion 2 throttle base */
+#define ORION2_LIGHT_DEVICE 0x60
+#define ORION2_LIGHT_FAMILY 0xbe
+
+/* Rumble motors of the TGRIP-15 grips, one report each */
+#define WINWING_STICK_FAMILY 0xbf
+#define WINWING_RUMBLE_LEFT_DEVICE 0x01
+#define WINWING_RUMBLE_RIGHT_DEVICE 0x03
+
struct winwing_led {
struct led_classdev cdev;
struct hid_device *hdev;
@@ -47,36 +71,41 @@ struct winwing_drv_data {
struct winwing_led leds[];
};

+/*
+ * Build a vendor report addressed to one controller and send it. The caller
+ * owns @buf and any serialization it needs.
+ *
+ * Mimicking requests captured by usbmon when the LEDs and the rumble motors
+ * are controlled by the vendor's app in a VM.
+ */
+static int winwing_send_set_ledx(struct hid_device *hdev, __u8 *buf,
+ __u8 device, __u8 family, __u8 index, __u8 value)
+{
+ memset(buf, 0, WINWING_REPORT_LEN);
+
+ buf[0] = WINWING_REPORT_ID;
+ buf[1] = device;
+ buf[2] = family;
+ buf[5] = WINWING_SET_LEDX_LEN;
+ buf[6] = WINWING_OP_SET_LEDX;
+ buf[7] = index;
+ buf[8] = value;
+
+ return hid_hw_output_report(hdev, buf, WINWING_REPORT_LEN);
+}
+
static int winwing_led_write(struct led_classdev *cdev,
enum led_brightness br)
{
struct winwing_led *led = (struct winwing_led *) cdev;
struct winwing_drv_data *data = hid_get_drvdata(led->hdev);
- __u8 *buf = data->report_lights;
int ret;

mutex_lock(&data->lights_lock);

- /*
- * Mimicking requests captured by usbmon when LEDs
- * are controlled by the vendor's app in a VM.
- */
- buf[0] = 0x02;
- buf[1] = 0x60;
- buf[2] = 0xbe;
- buf[3] = 0x00;
- buf[4] = 0x00;
- buf[5] = 0x03;
- buf[6] = 0x49;
- buf[7] = led->number;
- buf[8] = br;
- buf[9] = 0x00;
- buf[10] = 0;
- buf[11] = 0;
- buf[12] = 0;
- buf[13] = 0;
-
- ret = hid_hw_output_report(led->hdev, buf, 14);
+ ret = winwing_send_set_ledx(led->hdev, data->report_lights,
+ ORION2_LIGHT_DEVICE, ORION2_LIGHT_FAMILY,
+ led->number, br);

mutex_unlock(&data->lights_lock);

@@ -242,28 +271,12 @@ static int winwing_haptic_rumble(struct winwing_drv_data *data)
if (m != data->rumble_left) {
int ret;

- /*
- * Mimicking requests captured by usbmon when rumble
- * is activated by the vendor's app in a VM.
- */
- buf[0] = 0x02;
- buf[1] = 0x01;
- buf[2] = 0xbf;
- buf[3] = 0x00;
- buf[4] = 0x00;
- buf[5] = 0x03;
- buf[6] = 0x49;
- buf[7] = 0x00;
- buf[8] = m;
- buf[9] = 0x00;
- buf[10] = 0;
- buf[11] = 0;
- buf[12] = 0;
- buf[13] = 0;
-
- ret = hid_hw_output_report(data->hdev, buf, 14);
+ ret = winwing_send_set_ledx(data->hdev, buf,
+ WINWING_RUMBLE_LEFT_DEVICE,
+ WINWING_STICK_FAMILY, 0, m);
if (ret < 0) {
- hid_err(data->hdev, "error %d (%*ph)\n", ret, 14, buf);
+ hid_err(data->hdev, "error %d (%*ph)\n", ret,
+ WINWING_REPORT_LEN, buf);
return ret;
}
data->rumble_left = m;
@@ -273,28 +286,12 @@ static int winwing_haptic_rumble(struct winwing_drv_data *data)
if (m != data->rumble_right) {
int ret;

- /*
- * Mimicking requests captured by usbmon when rumble
- * is activated by the vendor's app in a VM.
- */
- buf[0] = 0x02;
- buf[1] = 0x03;
- buf[2] = 0xbf;
- buf[3] = 0x00;
- buf[4] = 0x00;
- buf[5] = 0x03;
- buf[6] = 0x49;
- buf[7] = 0x00;
- buf[8] = m;
- buf[9] = 0x00;
- buf[10] = 0;
- buf[11] = 0;
- buf[12] = 0;
- buf[13] = 0;
-
- ret = hid_hw_output_report(data->hdev, buf, 14);
+ ret = winwing_send_set_ledx(data->hdev, buf,
+ WINWING_RUMBLE_RIGHT_DEVICE,
+ WINWING_STICK_FAMILY, 0, m);
if (ret < 0) {
- hid_err(data->hdev, "error %d (%*ph)\n", ret, 14, buf);
+ hid_err(data->hdev, "error %d (%*ph)\n", ret,
+ WINWING_REPORT_LEN, buf);
return ret;
}
data->rumble_right = m;
--
2.55.0