From e2c905e9b2485db1050508826ad365cb544f58bb Mon Sep 17 00:00:00 2001 From: CTCaer Date: Sun, 14 Jun 2020 13:13:04 +0300 Subject: [PATCH] joycon: More robust spi dumping for bd addr/ltk --- nyx/nyx_gui/frontend/gui_options.c | 64 +++++++++++-- nyx/nyx_gui/input/joycon.c | 143 +++++++++++++++++++++-------- nyx/nyx_gui/input/joycon.h | 2 +- 3 files changed, 159 insertions(+), 50 deletions(-) diff --git a/nyx/nyx_gui/frontend/gui_options.c b/nyx/nyx_gui/frontend/gui_options.c index e38a864..cd656cc 100644 --- a/nyx/nyx_gui/frontend/gui_options.c +++ b/nyx/nyx_gui/frontend/gui_options.c @@ -740,7 +740,18 @@ void first_time_clock_edit(void *param) static lv_res_t _joycon_info_dump_action(lv_obj_t * btn) { FIL fp; - jc_gamepad_rpt_t *jc_pad = jc_get_bt_pairing_info(); + bool is_l_hos = false; + bool is_r_hos = false; + jc_gamepad_rpt_t *jc_pad = jc_get_bt_pairing_info(&is_l_hos, &is_r_hos); + + // Count valid joycon. + u32 joycon_found = jc_pad->bt_conn_l.type ? 1 : 0; + if (jc_pad->bt_conn_r.type) + joycon_found++; + + // Reset PC based for dumping. + jc_pad->bt_conn_l.type = is_l_hos ? jc_pad->bt_conn_l.type : 0; + jc_pad->bt_conn_r.type = is_r_hos ? jc_pad->bt_conn_r.type : 0; int error = !sd_mount(); @@ -796,17 +807,50 @@ static lv_res_t _joycon_info_dump_action(lv_obj_t * btn) lv_mbox_set_recolor_text(mbox, true); lv_obj_set_width(mbox, LV_HOR_RES / 9 * 5); - u32 joycon_found = jc_pad->bt_conn_l.type ? 1 : 0; - if (jc_pad->bt_conn_r.type) - joycon_found++; - - if (error) - s_printf(txt_buf, "#FFDD00 Failed to dump to# Joy-Con pairing info#FFDD00 !#\nError: %d", error); - else + if (!error) + { s_printf(txt_buf, "Dumping to SD card finished!\n" - "Found %d Joycon!\n\n" - "Saved to: #C7EA46 switchroot/joycon_mac.bin/ini#", joycon_found); + "Saved to: #C7EA46 switchroot/joycon_mac.[bin/ini]#\n\n"); + + bool success = true; + + // Check if pairing info was found. + if (joycon_found == 2) + s_printf(txt_buf + strlen(txt_buf), "#C7EA46 Found 2 out of 2 Joy-Con pairing data!#\n"); + else + { + s_printf(txt_buf + strlen(txt_buf), "#FF8000 Warning:# Found #FFDD00 %d out of 2# pairing data!\n", joycon_found); + success = false; + } + + // Check if pairing was done in HOS. + if (is_l_hos && is_r_hos) + s_printf(txt_buf + strlen(txt_buf), "#C7EA46 Both pairing data are HOS based!#"); + else if (!is_l_hos && is_r_hos) + { + s_printf(txt_buf + strlen(txt_buf), "#FF8000 Warning:# #FFDD00 Left# pairing data is not HOS based!"); + success = false; + } + else if (is_l_hos && !is_r_hos) + { + s_printf(txt_buf + strlen(txt_buf), "#FF8000 Warning:# #FFDD00 Right# pairing data is not HOS based!"); + success = false; + } + else + { + s_printf(txt_buf + strlen(txt_buf), "#FF8000 Warning:# #FFDD00 No# pairing data is HOS based!"); + success = false; + } + + if (!success) + s_printf(txt_buf + strlen(txt_buf), + "\n\n#FFDD00 Make sure that both Joy-Con are connected,#\n" + "#FFDD00 and that you paired them in HOS!#"); + } + else + s_printf(txt_buf, "#FFDD00 Failed to dump Joy-Con pairing info!#\n#FFDD00 Error: %d#", error); + lv_mbox_set_text(mbox, txt_buf); lv_mbox_add_btns(mbox, mbox_btn_map, mbox_action); // Important. After set_text. diff --git a/nyx/nyx_gui/input/joycon.c b/nyx/nyx_gui/input/joycon.c index f219ba2..8cc74aa 100644 --- a/nyx/nyx_gui/input/joycon.c +++ b/nyx/nyx_gui/input/joycon.c @@ -53,6 +53,7 @@ #define HCI_STATE_PAIR 0x02 #define HCI_STATE_HOME 0x04 #define JC_HID_SUBCMD_SPI_READ 0x10 +#define SPI_READ_OFFSET 0x20 #define JC_HID_SUBCMD_RUMBLE_CTL 0x48 #define JC_HID_SUBCMD_SND_RUMBLE 0xFF @@ -309,13 +310,7 @@ void jc_send_hid_cmd(u8 uart, u8 subcmd, u8 *data, u16 size) u8 pkt_size = sizeof(jc_hid_out_rpt_t) + size; - if (subcmd != JC_HID_SUBCMD_SPI_READ) - jc_send_hid_output_rpt(uart, (u8 *)hid_pkt, pkt_size); - else - { - jc_send_hid_output_rpt(UART_B, (u8 *)hid_pkt, pkt_size); - jc_send_hid_output_rpt(UART_C, (u8 *)hid_pkt, pkt_size); - } + jc_send_hid_output_rpt(uart, (u8 *)hid_pkt, pkt_size); } } @@ -380,7 +375,7 @@ static void jc_parse_wired_hid(joycon_ctxt_t *jc, const u8* packet, u32 size) jc_hid_in_pair_data_t *pair_data = (jc_hid_in_pair_data_t *)spi_info->data; // Check if we reply is pairing info. - if (spi_info->addr == 0x2000 && spi_info->size == 0x1A && pair_data->magic == 0x95) + if (spi_info->size == 0x1A && pair_data->magic == 0x95 && pair_data->size == 0x22) { bt_conn->type = jc->type; @@ -439,7 +434,7 @@ static void jc_rcv_pkt(joycon_ctxt_t *jc) if ((uart_irq & 0x8) != 0x8) return; - u32 len = uart_recv(jc->uart, (u8 *)jc->buf, 0); + u32 len = uart_recv(jc->uart, (u8 *)jc->buf, 0x100); // Check valid size and uart reply magic. if (len > 7 && !memcmp(jc->buf, "\x19\x81\x03", 3)) @@ -503,7 +498,27 @@ static void jc_req_nx_pad_status(joycon_ctxt_t *jc) jc->last_status_req_time = get_tmr_ms() + 15; } -jc_gamepad_rpt_t *jc_get_bt_pairing_info() +static bool _jc_validate_pairing_info(u8 *buf, bool *is_hos) +{ + u8 crc = 0; + for (u32 i = 0; i < 0x22; i++) + crc += buf[4 + i]; + + crc += 0x68; // Host is Switch. + + if ((crc ^ 0x55) == buf[2]) + *is_hos = true; + + crc -= 0x68; + crc += 0x08; // Host is PC. + + if (*is_hos || (crc ^ 0x55) == buf[2]) + return true; + + return false; +} + +jc_gamepad_rpt_t *jc_get_bt_pairing_info(bool *is_l_hos, bool *is_r_hos) { u8 retries; jc_bt_conn_t *bt_conn; @@ -522,9 +537,13 @@ jc_gamepad_rpt_t *jc_get_bt_pairing_info() jc_rcv_pkt(&jc_l); } - jc_hid_in_spi_read_t subcmd_data; - subcmd_data.addr = 0x2000; - subcmd_data.size = 0x1A; + jc_hid_in_spi_read_t subcmd_data_l; + subcmd_data_l.addr = 0x2000; + subcmd_data_l.size = 0x1A; + + jc_hid_in_spi_read_t subcmd_data_r; + subcmd_data_r.addr = 0x2000; + subcmd_data_r.size = 0x1A; // Turn off Joy-Con detect. gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_SPIO); @@ -533,46 +552,94 @@ jc_gamepad_rpt_t *jc_get_bt_pairing_info() bool jc_r_found = jc_r.connected ? false : true; bool jc_l_found = jc_l.connected ? false : true; - u32 total_retries = 5; + u32 total_retries = 10; retry: retries = 10; while (retries) { - if (jc_l.last_status_req_time < get_tmr_ms()) + u32 time_now = get_tmr_ms(); + if ((!jc_l_found && jc_l.last_status_req_time < time_now) || (!jc_r_found && jc_r.last_status_req_time < time_now)) { - jc_send_hid_cmd(0, JC_HID_SUBCMD_SPI_READ, (u8 *)&subcmd_data, 5); - jc_l.last_status_req_time = get_tmr_ms() + 15; - jc_r.last_status_req_time = get_tmr_ms() + 15; - retries--; - } + if (!jc_l_found) + { + jc_send_hid_cmd(jc_l.uart, JC_HID_SUBCMD_SPI_READ, (u8 *)&subcmd_data_l, 5); + jc_l.last_status_req_time = get_tmr_ms() + 15; + } - if (!jc_r_found) - { - memset(jc_r.buf, 0, 0x100); - jc_rcv_pkt(&jc_r); + if (!jc_r_found) + { + jc_send_hid_cmd(jc_r.uart, JC_HID_SUBCMD_SPI_READ, (u8 *)&subcmd_data_r, 5); + jc_r.last_status_req_time = get_tmr_ms() + 15; + } + + retries--; } if (!jc_l_found) { memset(jc_l.buf, 0, 0x100); jc_rcv_pkt(&jc_l); + + bool is_hos = false; + if (_jc_validate_pairing_info(&jc_l.buf[SPI_READ_OFFSET], &is_hos)) + { + bool is_active = jc_l.buf[SPI_READ_OFFSET] == 0x95; + + if (!is_active) + subcmd_data_l.addr += 0x26; // Get next slot. + else + jc_l_found = true; // Entry is active. + + if (jc_l_found && is_hos) + *is_l_hos = true; + } } + + if (!jc_r_found) + { + memset(jc_r.buf, 0, 0x100); + jc_rcv_pkt(&jc_r); + + bool is_hos = false; + if (_jc_validate_pairing_info(&jc_r.buf[SPI_READ_OFFSET], &is_hos)) + { + bool is_active = jc_r.buf[SPI_READ_OFFSET] == 0x95; + + if (!is_active) + subcmd_data_r.addr += 0x26; // Get next slot. + else + jc_r_found = true; // Entry is active. + + if (jc_r_found && is_hos) + *is_r_hos = true; + } + } + + if (jc_l_found && jc_r_found) + break; } - if (jc_l.connected && - memcmp(&jc_gamepad.bt_conn_l.ltk[0], "\x00\x00\x00\x00", 4) && - memcmp(&jc_gamepad.bt_conn_l.ltk[8], "\x00\x00\x00\x00", 4)) - jc_l_found = true; - - if (jc_r.connected && - memcmp(&jc_gamepad.bt_conn_r.ltk[0], "\x00\x00\x00\x00", 4) && - memcmp(&jc_gamepad.bt_conn_r.ltk[8], "\x00\x00\x00\x00", 4)) - jc_r_found = true; - - if (total_retries && (!jc_l_found || !jc_r_found)) + if (!jc_l_found || !jc_r_found) { - total_retries--; - goto retry; + if (total_retries) + { + total_retries--; + goto retry; + } + + if (!jc_l_found) + { + bt_conn = &jc_gamepad.bt_conn_l; + memset(bt_conn->host_mac, 0, 6); + memset(bt_conn->ltk, 0, 16); + } + + if (!jc_r_found) + { + bt_conn = &jc_gamepad.bt_conn_r; + memset(bt_conn->host_mac, 0, 6); + memset(bt_conn->ltk, 0, 16); + } } // Turn Joy-Con detect on. @@ -592,13 +659,11 @@ void jc_deinit() if (jc_r.connected) { jc_send_hid_cmd(UART_B, JC_HID_SUBCMD_HCI_STATE, &data, 1); - msleep(1); jc_rcv_pkt(&jc_r); } if (jc_l.connected) { jc_send_hid_cmd(UART_C, JC_HID_SUBCMD_HCI_STATE, &data, 1); - msleep(1); jc_rcv_pkt(&jc_l); } diff --git a/nyx/nyx_gui/input/joycon.h b/nyx/nyx_gui/input/joycon.h index 2d7d8f9..187ccb7 100644 --- a/nyx/nyx_gui/input/joycon.h +++ b/nyx/nyx_gui/input/joycon.h @@ -93,6 +93,6 @@ void jc_power_supply(u8 uart, bool enable); void jc_init_hw(); void jc_deinit(); jc_gamepad_rpt_t *joycon_poll(); -jc_gamepad_rpt_t *jc_get_bt_pairing_info(); +jc_gamepad_rpt_t *jc_get_bt_pairing_info(bool *is_l_hos, bool *is_r_hos); #endif \ No newline at end of file