Skip to content

Commit

Permalink
HID: nintendo: send subcommands after receiving input report
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Daniel J. Ogorchock authored and Jiri Kosina committed Oct 27, 2021
1 parent 6b5dca2 commit 479da17
Showing 1 changed file with 33 additions and 0 deletions.
33 changes: 33 additions & 0 deletions drivers/hid/hid-nintendo.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 479da17

Please sign in to comment.