summaryrefslogtreecommitdiff
path: root/drivers/hid
diff options
context:
space:
mode:
authorDaniel J. Ogorchock <djogorchock@gmail.com>2021-09-11 13:36:37 -0400
committerJiri Kosina <jkosina@suse.cz>2021-10-27 10:05:52 +0200
commit4c048f6b2c25ff0fc0a7bdedc285ab8fe97df2fc (patch)
treea3deee127a42f1f742c38fb5422437d25926a958 /drivers/hid
parent4ff5b10840a88eb3d7609f08adfd663cac151152 (diff)
HID: nintendo: improve rumble performance and stability
This patch alters the method that the rumble data is sent to the controller. Rather than using the enable rumble subcommand for this purpose, the driver now employs the RUMBLE_ONLY output report. This has the advantage of not needing to receive a subcommand reply (to the major benefit of reducing IMU latency) and also seems to make the rumble vibrations more continuous. Perhaps most importantly it reduces disconnects during times of heavy rumble. Signed-off-by: Daniel J. Ogorchock <djogorchock@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/hid-nintendo.c38
1 files changed, 37 insertions, 1 deletions
diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c
index 1e8dc34f3980..2351e1d8c8e0 100644
--- a/drivers/hid/hid-nintendo.c
+++ b/drivers/hid/hid-nintendo.c
@@ -348,6 +348,12 @@ enum joycon_msg_type {
JOYCON_MSG_TYPE_SUBCMD,
};
+struct joycon_rumble_output {
+ u8 output_id;
+ u8 packet_num;
+ u8 rumble_data[8];
+} __packed;
+
struct joycon_subcmd_request {
u8 output_id; /* must be 0x01 for subcommand, 0x10 for rumble only */
u8 packet_num; /* incremented every send */
@@ -1328,6 +1334,36 @@ static void joycon_parse_report(struct joycon_ctlr *ctlr,
joycon_parse_imu_report(ctlr, rep);
}
+static int joycon_send_rumble_data(struct joycon_ctlr *ctlr)
+{
+ int ret;
+ unsigned long flags;
+ struct joycon_rumble_output rumble_output = { 0 };
+
+ spin_lock_irqsave(&ctlr->lock, flags);
+ /*
+ * If the controller has been removed, just return ENODEV so the LED
+ * subsystem doesn't print invalid errors on removal.
+ */
+ if (ctlr->ctlr_state == JOYCON_CTLR_STATE_REMOVED) {
+ spin_unlock_irqrestore(&ctlr->lock, flags);
+ return -ENODEV;
+ }
+ memcpy(rumble_output.rumble_data,
+ ctlr->rumble_data[ctlr->rumble_queue_tail],
+ JC_RUMBLE_DATA_SIZE);
+ spin_unlock_irqrestore(&ctlr->lock, flags);
+
+ rumble_output.output_id = JC_OUTPUT_RUMBLE_ONLY;
+ rumble_output.packet_num = ctlr->subcmd_num;
+ if (++ctlr->subcmd_num > 0xF)
+ ctlr->subcmd_num = 0;
+
+ ret = __joycon_hid_send(ctlr->hdev, (u8 *)&rumble_output,
+ sizeof(rumble_output));
+ return ret;
+}
+
static void joycon_rumble_worker(struct work_struct *work)
{
struct joycon_ctlr *ctlr = container_of(work, struct joycon_ctlr,
@@ -1338,7 +1374,7 @@ static void joycon_rumble_worker(struct work_struct *work)
while (again) {
mutex_lock(&ctlr->output_mutex);
- ret = joycon_enable_rumble(ctlr);
+ ret = joycon_send_rumble_data(ctlr);
mutex_unlock(&ctlr->output_mutex);
/* -ENODEV means the controller was just unplugged */