summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/hid/hid-nintendo.c69
1 files changed, 49 insertions, 20 deletions
diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c
index 2351e1d8c8e0..ae3cd8ca9fa7 100644
--- a/drivers/hid/hid-nintendo.c
+++ b/drivers/hid/hid-nintendo.c
@@ -2,7 +2,7 @@
/*
* HID driver for Nintendo Switch Joy-Cons and Pro Controllers
*
- * Copyright (c) 2019-2020 Daniel J. Ogorchock <djogorchock@gmail.com>
+ * Copyright (c) 2019-2021 Daniel J. Ogorchock <djogorchock@gmail.com>
*
* The following resources/projects were referenced for this driver:
* https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering
@@ -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;
@@ -510,6 +511,50 @@ static int __joycon_hid_send(struct hid_device *hdev, u8 *data, size_t len)
return ret;
}
+static void joycon_wait_for_input_report(struct joycon_ctlr *ctlr)
+{
+ int ret;
+
+ /*
+ * If we are in the proper reporting mode, wait for an input
+ * report prior to sending the subcommand. This improves
+ * reliability considerably.
+ */
+ if (ctlr->ctlr_state == JOYCON_CTLR_STATE_READ) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctlr->lock, flags);
+ ctlr->received_input_report = false;
+ spin_unlock_irqrestore(&ctlr->lock, flags);
+ ret = wait_event_timeout(ctlr->wait,
+ ctlr->received_input_report,
+ HZ / 4);
+ /* We will still proceed, even with a timeout here */
+ if (!ret)
+ hid_warn(ctlr->hdev,
+ "timeout waiting for input report\n");
+ }
+}
+
+/*
+ * 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;
+
+ while (delta_ms < max_subcmd_rate_ms &&
+ ctlr->ctlr_state == JOYCON_CTLR_STATE_READ) {
+ joycon_wait_for_input_report(ctlr);
+ current_ms = jiffies_to_msecs(jiffies);
+ delta_ms = current_ms - ctlr->last_subcmd_sent_msecs;
+ }
+ ctlr->last_subcmd_sent_msecs = current_ms;
+}
+
static int joycon_hid_send_sync(struct joycon_ctlr *ctlr, u8 *data, size_t len,
u32 timeout)
{
@@ -521,25 +566,7 @@ static int joycon_hid_send_sync(struct joycon_ctlr *ctlr, u8 *data, size_t len,
* doing one retry after a timeout appears to always work.
*/
while (tries--) {
- /*
- * If we are in the proper reporting mode, wait for an input
- * report prior to sending the subcommand. This improves
- * reliability considerably.
- */
- if (ctlr->ctlr_state == JOYCON_CTLR_STATE_READ) {
- unsigned long flags;
-
- spin_lock_irqsave(&ctlr->lock, flags);
- ctlr->received_input_report = false;
- spin_unlock_irqrestore(&ctlr->lock, flags);
- ret = wait_event_timeout(ctlr->wait,
- ctlr->received_input_report,
- HZ / 4);
- /* We will still proceed, even with a timeout here */
- if (!ret)
- hid_warn(ctlr->hdev,
- "timeout waiting for input report\n");
- }
+ joycon_enforce_subcmd_rate(ctlr);
ret = __joycon_hid_send(ctlr->hdev, data, len);
if (ret < 0) {
@@ -1359,6 +1386,8 @@ static int joycon_send_rumble_data(struct joycon_ctlr *ctlr)
if (++ctlr->subcmd_num > 0xF)
ctlr->subcmd_num = 0;
+ joycon_enforce_subcmd_rate(ctlr);
+
ret = __joycon_hid_send(ctlr->hdev, (u8 *)&rumble_output,
sizeof(rumble_output));
return ret;