summaryrefslogtreecommitdiff
path: root/drivers/hid
diff options
context:
space:
mode:
authorDaniel J. Ogorchock <djogorchock@gmail.com>2021-09-11 13:36:30 -0400
committerJiri Kosina <jkosina@suse.cz>2021-10-27 10:05:51 +0200
commit479da173c43322411d8a81f77fae50414299d7d2 (patch)
tree1168323e8eb0b0260ff4b3191ba9a1d4f12092fd /drivers/hid
parent6b5dca2dea4eb797543c99541e97324f69db812a (diff)
HID: nintendo: send subcommands after receiving input report
Waiting to send subcommands until right after receiving an input report drastically improves subcommand reliability. If the driver has finished initial controller configuration, it now waits until receiving an input report for all subcommands. 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.c33
1 files changed, 33 insertions, 0 deletions
diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c
index a95943c2fc6c..a000a287206e 100644
--- a/drivers/hid/hid-nintendo.c
+++ b/drivers/hid/hid-nintendo.c
@@ -335,6 +335,7 @@ struct joycon_ctlr {
bool received_resp;
u8 usb_ack_match;
u8 subcmd_ack_match;
+ bool received_input_report;
/* factory calibration data */
struct joycon_stick_cal left_stick_cal_x;
@@ -388,6 +389,26 @@ 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");
+ }
+
ret = __joycon_hid_send(ctlr->hdev, data, len);
if (ret < 0) {
memset(ctlr->input_buf, 0, JC_MAX_RESP_SIZE);
@@ -760,6 +781,18 @@ static void joycon_parse_report(struct joycon_ctlr *ctlr,
}
input_sync(dev);
+
+ /*
+ * Immediately after receiving a report is the most reliable time to
+ * send a subcommand to the controller. Wake any subcommand senders
+ * waiting for a report.
+ */
+ if (unlikely(mutex_is_locked(&ctlr->output_mutex))) {
+ spin_lock_irqsave(&ctlr->lock, flags);
+ ctlr->received_input_report = true;
+ spin_unlock_irqrestore(&ctlr->lock, flags);
+ wake_up(&ctlr->wait);
+ }
}
static void joycon_rumble_worker(struct work_struct *work)