diff mbox series

[v14,15/17] HID: nintendo: ratelimit subcommands and rumble

Message ID 20210725032707.440071-16-djogorchock@gmail.com (mailing list archive)
State Superseded
Delegated to: Jiri Kosina
Headers show
Series HID: nintendo | expand

Commit Message

Daniel Ogorchock July 25, 2021, 3:27 a.m. UTC
It has been found that sending subcommands and rumble data packets at
too great a rate can result in controller disconnects. This patch limits
the rate of subcommands/rumble to once every 25 milliseconds.

Signed-off-by: Daniel J. Ogorchock <djogorchock@gmail.com>
---
 drivers/hid/hid-nintendo.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)
diff mbox series

Patch

diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c
index 9376f20baab00..71aa676ba3907 100644
--- a/drivers/hid/hid-nintendo.c
+++ b/drivers/hid/hid-nintendo.c
@@ -431,6 +431,7 @@  struct joycon_ctlr {
 	u8 usb_ack_match;
 	u8 subcmd_ack_match;
 	bool received_input_report;
+	unsigned int last_subcmd_sent_msecs;
 
 	/* factory calibration data */
 	struct joycon_stick_cal left_stick_cal_x;
@@ -582,6 +583,21 @@  static int joycon_send_usb(struct joycon_ctlr *ctlr, u8 cmd, u32 timeout)
 	return ret;
 }
 
+/*
+ * Sending subcommands and/or rumble data at too high a rate can cause bluetooth
+ * controller disconnections.
+ */
+static void joycon_enforce_subcmd_rate(struct joycon_ctlr *ctlr)
+{
+	static const unsigned int max_subcmd_rate_ms = 25;
+	unsigned int current_ms = jiffies_to_msecs(jiffies);
+	unsigned int delta_ms = current_ms - ctlr->last_subcmd_sent_msecs;
+
+	if (delta_ms < max_subcmd_rate_ms)
+		msleep(max_subcmd_rate_ms - delta_ms);
+	ctlr->last_subcmd_sent_msecs = current_ms;
+}
+
 static int joycon_send_subcmd(struct joycon_ctlr *ctlr,
 			      struct joycon_subcmd_request *subcmd,
 			      size_t data_len, u32 timeout)
@@ -589,6 +605,8 @@  static int joycon_send_subcmd(struct joycon_ctlr *ctlr,
 	int ret;
 	unsigned long flags;
 
+	joycon_enforce_subcmd_rate(ctlr);
+
 	spin_lock_irqsave(&ctlr->lock, flags);
 	/*
 	 * If the controller has been removed, just return ENODEV so the LED
@@ -1340,6 +1358,8 @@  static int joycon_send_rumble_data(struct joycon_ctlr *ctlr)
 	unsigned long flags;
 	struct joycon_rumble_output rumble_output = { 0 };
 
+	joycon_enforce_subcmd_rate(ctlr);
+
 	spin_lock_irqsave(&ctlr->lock, flags);
 	/*
 	 * If the controller has been removed, just return ENODEV so the LED