mirror of
https://github.com/libretro/Lakka-LibreELEC.git
synced 2025-03-01 06:51:22 +00:00
188 lines
6.7 KiB
Diff
188 lines
6.7 KiB
Diff
From 377fcc446c4443dd1d35377d7379962abb88b484 Mon Sep 17 00:00:00 2001
|
|
From: "Daniel J. Ogorchock" <djogorchock@gmail.com>
|
|
Date: Sat, 14 Sep 2019 17:31:49 -0500
|
|
Subject: [PATCH] HID: nintendo: improve subcommand reliability
|
|
|
|
The controller occasionally doesn't respond to subcommands. It appears
|
|
that it's dropping them. To improve reliability, this patch attempts one
|
|
retry in the case of a synchronous send timeout. In testing, this has
|
|
resolved all timeout failures (most common for LED setting and rumble
|
|
setting subcommands).
|
|
|
|
The 1 second timeout is excessively long for rumble and LED subcommands,
|
|
so the timeout has been made a param for joycon_hid_send_sync. Most
|
|
subcommands continue to use the 1s timeout, since they can result in
|
|
long response times. Rumble and LED setting subcommands have been
|
|
reduced to 250ms, since response times for them are much quicker (and
|
|
this significantly reduces the observable impact in the case of a retry
|
|
being required).
|
|
|
|
Signed-off-by: Daniel J. Ogorchock <djogorchock@gmail.com>
|
|
---
|
|
drivers/hid/hid-nintendo.c | 66 ++++++++++++++++++++++++--------------
|
|
1 file changed, 42 insertions(+), 24 deletions(-)
|
|
|
|
diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c
|
|
index 21fa85802894..2638c6aff9d2 100644
|
|
--- a/drivers/hid/hid-nintendo.c
|
|
+++ b/drivers/hid/hid-nintendo.c
|
|
@@ -370,27 +370,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};
|
|
@@ -398,7 +416,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;
|
|
@@ -406,7 +424,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;
|
|
@@ -424,7 +442,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
|
|
@@ -443,7 +461,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;
|
|
@@ -474,7 +492,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",
|
|
@@ -564,7 +582,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)
|
|
@@ -577,7 +595,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)
|
|
@@ -1081,7 +1099,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;
|
|
@@ -1378,16 +1396,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;
|
|
@@ -1396,7 +1414,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 */
|