summaryrefslogtreecommitdiff
path: root/drivers/hid/hid-nintendo.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hid/hid-nintendo.c')
-rw-r--r--drivers/hid/hid-nintendo.c66
1 files changed, 42 insertions, 24 deletions
diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c
index 1a1bb18a0216..a95943c2fc6c 100644
--- a/drivers/hid/hid-nintendo.c
+++ b/drivers/hid/hid-nintendo.c
@@ -377,27 +377,45 @@ 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)
+static int joycon_send_usb(struct joycon_ctlr *ctlr, u8 cmd, u32 timeout)
{
int ret;
u8 buf[2] = {JC_OUTPUT_USB_CMD};
@@ -405,7 +423,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), timeout);
if (ret)
hid_dbg(ctlr->hdev, "send usb command failed; ret=%d\n", ret);
return ret;
@@ -413,7 +431,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;
@@ -431,7 +449,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
@@ -450,7 +468,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;
@@ -481,7 +499,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",
@@ -571,7 +589,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)
@@ -584,7 +602,7 @@ static int joycon_enable_rumble(struct joycon_ctlr *ctlr)
req->data[0] = 0x01; /* note: 0x00 would disable */
hid_dbg(ctlr->hdev, "enabling rumble\n");
- 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)
@@ -1088,7 +1106,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;
@@ -1381,16 +1399,16 @@ static int nintendo_hid_probe(struct hid_device *hdev,
mutex_lock(&ctlr->output_mutex);
/* if handshake command fails, assume ble pro controller */
if (hdev->product == USB_DEVICE_ID_NINTENDO_PROCON &&
- !joycon_send_usb(ctlr, JC_USB_CMD_HANDSHAKE)) {
+ !joycon_send_usb(ctlr, JC_USB_CMD_HANDSHAKE, HZ)) {
hid_dbg(hdev, "detected USB controller\n");
/* set baudrate for improved latency */
- ret = joycon_send_usb(ctlr, JC_USB_CMD_BAUDRATE_3M);
+ ret = joycon_send_usb(ctlr, JC_USB_CMD_BAUDRATE_3M, HZ);
if (ret) {
hid_err(hdev, "Failed to set baudrate; ret=%d\n", ret);
goto err_mutex;
}
/* handshake */
- ret = joycon_send_usb(ctlr, JC_USB_CMD_HANDSHAKE);
+ ret = joycon_send_usb(ctlr, JC_USB_CMD_HANDSHAKE, HZ);
if (ret) {
hid_err(hdev, "Failed handshake; ret=%d\n", ret);
goto err_mutex;
@@ -1399,7 +1417,7 @@ static int nintendo_hid_probe(struct hid_device *hdev,
* Set no timeout (to keep controller in USB mode).
* This doesn't send a response, so ignore the timeout.
*/
- joycon_send_usb(ctlr, JC_USB_CMD_NO_TIMEOUT);
+ joycon_send_usb(ctlr, JC_USB_CMD_NO_TIMEOUT, HZ/10);
}
/* get controller calibration data, and parse it */