From 74972a68df1ed6285cb3b6fadbffc792f73cf138 Mon Sep 17 00:00:00 2001 From: CTCaer Date: Tue, 20 Jan 2026 20:03:38 +0200 Subject: [PATCH] bdk: joycon: make init stricter but relax timings Additionally, if both Joy-Con are found, try to send rumble simultaneously. --- bdk/input/joycon.c | 122 ++++++++++++++++++++++++++++++--------------- 1 file changed, 81 insertions(+), 41 deletions(-) diff --git a/bdk/input/joycon.c b/bdk/input/joycon.c index 99125b26..d8e8aeef 100644 --- a/bdk/input/joycon.c +++ b/bdk/input/joycon.c @@ -107,9 +107,11 @@ enum { JC_STATE_START = 0, JC_STATE_HANDSHAKED = 1, - JC_STATE_BRATE_CHANGED = 2, - JC_STATE_BRATE_OK = 3, - JC_STATE_INIT_DONE = 4 + JC_STATE_INFO_PARSED = 2, + JC_STATE_BRATE_CHANGED = 3, + JC_STATE_HID_NO_CONN = 4, + JC_STATE_HID_CONN = 5, + JC_STATE_INIT_DONE = 6 }; enum @@ -650,14 +652,12 @@ static void _jc_send_hid_cmd(jc_dev_t *jc, u8 subcmd, const u8 *data, u16 size) if (send_l_rumble) _jc_send_hid_output_rpt(&jc_l, hid_pkt, 0x10, false); - if (send_r_rumble || send_l_rumble) - { - msleep(2); - if (send_r_rumble) - _jc_rcv_pkt(&jc_r); - if (send_l_rumble) - _jc_rcv_pkt(&jc_l); - } + msleep(15); + + if (send_r_rumble) + _jc_rcv_pkt(&jc_r); + if (send_l_rumble) + _jc_rcv_pkt(&jc_l); // Send rumble. hid_pkt->cmd = JC_HID_RUMBLE_RPT; @@ -785,23 +785,35 @@ static void _jc_parse_wired_init(jc_dev_t *jc, const jc_wired_hdr_t *pkt, int si switch (pkt->subcmd) { case JC_WIRED_CMD_GET_INFO: - for (int i = 6; i > 0; i--) + if (!pkt->status) + { + for (int i = 6; i > 0; i--) jc->mac[6 - i] = payload[i]; - jc->type = payload[0]; - jc->connected = true; + jc->type = payload[0]; + jc->state = JC_STATE_INFO_PARSED; + } break; case JC_WIRED_CMD_SET_BRATE: - jc->state = JC_STATE_BRATE_CHANGED; + if (!pkt->status) + jc->state = JC_STATE_BRATE_CHANGED; break; case JC_WIRED_CMD_HID_DISC: - jc->state = JC_STATE_BRATE_OK; + if (pkt->status == 0xF) + jc->state = JC_STATE_HID_NO_CONN; break; case JC_WIRED_CMD_HID_CONN: + if (!pkt->status) + jc->state = JC_STATE_HID_CONN; + break; + case JC_WIRED_CMD_SET_HIDRATE: - // done. + jc->state = JC_STATE_INIT_DONE; + jc->connected = true; + break; + default: break; } @@ -870,12 +882,13 @@ static void _jc_sio_uart_pkt_parse(jc_dev_t *jc, const jc_sio_in_rpt_t *pkt, int switch (cmd) { case JC_SIO_CMD_INIT: - jc->connected = pkt->status == 0; + if (!pkt->status) + jc->state = JC_STATE_HANDSHAKED; break; case JC_SIO_CMD_VER_RPT: - if (jc->connected) - jc->connected = pkt->status == 0; + if (!pkt->status) + jc->state = JC_STATE_HID_CONN; break; case JC_SIO_CMD_IAP_VER: @@ -1012,9 +1025,16 @@ set_mode: static bool _jc_send_enable_rumble(jc_dev_t *jc) { + bool send_r_rumble = jc_r.connected && !jc_r.rumble_sent; + bool send_l_rumble = jc_l.connected && !jc_l.rumble_sent; + + // Do not sent report yet if second Joy-Con is expected to be initialized. + if ((send_r_rumble && !jc_l.rumble_sent && jc_l.state == JC_STATE_HID_CONN) || + (send_l_rumble && !jc_r.rumble_sent && jc_r.state == JC_STATE_HID_CONN)) + return 1; + // Send init rumble or request nx pad status report. - if ((jc_r.connected && !jc_r.rumble_sent) || - (jc_l.connected && !jc_l.rumble_sent)) + if (send_r_rumble || send_l_rumble) { _jc_send_hid_cmd(jc, JC_HID_SUBCMD_SND_RUMBLE, NULL, 0); @@ -1033,14 +1053,14 @@ static bool _jc_send_enable_rumble(jc_dev_t *jc) static void _jc_req_status(jc_dev_t *jc) { - if (!jc->detected) + if (!jc->connected) + return; + + if (jc->last_status_req_time > get_tmr_ms()) return; bool is_nxpad = !(jc->type & JC_ID_HORI) && !jc->sio_mode; - if (jc->last_status_req_time > get_tmr_ms() || !jc->connected) - return; - // Init/maintenance for Joy-Con. if (is_nxpad) { @@ -1258,24 +1278,26 @@ static void _jc_conn_init(jc_dev_t *jc) // Initialize uart to 1 megabaud and manual RTS. uart_init(jc->uart, 1000000, UART_AO_TX_MN_RX); + jc->state = JC_STATE_START; + if (!jc->sio_mode) { jc_gamepad.buttons = 0; - jc->state = JC_STATE_START; // Set TX and RTS inversion for Joycon. uart_invert(jc->uart, true, UART_INVERT_TXD | UART_INVERT_RTS); - // Wake up the controller. - _joycon_send_raw(jc->uart, _jc_init_wake, sizeof(_jc_init_wake)); - _jc_rcv_pkt(jc); // Clear RX FIFO. - - // Do a handshake. + // Initialize. u32 retries = 10; while (retries && jc->state != JC_STATE_HANDSHAKED) { + // Wake up the controller. + _joycon_send_raw(jc->uart, _jc_init_wake, sizeof(_jc_init_wake)); + _jc_rcv_pkt(jc); // Clear RX FIFO. + + // Do a handshake. _joycon_send_raw(jc->uart, _jc_init_handshake, sizeof(_jc_init_handshake)); - msleep(5); + msleep(4); _jc_rcv_pkt(jc); retries--; } @@ -1288,6 +1310,9 @@ static void _jc_conn_init(jc_dev_t *jc) msleep(2); _jc_rcv_pkt(jc); + if (jc->state != JC_STATE_INFO_PARSED) + goto out; + if (!(jc->type & JC_ID_HORI)) { // Request 3 megabaud change. @@ -1301,9 +1326,9 @@ static void _jc_conn_init(jc_dev_t *jc) uart_init(jc->uart, 3000000, UART_AO_TX_MN_RX); uart_invert(jc->uart, true, UART_INVERT_TXD | UART_INVERT_RTS); - // Disconnect HID. + // Make sure HID is disconnected and check connection. Reply expected after 30ms. retries = 10; - while (retries && jc->state != JC_STATE_BRATE_OK) + while (retries && jc->state != JC_STATE_HID_NO_CONN) { _joycon_send_raw(jc->uart, _jc_init_hid_disconnect, sizeof(_jc_init_hid_disconnect)); msleep(5); @@ -1311,7 +1336,8 @@ static void _jc_conn_init(jc_dev_t *jc) retries--; } - if (jc->state != JC_STATE_BRATE_OK) + // Was connected before or no response. Do a reinit. + if (jc->state != JC_STATE_HID_NO_CONN) goto out; } @@ -1320,10 +1346,15 @@ static void _jc_conn_init(jc_dev_t *jc) msleep(2); _jc_rcv_pkt(jc); + if (jc->state != JC_STATE_HID_CONN) + goto out; + // Set hid packet rate. _joycon_send_raw(jc->uart, _jc_init_set_hid_rate, sizeof(_jc_init_set_hid_rate)); msleep(2); _jc_rcv_pkt(jc); + + goto out; // Wait for set hid rate reply. } else // Hori. Unset RTS inversion. uart_invert(jc->uart, false, UART_INVERT_RTS); @@ -1342,7 +1373,7 @@ static void _jc_conn_init(jc_dev_t *jc) // Initialize the controller. u32 retries = 10; - while (!jc->connected && retries) + while (retries && jc->state != JC_STATE_HANDSHAKED) { _joycon_send_raw(jc->uart, _sio_init, sizeof(_sio_init)); msleep(5); @@ -1350,17 +1381,26 @@ static void _jc_conn_init(jc_dev_t *jc) retries--; } - if (!jc->connected) + if (jc->state != JC_STATE_HANDSHAKED) goto out; // Set output report version. - _joycon_send_raw(jc->uart, _sio_set_rpt_version, sizeof(_sio_set_rpt_version)); - msleep(5); - _jc_rcv_pkt(jc); + retries = 10; + while (retries && jc->state != JC_STATE_HID_CONN) + { + _joycon_send_raw(jc->uart, _sio_set_rpt_version, sizeof(_sio_set_rpt_version)); + msleep(5); + _jc_rcv_pkt(jc); + retries--; + } + + if (jc->state != JC_STATE_HID_CONN) + goto out; } // Initialization done. jc->state = JC_STATE_INIT_DONE; + jc->connected = true; out: jc->last_received_time = get_tmr_ms();