diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c
index ccbcb94616c9..df00d2a6504d 100644
--- a/drivers/hid/hid-nintendo.c
+++ b/drivers/hid/hid-nintendo.c
@@ -365,24 +365,42 @@ static int __joycon_hid_send(struct hid_device *hdev, u8 *data, size_t len)
return ret;
}
-static int joycon_hid_send_sync(struct joycon_ctlr *ctlr, u8 *data, size_t len)
+static int joycon_hid_send_sync(struct joycon_ctlr *ctlr, u8 *data, size_t len,
+ u32 timeout)
{
int ret;
+ int tries = 2;
- ret = __joycon_hid_send(ctlr->hdev, data, len);
- if (ret < 0) {
- memset(ctlr->input_buf, 0, JC_MAX_RESP_SIZE);
- return ret;
- }
+ /*
+ * The controller occasionally seems to drop subcommands. In testing,
+ * doing one retry after a timeout appears to always work.
+ */
+ while (tries--) {
+ ret = __joycon_hid_send(ctlr->hdev, data, len);
+ if (ret < 0) {
+ memset(ctlr->input_buf, 0, JC_MAX_RESP_SIZE);
+ return ret;
+ }
- if (!wait_event_timeout(ctlr->wait, ctlr->received_resp, HZ)) {
- hid_dbg(ctlr->hdev, "synchronous send/receive timed out\n");
- memset(ctlr->input_buf, 0, JC_MAX_RESP_SIZE);
- return -ETIMEDOUT;
+ ret = wait_event_timeout(ctlr->wait, ctlr->received_resp,
+ timeout);
+ if (!ret) {
+ hid_dbg(ctlr->hdev,
+ "synchronous send/receive timed out\n");
+ if (tries) {
+ hid_dbg(ctlr->hdev,
+ "retrying sync send after timeout\n");
+ }
+ memset(ctlr->input_buf, 0, JC_MAX_RESP_SIZE);
+ ret = -ETIMEDOUT;
+ } else {
+ ret = 0;
+ break;
+ }
}
ctlr->received_resp = false;
- return 0;
+ return ret;
}
static int joycon_send_usb(struct joycon_ctlr *ctlr, u8 cmd)@@ -393,7 +411,7 @@ static int joycon_send_usb(struct joycon_ctlr *ctlr, u8 cmd)
buf[1] = cmd;
ctlr->usb_ack_match = cmd;
ctlr->msg_type = JOYCON_MSG_TYPE_USB;
- ret = joycon_hid_send_sync(ctlr, buf, sizeof(buf));
+ ret = joycon_hid_send_sync(ctlr, buf, sizeof(buf), HZ);
if (ret)
hid_dbg(ctlr->hdev, "send usb command failed; ret=%d\n", ret);
return ret;
@@ -401,7 +419,7 @@ static int joycon_send_usb(struct joycon_ctlr *ctlr, u8 cmd)
static int joycon_send_subcmd(struct joycon_ctlr *ctlr,
struct joycon_subcmd_request *subcmd,
- size_t data_len)
+ size_t data_len, u32 timeout)
{
int ret;
unsigned long flags;@@ -419,7 +437,7 @@ static int joycon_send_subcmd(struct joycon_ctlr *ctlr,
ctlr->msg_type = JOYCON_MSG_TYPE_SUBCMD;
ret = joycon_hid_send_sync(ctlr, (u8 *)subcmd,
- sizeof(*subcmd) + data_len);
+ sizeof(*subcmd) + data_len, timeout);
if (ret < 0)
hid_dbg(ctlr->hdev, "send subcommand failed; ret=%d\n", ret);
else
@@ -438,7 +456,7 @@ static int joycon_set_player_leds(struct joycon_ctlr *ctlr, u8 flash, u8 on)
req->data[0] = (flash << 4) | on;
hid_dbg(ctlr->hdev, "setting player leds\n");
- return joycon_send_subcmd(ctlr, req, 1);
+ return joycon_send_subcmd(ctlr, req, 1, HZ/4);
}
static const u16 DFLT_STICK_CAL_CEN = 2000;
@@ -469,7 +487,7 @@ static int joycon_request_calibration(struct joycon_ctlr *ctlr)
data[4] = JC_CAL_DATA_SIZE;
hid_dbg(ctlr->hdev, "requesting cal data\n");
- ret = joycon_send_subcmd(ctlr, req, 5);
+ ret = joycon_send_subcmd(ctlr, req, 5, HZ);
if (ret) {
hid_warn(ctlr->hdev,
"Failed to read stick cal, using defaults; ret=%d\n",@@ -559,7 +577,7 @@ static int joycon_set_report_mode(struct joycon_ctlr *ctlr)
req->data[0] = 0x30; /* standard, full report mode */
hid_dbg(ctlr->hdev, "setting controller report mode\n");
- return joycon_send_subcmd(ctlr, req, 1);
+ return joycon_send_subcmd(ctlr, req, 1, HZ);
}
static int joycon_enable_rumble(struct joycon_ctlr *ctlr, bool enable)
@@ -572,7 +590,7 @@ static int joycon_enable_rumble(struct joycon_ctlr *ctlr, bool enable)
req->data[0] = enable ? 0x01 : 0x00;
hid_dbg(ctlr->hdev, "%s rumble\n", enable ? "enabling" : "disabling");
- return joycon_send_subcmd(ctlr, req, 1);
+ return joycon_send_subcmd(ctlr, req, 1, HZ/4);
}
static s32 joycon_map_stick_val(struct joycon_stick_cal *cal, s32 val)
@@ -1014,7 +1032,7 @@ static int joycon_home_led_brightness_set(struct led_classdev *led,
hid_dbg(hdev, "setting home led brightness\n");
mutex_lock(&ctlr->output_mutex);
- ret = joycon_send_subcmd(ctlr, req, 5);
+ ret = joycon_send_subcmd(ctlr, req, 5, HZ/4);
mutex_unlock(&ctlr->output_mutex);
return ret;
--
2.23.0