* [PATCH RESEND 1/4] HID: winwing: factor out vendor SET_LEDX report builder
2026-09-30 20:13 [PATCH RESEND 0/4] HID: winwing: add WinWing URSA MINOR sticks René Onier
@ 2026-09-30 20:13 ` René Onier
2026-09-30 20:13 ` [PATCH RESEND 2/4] HID: winwing: make the LED set and lighting controller model-dependent René Onier
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: René Onier @ 2026-09-30 20:13 UTC (permalink / raw)
To: Benjamin Tissoires, Jiri Kosina
Cc: René Onier, Ivan Gorinov, linux-input, linux-kernel
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@me.com>
---
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
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH RESEND 2/4] HID: winwing: make the LED set and lighting controller model-dependent
2026-09-30 20:13 [PATCH RESEND 0/4] HID: winwing: add WinWing URSA MINOR sticks René Onier
2026-09-30 20:13 ` [PATCH RESEND 1/4] HID: winwing: factor out vendor SET_LEDX report builder René Onier
@ 2026-09-30 20:13 ` René Onier
2026-09-30 20:13 ` [PATCH RESEND 3/4] HID: winwing: add URSA MINOR sticks René Onier
2026-09-30 20:13 ` [PATCH RESEND 4/4] HID: winwing: drive the URSA MINOR rumble motor René Onier
3 siblings, 0 replies; 5+ messages in thread
From: René Onier @ 2026-09-30 20:13 UTC (permalink / raw)
To: Benjamin Tissoires, Jiri Kosina
Cc: René Onier, Ivan Gorinov, linux-input, linux-kernel
The driver hardcodes the three LEDs of the Orion 2 throttle base and the
lighting controller they are addressed to. Other WinWing devices speaking
this protocol carry a different LED set on a different controller, so
move both to the per-device data and select them once at init time.
Turn has_grip15 into a quirk mask at the same time, so that a model can
be described by more than one bit.
No functional change: the Orion 2 keeps its three LEDs on device 0x60,
family 0xbe, and the same button mapping.
Signed-off-by: René Onier <f3nr1l@me.com>
---
drivers/hid/hid-winwing.c | 52 ++++++++++++++++++++++++++-------------
1 file changed, 35 insertions(+), 17 deletions(-)
diff --git a/drivers/hid/hid-winwing.c b/drivers/hid/hid-winwing.c
index a9bf81c19f..9ec0d2cf53 100644
--- a/drivers/hid/hid-winwing.c
+++ b/drivers/hid/hid-winwing.c
@@ -40,6 +40,9 @@
#define WINWING_RUMBLE_LEFT_DEVICE 0x01
#define WINWING_RUMBLE_RIGHT_DEVICE 0x03
+/* Grip with a rumble motor and more than 32 buttons */
+#define WINWING_GRIP15 BIT(0)
+
struct winwing_led {
struct led_classdev cdev;
struct hid_device *hdev;
@@ -52,7 +55,7 @@ struct winwing_led_info {
const char *led_name;
};
-static const struct winwing_led_info led_info[3] = {
+static const struct winwing_led_info orion2_led_info[] = {
{ 0, 255, "backlight" },
{ 1, 1, "a-a" },
{ 2, 1, "a-g" },
@@ -67,7 +70,13 @@ struct winwing_drv_data {
struct ff_rumble_effect rumble;
int rumble_left;
int rumble_right;
- int has_grip15;
+ unsigned long quirks;
+
+ /* Lighting controller and LED set of this model */
+ unsigned int num_leds;
+ __u8 led_device;
+ __u8 led_family;
+
struct winwing_led leds[];
};
@@ -104,7 +113,7 @@ static int winwing_led_write(struct led_classdev *cdev,
mutex_lock(&data->lights_lock);
ret = winwing_send_set_ledx(led->hdev, data->report_lights,
- ORION2_LIGHT_DEVICE, ORION2_LIGHT_FAMILY,
+ data->led_device, data->led_family,
led->number, br);
mutex_unlock(&data->lights_lock);
@@ -115,10 +124,11 @@ static int winwing_led_write(struct led_classdev *cdev,
static int winwing_init_led(struct hid_device *hdev,
struct input_dev *input)
{
+ const struct winwing_led_info *table;
struct winwing_drv_data *data;
struct winwing_led *led;
- int ret;
- int i;
+ int ret = 0;
+ unsigned int i;
data = hid_get_drvdata(hdev);
@@ -130,8 +140,13 @@ static int winwing_init_led(struct hid_device *hdev,
if (!data->report_lights)
return -ENOMEM;
- for (i = 0; i < 3; i += 1) {
- const struct winwing_led_info *info = &led_info[i];
+ table = orion2_led_info;
+ data->num_leds = ARRAY_SIZE(orion2_led_info);
+ data->led_device = ORION2_LIGHT_DEVICE;
+ data->led_family = ORION2_LIGHT_FAMILY;
+
+ for (i = 0; i < data->num_leds; i += 1) {
+ const struct winwing_led_info *info = &table[i];
led = &data->leds[i];
led->hdev = hdev;
@@ -155,7 +170,7 @@ static int winwing_init_led(struct hid_device *hdev,
return ret;
}
-static int winwing_map_button(int button, int has_grip15)
+static int winwing_map_button(int button, int map_more_buttons)
{
if (button < 1)
return KEY_RESERVED;
@@ -179,7 +194,7 @@ static int winwing_map_button(int button, int has_grip15)
return (button - 65) + BTN_TRIGGER_HAPPY17;
}
- if (!has_grip15) {
+ if (!map_more_buttons) {
/*
* Not mapping numbers [33 .. 64] which
* are not assigned to any real buttons
@@ -232,7 +247,7 @@ static int winwing_input_mapping(struct hid_device *hdev,
/* Button numbers start with 1 */
button = usage->hid & HID_USAGE;
- code = winwing_map_button(button, data->has_grip15);
+ code = winwing_map_button(button, data->quirks & WINWING_GRIP15);
hid_map_usage(hi, usage, bit, max, EV_KEY, code);
@@ -368,7 +383,7 @@ static int winwing_probe(struct hid_device *hdev,
return -ENOMEM;
data->hdev = hdev;
- data->has_grip15 = id->driver_data;
+ data->quirks = id->driver_data;
hid_set_drvdata(hdev, data);
mutex_init(&data->lights_lock);
@@ -414,18 +429,21 @@ static int winwing_input_configured(struct hid_device *hdev,
if (ret)
hid_err(hdev, "led init failed\n");
- if (data->has_grip15)
+ if (data->quirks & WINWING_GRIP15)
winwing_init_ff(hdev, hidinput);
return ret;
}
-/* Set driver_data to 1 for grips with rumble motor and more than 32 buttons */
static const struct hid_device_id winwing_devices[] = {
- { HID_USB_DEVICE(0x4098, 0xbd65), .driver_data = 1 }, /* TGRIP-15E */
- { HID_USB_DEVICE(0x4098, 0xbd64), .driver_data = 1 }, /* TGRIP-15EX */
- { HID_USB_DEVICE(0x4098, 0xbe68), .driver_data = 0 }, /* TGRIP-16EX */
- { HID_USB_DEVICE(0x4098, 0xbe62), .driver_data = 0 }, /* TGRIP-18 */
+ { HID_USB_DEVICE(0x4098, 0xbd65), /* TGRIP-15E */
+ .driver_data = WINWING_GRIP15 },
+ { HID_USB_DEVICE(0x4098, 0xbd64), /* TGRIP-15EX */
+ .driver_data = WINWING_GRIP15 },
+ { HID_USB_DEVICE(0x4098, 0xbe68), /* TGRIP-16EX */
+ .driver_data = 0 },
+ { HID_USB_DEVICE(0x4098, 0xbe62), /* TGRIP-18 */
+ .driver_data = 0 },
{}
};
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH RESEND 3/4] HID: winwing: add URSA MINOR sticks
2026-09-30 20:13 [PATCH RESEND 0/4] HID: winwing: add WinWing URSA MINOR sticks René Onier
2026-09-30 20:13 ` [PATCH RESEND 1/4] HID: winwing: factor out vendor SET_LEDX report builder René Onier
2026-09-30 20:13 ` [PATCH RESEND 2/4] HID: winwing: make the LED set and lighting controller model-dependent René Onier
@ 2026-09-30 20:13 ` René Onier
2026-09-30 20:13 ` [PATCH RESEND 4/4] HID: winwing: drive the URSA MINOR rumble motor René Onier
3 siblings, 0 replies; 5+ messages in thread
From: René Onier @ 2026-09-30 20:13 UTC (permalink / raw)
To: Benjamin Tissoires, Jiri Kosina
Cc: René Onier, Ivan Gorinov, linux-input, linux-kernel
The URSA MINOR is a single-handed stick sold as a left/right pair, each
hand enumerating as its own USB device. It speaks the same vendor
protocol as the Orion 2 throttle, so reuse the LED code for it.
A stick exposes two controllers on the same endpoint, both named by the
vendor software: the base, J5_BASE, which answers on device 0x20, family
0xbb, and drives a single backlight, and the grip, JGRIP_F1_L or
JGRIP_F1_R, which carries the rumble motor. The a-a and a-g indicators of
the Orion 2 belong to an F/A-18 throttle panel and have no counterpart on
a stick, so give the sticks their own LED table.
The grips carry more than 32 buttons and so take the extended button
mapping as well. Their rumble motor is driven by a report the two-motor
throttle path does not send, and is added separately.
Signed-off-by: René Onier <f3nr1l@me.com>
---
drivers/hid/hid-winwing.c | 54 ++++++++++++++++++++++++++++++++++-----
1 file changed, 47 insertions(+), 7 deletions(-)
diff --git a/drivers/hid/hid-winwing.c b/drivers/hid/hid-winwing.c
index 9ec0d2cf53..a52e7efece 100644
--- a/drivers/hid/hid-winwing.c
+++ b/drivers/hid/hid-winwing.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * HID driver for WinWing Orion 2 throttle
+ * HID driver for WinWing Orion 2 throttle and URSA MINOR sticks
*
* Copyright (c) 2023 Ivan Gorinov
*/
@@ -35,6 +35,16 @@
#define ORION2_LIGHT_DEVICE 0x60
#define ORION2_LIGHT_FAMILY 0xbe
+/*
+ * Each URSA MINOR stick exposes two controllers on the same endpoint. The
+ * vendor software names them J5_BASE (0xbb20), the base, which carries the
+ * backlight, and JGRIP_F1_L and JGRIP_F1_R (0xbf09 and 0xbf0a), the grips,
+ * which carry the rumble motor. Each of these ids is the family byte followed
+ * by the device byte. The base answers on the same id on either hand.
+ */
+#define URSA_LIGHT_DEVICE 0x20
+#define URSA_LIGHT_FAMILY 0xbb
+
/* Rumble motors of the TGRIP-15 grips, one report each */
#define WINWING_STICK_FAMILY 0xbf
#define WINWING_RUMBLE_LEFT_DEVICE 0x01
@@ -42,6 +52,8 @@
/* Grip with a rumble motor and more than 32 buttons */
#define WINWING_GRIP15 BIT(0)
+/* URSA MINOR stick, one USB device per hand */
+#define WINWING_URSA_MINOR BIT(1)
struct winwing_led {
struct led_classdev cdev;
@@ -61,6 +73,17 @@ static const struct winwing_led_info orion2_led_info[] = {
{ 2, 1, "a-g" },
};
+/*
+ * The URSA MINOR sticks have a single monochrome backlight. The a-a and a-g
+ * indicators of the Orion 2 belong to an F/A-18 throttle panel and have no
+ * counterpart here.
+ */
+static const struct winwing_led_info ursa_led_info[] = {
+ { 0, 255, "backlight" },
+};
+
+#define WINWING_MAX_LEDS ARRAY_SIZE(orion2_led_info)
+
struct winwing_drv_data {
struct hid_device *hdev;
struct mutex lights_lock;
@@ -140,10 +163,17 @@ static int winwing_init_led(struct hid_device *hdev,
if (!data->report_lights)
return -ENOMEM;
- table = orion2_led_info;
- data->num_leds = ARRAY_SIZE(orion2_led_info);
- data->led_device = ORION2_LIGHT_DEVICE;
- data->led_family = ORION2_LIGHT_FAMILY;
+ if (data->quirks & WINWING_URSA_MINOR) {
+ table = ursa_led_info;
+ data->num_leds = ARRAY_SIZE(ursa_led_info);
+ data->led_device = URSA_LIGHT_DEVICE;
+ data->led_family = URSA_LIGHT_FAMILY;
+ } else {
+ table = orion2_led_info;
+ data->num_leds = ARRAY_SIZE(orion2_led_info);
+ data->led_device = ORION2_LIGHT_DEVICE;
+ data->led_family = ORION2_LIGHT_FAMILY;
+ }
for (i = 0; i < data->num_leds; i += 1) {
const struct winwing_led_info *info = &table[i];
@@ -368,7 +398,7 @@ static int winwing_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
struct winwing_drv_data *data;
- size_t data_size = struct_size(data, leds, 3);
+ size_t data_size = struct_size(data, leds, WINWING_MAX_LEDS);
int ret;
ret = hid_parse(hdev);
@@ -444,6 +474,16 @@ static const struct hid_device_id winwing_devices[] = {
.driver_data = 0 },
{ HID_USB_DEVICE(0x4098, 0xbe62), /* TGRIP-18 */
.driver_data = 0 },
+ /*
+ * The Fighter and Space variants are electrically identical and share
+ * these product ids; only the stick tilt accessory differs. The Civil
+ * variant, which has fewer buttons, is likely compatible, but its
+ * product ids have not been verified on hardware.
+ */
+ { HID_USB_DEVICE(0x4098, 0xbc2a), /* URSA MINOR R */
+ .driver_data = WINWING_GRIP15 | WINWING_URSA_MINOR },
+ { HID_USB_DEVICE(0x4098, 0xbc29), /* URSA MINOR L */
+ .driver_data = WINWING_GRIP15 | WINWING_URSA_MINOR },
{}
};
@@ -459,5 +499,5 @@ static struct hid_driver winwing_driver = {
};
module_hid_driver(winwing_driver);
-MODULE_DESCRIPTION("HID driver for WinWing Orion 2 throttle");
+MODULE_DESCRIPTION("HID driver for WinWing Orion 2 throttle and URSA MINOR sticks");
MODULE_LICENSE("GPL");
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH RESEND 4/4] HID: winwing: drive the URSA MINOR rumble motor
2026-09-30 20:13 [PATCH RESEND 0/4] HID: winwing: add WinWing URSA MINOR sticks René Onier
` (2 preceding siblings ...)
2026-09-30 20:13 ` [PATCH RESEND 3/4] HID: winwing: add URSA MINOR sticks René Onier
@ 2026-09-30 20:13 ` René Onier
3 siblings, 0 replies; 5+ messages in thread
From: René Onier @ 2026-09-30 20:13 UTC (permalink / raw)
To: Benjamin Tissoires, Jiri Kosina
Cc: René Onier, Ivan Gorinov, linux-input, linux-kernel
An URSA MINOR stick has a single rumble motor, carried by the grip rather
than by the base and addressed by its own device number, derived from the
product id. The two-motor throttle path sends to device 0x01 and 0x03,
which a stick ignores, so give it its own path.
Both magnitudes feed the one motor and the stronger one wins, which is
what the memoryless force-feedback core expects of a device that cannot
drive them separately.
The Orion 2 keeps its two motors.
Signed-off-by: René Onier <f3nr1l@me.com>
---
drivers/hid/hid-winwing.c | 50 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 49 insertions(+), 1 deletion(-)
diff --git a/drivers/hid/hid-winwing.c b/drivers/hid/hid-winwing.c
index a52e7efece..86caff9183 100644
--- a/drivers/hid/hid-winwing.c
+++ b/drivers/hid/hid-winwing.c
@@ -4,6 +4,7 @@
* HID driver for WinWing Orion 2 throttle and URSA MINOR sticks
*
* Copyright (c) 2023 Ivan Gorinov
+ * Copyright (c) 2026 René Onier
*/
#include <linux/device.h>
@@ -45,8 +46,10 @@
#define URSA_LIGHT_DEVICE 0x20
#define URSA_LIGHT_FAMILY 0xbb
-/* Rumble motors of the TGRIP-15 grips, one report each */
+/* Rumble motors answer on the grip, not on the base */
#define WINWING_STICK_FAMILY 0xbf
+
+/* The TGRIP-15 grips have two motors, each taking its own report */
#define WINWING_RUMBLE_LEFT_DEVICE 0x01
#define WINWING_RUMBLE_RIGHT_DEVICE 0x03
@@ -95,6 +98,10 @@ struct winwing_drv_data {
int rumble_right;
unsigned long quirks;
+ /* URSA MINOR: single motor, on the grip */
+ __u8 rumble_device;
+ int rumble_sent;
+
/* Lighting controller and LED set of this model */
unsigned int num_leds;
__u8 led_device;
@@ -296,6 +303,40 @@ static inline int convert_magnitude(int x)
return ((x * 255) >> 16) + 1;
}
+/* Device number of the grip, 0x09 for the left stick and 0x0a for the right */
+static inline __u8 winwing_ursa_device(__u32 product)
+{
+ return (product & 0xff) - 0x20;
+}
+
+/*
+ * An URSA MINOR stick has a single motor, driven by the same opcode as the
+ * lights but addressed to the grip instead of the base. Both magnitudes feed
+ * it and the stronger one wins.
+ */
+static int winwing_ursa_rumble(struct winwing_drv_data *data, __u8 *buf)
+{
+ int ret;
+ __u8 m;
+
+ m = convert_magnitude(max(data->rumble.strong_magnitude,
+ data->rumble.weak_magnitude));
+
+ if (m == data->rumble_sent)
+ return 0;
+
+ ret = winwing_send_set_ledx(data->hdev, buf, data->rumble_device,
+ WINWING_STICK_FAMILY, 0, m);
+ if (ret < 0) {
+ hid_err(data->hdev, "error %d (%*ph)\n", ret,
+ WINWING_REPORT_LEN, buf);
+ return ret;
+ }
+ data->rumble_sent = m;
+
+ return 0;
+}
+
static int winwing_haptic_rumble(struct winwing_drv_data *data)
{
__u8 *buf;
@@ -312,6 +353,9 @@ static int winwing_haptic_rumble(struct winwing_drv_data *data)
if (!buf)
return -EINVAL;
+ if (data->quirks & WINWING_URSA_MINOR)
+ return winwing_ursa_rumble(data, buf);
+
m = convert_magnitude(data->rumble.strong_magnitude);
if (m != data->rumble_left) {
int ret;
@@ -382,6 +426,10 @@ static int winwing_init_ff(struct hid_device *hdev, struct hid_input *hidinput)
data->report_rumble = devm_kzalloc(&hdev->dev, MAX_REPORT, GFP_KERNEL);
data->rumble_left = -1;
data->rumble_right = -1;
+ data->rumble_sent = -1;
+
+ if (data->quirks & WINWING_URSA_MINOR)
+ data->rumble_device = winwing_ursa_device(hdev->product);
input_set_capability(hidinput->input, EV_FF, FF_RUMBLE);
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread