From 2aed1b3b830e8e4845f7b19ce3714956e78091ca Mon Sep 17 00:00:00 2001 From: CTCaer Date: Mon, 9 May 2022 05:48:10 +0300 Subject: [PATCH] bdk: joycon: add 3 mbaud support and full init Additionally use states for proper init --- bdk/input/joycon.c | 115 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 109 insertions(+), 6 deletions(-) diff --git a/bdk/input/joycon.c b/bdk/input/joycon.c index 958a96e..52a1402 100644 --- a/bdk/input/joycon.c +++ b/bdk/input/joycon.c @@ -44,6 +44,9 @@ #define JC_WIRED_CMD_GET_INFO 0x01 #define JC_WIRED_CMD_INIT_DONE 0x10 +#define JC_WIRED_CMD_BRATE_DONE 0x11 +#define JC_WIRED_CMD_SET_RATE 0x12 // Output report rate. +#define JC_WIRED_CMD_SET_BRATE 0x20 #define JC_HID_OUTPUT_RPT 0x01 #define JC_HID_RUMBLE_RPT 0x10 @@ -70,6 +73,15 @@ #define JC_CRC8_POLY 0x8D +enum +{ + JC_STATE_START = 0, + JC_STATE_HANDSHAKED = 1, + JC_STATE_BRATE_CHANGED = 2, + JC_STATE_BRATE_OK = 3, + JC_STATE_INIT_DONE = 4 +}; + enum { JC_BATT_EMTPY = 0, @@ -95,6 +107,32 @@ static const u8 init_get_info[] = { 0x00, 0x00, 0x00, 0x00, 0x24 // Wired subcmd data and crc. }; +static const u8 init_switch_brate[] = { + 0x19, 0x01, 0x03, 0x0F, 0x00, // Uart header. + JC_WIRED_CMD, JC_WIRED_CMD_SET_BRATE, // Wired cmd and subcmd. + 0x08, 0x00, 0x00, 0xBD, 0xB1, // Wired subcmd data, data crc and crc. + // Baudrate 3 megabaud. + 0xC0, 0xC6, 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const u8 init_switched_brate[] = { + 0x19, 0x01, 0x03, 0x07, 0x00, // Uart header. + JC_WIRED_CMD, JC_WIRED_CMD_BRATE_DONE, // Wired cmd and subcmd. + 0x00, 0x00, 0x00, 0x00, 0x0E // Wired subcmd data and crc. +}; + +static const u8 init_set_rpt_rate[] = { + 0x19, 0x01, 0x03, 0x0B, 0x00, // Uart header. + JC_WIRED_CMD, JC_WIRED_CMD_SET_RATE, // Wired cmd and subcmd. + 0x04, 0x00, 0x00, 0x12, 0xA6, // Wired subcmd data, data crc and crc. + // Output report rate 15 ms. + 0x0F, 0x00, 0x00, 0x00 + + // 5 ms. + // 0x04, 0x00, 0x00, 0x0E, 0xD5, + // 0x05, 0x00, 0x00, 0x00 +}; + static const u8 init_finalize[] = { 0x19, 0x01, 0x03, 0x07, 0x00, // Uart header. JC_WIRED_CMD, JC_WIRED_CMD_INIT_DONE, // Wired cmd and subcmd. @@ -189,6 +227,7 @@ typedef struct _joycon_ctxt_t u8 buf[0x100]; //FIXME: If heap is used, dumping breaks. u8 uart; u8 type; + u8 state; u8 mac[6]; u32 last_received_time; u32 last_status_req_time; @@ -521,7 +560,6 @@ static void _jc_parse_wired_hid(joycon_ctxt_t *jc, const u8* packet, u32 size) default: break; } - jc->last_received_time = get_tmr_ms(); } static void _jc_parse_wired_init(joycon_ctxt_t *jc, const u8* data, u32 size) @@ -533,6 +571,16 @@ static void _jc_parse_wired_init(joycon_ctxt_t *jc, const u8* data, u32 size) jc->mac[12 - i] = data[i]; jc->type = data[6]; jc->connected = true; + break; + case JC_WIRED_CMD_SET_BRATE: + jc->state = JC_STATE_BRATE_CHANGED; + break; + case JC_WIRED_CMD_BRATE_DONE: + jc->state = JC_STATE_BRATE_OK; + break; + case JC_WIRED_CMD_INIT_DONE: + case JC_WIRED_CMD_SET_RATE: + // done. default: break; } @@ -550,9 +598,14 @@ static void jc_uart_pkt_parse(joycon_ctxt_t *jc, const u8* packet, size_t size) case JC_WIRED_INIT_REPLY: _jc_parse_wired_init(jc, pkt->data, size - sizeof(jc_uart_hdr_t) - 1); break; + case JC_INIT_HANDSHAKE: + jc->state = JC_STATE_HANDSHAKED; + break; default: break; } + + jc->last_received_time = get_tmr_ms(); } static void _jc_rcv_pkt(joycon_ctxt_t *jc) @@ -795,26 +848,76 @@ static void _jc_init_conn(joycon_ctxt_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; + uart_invert(jc->uart, true, UART_INVERT_TXD); uart_set_IIR(jc->uart); + // Wake up the controller. _joycon_send_raw(jc->uart, init_wake, sizeof(init_wake)); - _joycon_send_raw(jc->uart, init_handshake, sizeof(init_handshake)); + _jc_rcv_pkt(jc); // Clear RX FIFO. - msleep(5); - _jc_rcv_pkt(jc); + // Do a handshake. + u32 retries = 10; + while (retries && jc->state != JC_STATE_HANDSHAKED) + { + _joycon_send_raw(jc->uart, init_handshake, sizeof(init_handshake)); + msleep(5); + _jc_rcv_pkt(jc); + retries--; + } + + if (jc->state != JC_STATE_HANDSHAKED) + goto out; + + // Get info about the controller. _joycon_send_raw(jc->uart, init_get_info, sizeof(init_get_info)); - msleep(5); + msleep(2); _jc_rcv_pkt(jc); if (!(jc->type & JC_ID_HORI)) { + // Request 3 megabaud change. + _joycon_send_raw(jc->uart, init_switch_brate, sizeof(init_switch_brate)); + msleep(2); + _jc_rcv_pkt(jc); + + if (jc->state == JC_STATE_BRATE_CHANGED) + { + // Reinitialize uart to 3 megabaud and manual RTS. + uart_init(jc->uart, 3000000, UART_AO_TX_MN_RX); + uart_invert(jc->uart, true, UART_INVERT_TXD | UART_INVERT_RTS); + + // Handshake with the new speed. + retries = 10; + while (retries && jc->state != JC_STATE_BRATE_OK) + { + _joycon_send_raw(jc->uart, init_switched_brate, sizeof(init_switched_brate)); + msleep(5); + _jc_rcv_pkt(jc); + retries--; + } + + if (jc->state != JC_STATE_BRATE_OK) + goto out; + } + + // Finalize initialization. _joycon_send_raw(jc->uart, init_finalize, sizeof(init_finalize)); - msleep(5); + msleep(2); + _jc_rcv_pkt(jc); + + // Set packet rate. + _joycon_send_raw(jc->uart, init_set_rpt_rate, sizeof(init_set_rpt_rate)); + msleep(2); _jc_rcv_pkt(jc); } + // Initialization done. + jc->state = JC_STATE_INIT_DONE; + +out: jc->last_received_time = get_tmr_ms(); if (jc->connected)