From 9f30c51bd1a628272ad15017b6f49d0d46125b5f Mon Sep 17 00:00:00 2001 From: CTCaer Date: Mon, 9 May 2022 05:32:32 +0300 Subject: [PATCH] bdk: joycon: refactor driver --- bdk/input/joycon.c | 422 ++++++++++++++++++++++----------------------- bdk/input/joycon.h | 1 - 2 files changed, 208 insertions(+), 215 deletions(-) diff --git a/bdk/input/joycon.c b/bdk/input/joycon.c index 1707aba..66d1037 100644 --- a/bdk/input/joycon.c +++ b/bdk/input/joycon.c @@ -42,8 +42,8 @@ #define JC_HORI_INPUT_RPT_CMD 0x9A #define JC_HORI_INPUT_RPT 0x00 -#define JC_WIRED_CMD_MAC 0x01 -#define JC_WIRED_CMD_10 0x10 +#define JC_WIRED_CMD_GET_INFO 0x01 +#define JC_WIRED_CMD_INIT_DONE 0x10 #define JC_HID_OUTPUT_RPT 0x01 #define JC_HID_RUMBLE_RPT 0x10 @@ -68,8 +68,7 @@ #define JC_ID_R 0x02 #define JC_ID_HORI 0x20 -#define JC_CRC8_INIT 0x00 -#define JC_CRC8_POLY 0x8D +#define JC_CRC8_POLY 0x8D enum { @@ -80,7 +79,7 @@ enum JC_BATT_FULL = 8 }; -static const u8 init_jc[] = { +static const u8 init_wake[] = { 0xA1, 0xA2, 0xA3, 0xA4 }; @@ -91,21 +90,21 @@ static const u8 init_handshake[] = { }; static const u8 init_get_info[] = { - 0x19, 0x01, 0x03, 0x07, 0x00, // Uart header. - JC_WIRED_CMD, JC_WIRED_CMD_MAC, // Wired cmd and subcmd. - 0x00, 0x00, 0x00, 0x00, 0x24 // Wired subcmd data and crc. + 0x19, 0x01, 0x03, 0x07, 0x00, // Uart header. + JC_WIRED_CMD, JC_WIRED_CMD_GET_INFO, // Wired cmd and subcmd. + 0x00, 0x00, 0x00, 0x00, 0x24 // Wired subcmd data and crc. }; static const u8 init_finalize[] = { - 0x19, 0x01, 0x03, 0x07, 0x00, // Uart header. - JC_WIRED_CMD, JC_WIRED_CMD_10, // Wired cmd and subcmd. - 0x00, 0x00, 0x00, 0x00, 0x3D // Wired subcmd data and crc. + 0x19, 0x01, 0x03, 0x07, 0x00, // Uart header. + JC_WIRED_CMD, JC_WIRED_CMD_INIT_DONE, // Wired cmd and subcmd. + 0x00, 0x00, 0x00, 0x00, 0x3D // Wired subcmd data and crc. }; static const u8 nx_pad_status[] = { 0x19, 0x01, 0x03, 0x08, 0x00, // Uart header. JC_WIRED_HID, 0x00, // Wired cmd and hid cmd. - 0x01, 0x00, 0x00, 0x69, 0x2D, 0x1F // hid data and crc. + 0x01, 0x00, 0x00, 0x69, 0x2D, 0x1F // hid data, data crc and crc. }; static const u8 hori_pad_status[] = { @@ -187,15 +186,14 @@ typedef struct _jc_hid_in_pair_data_t typedef struct _joycon_ctxt_t { - u8 buf[0x100]; //FIXME: If heap is used, dumping breaks. - u8 uart; - u8 type; - u8 mac[6]; - u32 hw_init_done; + u8 buf[0x100]; //FIXME: If heap is used, dumping breaks. + u8 uart; + u8 type; + u8 mac[6]; u32 last_received_time; u32 last_status_req_time; - u8 rumble_sent; - u8 connected; + u8 rumble_sent; + u8 connected; } joycon_ctxt_t; static joycon_ctxt_t jc_l = {0}; @@ -206,15 +204,14 @@ static u32 hid_pkt_inc = 0; static jc_gamepad_rpt_t jc_gamepad; -void jc_power_supply(u8 uart, bool enable); - -static u8 jc_crc(u8 *data, u16 len) +static u8 _jc_crc(u8 *data, u16 len, u8 init) { - u8 crc = JC_CRC8_INIT; - u16 i, j; - for (i = 0; i < len; i++) { + u8 crc = init; + for (u16 i = 0; i < len; i++) + { crc ^= data[i]; - for (j = 0; j < 8; j++) { + for (u16 j = 0; j < 8; j++) + { if ((crc & 0x80) != 0) crc = (u8)((crc << 1) ^ JC_CRC8_POLY); else @@ -224,13 +221,98 @@ static u8 jc_crc(u8 *data, u16 len) return crc; } -void joycon_send_raw(u8 uart_port, const u8 *buf, u16 size) +static void _jc_power_supply(u8 uart, bool enable) +{ + if (enable) + { + if (regulator_5v_get_dev_enabled(1 << uart)) + return; + + regulator_5v_enable(1 << uart); + + if (jc_init_done) + { + if (uart == UART_C) + gpio_write(GPIO_PORT_CC, GPIO_PIN_3, GPIO_HIGH); + else + gpio_write(GPIO_PORT_K, GPIO_PIN_3, GPIO_HIGH); + return; + } + + if (uart == UART_C) + { + // Joy-Con(L) Charge Enable. + PINMUX_AUX(PINMUX_AUX_SPDIF_IN) = PINMUX_PULL_DOWN | 1; + gpio_config(GPIO_PORT_CC, GPIO_PIN_3, GPIO_MODE_GPIO); + gpio_output_enable(GPIO_PORT_CC, GPIO_PIN_3, GPIO_OUTPUT_ENABLE); + gpio_write(GPIO_PORT_CC, GPIO_PIN_3, GPIO_HIGH); + } + else + { + // Joy-Con(R) Charge Enable. + PINMUX_AUX(PINMUX_AUX_GPIO_PK3) = PINMUX_DRIVE_4X | PINMUX_PULL_DOWN | 2; + gpio_config(GPIO_PORT_K, GPIO_PIN_3, GPIO_MODE_GPIO); + gpio_output_enable(GPIO_PORT_K, GPIO_PIN_3, GPIO_OUTPUT_ENABLE); + gpio_write(GPIO_PORT_K, GPIO_PIN_3, GPIO_HIGH); + } + } + else + { + if (!regulator_5v_get_dev_enabled(1 << uart)) + return; + + regulator_5v_disable(1 << uart); + + if (uart == UART_C) + gpio_write(GPIO_PORT_CC, GPIO_PIN_3, GPIO_LOW); + else + gpio_write(GPIO_PORT_K, GPIO_PIN_3, GPIO_LOW); + } +} + +static void _jc_conn_check() +{ + // Check if a Joy-Con was disconnected. + if (gpio_read(GPIO_PORT_E, GPIO_PIN_6)) + { + _jc_power_supply(UART_C, false); + + hid_pkt_inc = 0; + + jc_l.connected = false; + jc_l.rumble_sent = false; + + jc_gamepad.buttons &= ~JC_BTN_MASK_L; + jc_gamepad.conn_l = false; + + jc_gamepad.batt_info_l = 0; + jc_gamepad.bt_conn_l.type = 0; + } + + if (gpio_read(GPIO_PORT_H, GPIO_PIN_6)) + { + _jc_power_supply(UART_B, false); + + hid_pkt_inc = 0; + + jc_r.connected = false; + jc_r.rumble_sent = false; + + jc_gamepad.buttons &= ~JC_BTN_MASK_R; + jc_gamepad.conn_r = false; + + jc_gamepad.batt_info_r = 0; + jc_gamepad.bt_conn_r.type = 0; + } +} + +static void _joycon_send_raw(u8 uart_port, const u8 *buf, u16 size) { uart_send(uart_port, buf, size); uart_wait_xfer(uart_port, UART_TX_IDLE); } -static u16 jc_packet_add_uart_hdr(jc_wired_hdr_t *out, u8 wired_cmd, u8 *data, u16 size, bool crc) +static u16 _jc_packet_add_uart_hdr(jc_wired_hdr_t *out, u8 wired_cmd, u8 *data, u16 size, bool crc) { out->uart_hdr.magic[0] = 0x19; out->uart_hdr.magic[1] = 0x01; @@ -243,14 +325,16 @@ static u16 jc_packet_add_uart_hdr(jc_wired_hdr_t *out, u8 wired_cmd, u8 *data, u if (data) memcpy(out->data, data, size); - out->crc = crc ? jc_crc(&out->uart_hdr.total_size_msb, sizeof(out->uart_hdr.total_size_msb) + sizeof(out->cmd) + sizeof(out->data)) : 0; + out->crc = crc ? _jc_crc(&out->uart_hdr.total_size_msb, + sizeof(out->uart_hdr.total_size_msb) + + sizeof(out->cmd) + sizeof(out->data), 0) : 0; return sizeof(jc_wired_hdr_t); } -static u16 jc_hid_output_rpt_craft(jc_wired_hdr_t *rpt, u8 *payload, u16 size, bool crc) +static u16 _jc_hid_output_rpt_craft(jc_wired_hdr_t *rpt, u8 *payload, u16 size, bool crc) { - u16 pkt_size = jc_packet_add_uart_hdr(rpt, JC_WIRED_HID, NULL, 0, crc); + u16 pkt_size = _jc_packet_add_uart_hdr(rpt, JC_WIRED_HID, NULL, 0, crc); pkt_size += size; rpt->uart_hdr.total_size_lsb += size; @@ -263,34 +347,29 @@ static u16 jc_hid_output_rpt_craft(jc_wired_hdr_t *rpt, u8 *payload, u16 size, b return pkt_size; } -void jc_send_hid_output_rpt(u8 uart, u8 *payload, u16 size, bool crc) +static void _jc_send_hid_output_rpt(u8 uart, u8 *payload, u16 size, bool crc) { u8 rpt[0x50]; memset(rpt, 0, sizeof(rpt)); - u32 rpt_size = jc_hid_output_rpt_craft((jc_wired_hdr_t *)rpt, payload, size, crc); + u32 rpt_size = _jc_hid_output_rpt_craft((jc_wired_hdr_t *)rpt, payload, size, crc); - joycon_send_raw(uart, rpt, rpt_size); + _joycon_send_raw(uart, rpt, rpt_size); } -static u8 jc_hid_pkt_id_incr() +static u8 _jc_hid_pkt_id_incr() { - u8 curr_id = hid_pkt_inc; - hid_pkt_inc++; - - return (curr_id & 0xF); + return (hid_pkt_inc++ & 0xF); } -void jc_send_hid_cmd(u8 uart, u8 subcmd, u8 *data, u16 size) +static void _jc_send_hid_cmd(u8 uart, u8 subcmd, u8 *data, u16 size) { - u8 temp[0x30]; - u8 rumble_neutral[8] = {0x00, 0x01, 0x40, 0x40, 0x00, 0x01, 0x40, 0x40}; - u8 rumble_init[8] = {0xc2, 0xc8, 0x03, 0x72, 0xc2, 0xc8, 0x03, 0x72}; + const u8 rumble_neutral[8] = { 0x00, 0x01, 0x40, 0x40, 0x00, 0x01, 0x40, 0x40 }; + const u8 rumble_init[8] = { 0xc2, 0xc8, 0x03, 0x72, 0xc2, 0xc8, 0x03, 0x72 }; - memset(temp, 0, sizeof(temp)); + u8 temp[0x30] = {0}; jc_hid_out_rpt_t *hid_pkt = (jc_hid_out_rpt_t *)temp; - memcpy(hid_pkt->rumble, rumble_neutral, sizeof(rumble_neutral)); if (subcmd == JC_HID_SUBCMD_SND_RUMBLE) @@ -300,62 +379,62 @@ void jc_send_hid_cmd(u8 uart, u8 subcmd, u8 *data, u16 size) // Enable rumble. hid_pkt->cmd = JC_HID_OUTPUT_RPT; - hid_pkt->pkt_id = jc_hid_pkt_id_incr(); + hid_pkt->pkt_id = _jc_hid_pkt_id_incr(); hid_pkt->subcmd = JC_HID_SUBCMD_RUMBLE_CTL; hid_pkt->subcmd_data[0] = 1; if (send_r_rumble) - jc_send_hid_output_rpt(UART_B, (u8 *)hid_pkt, 0x10, false); + _jc_send_hid_output_rpt(UART_B, (u8 *)hid_pkt, 0x10, false); if (send_l_rumble) - jc_send_hid_output_rpt(UART_C, (u8 *)hid_pkt, 0x10, false); + _jc_send_hid_output_rpt(UART_C, (u8 *)hid_pkt, 0x10, false); // Send rumble. hid_pkt->cmd = JC_HID_RUMBLE_RPT; - hid_pkt->pkt_id = jc_hid_pkt_id_incr(); + hid_pkt->pkt_id = _jc_hid_pkt_id_incr(); memcpy(hid_pkt->rumble, rumble_init, sizeof(rumble_init)); if (send_r_rumble) - jc_send_hid_output_rpt(UART_B, (u8 *)hid_pkt, 10, false); + _jc_send_hid_output_rpt(UART_B, (u8 *)hid_pkt, 10, false); if (send_l_rumble) - jc_send_hid_output_rpt(UART_C, (u8 *)hid_pkt, 10, false); + _jc_send_hid_output_rpt(UART_C, (u8 *)hid_pkt, 10, false); msleep(15); // Disable rumble. hid_pkt->cmd = JC_HID_OUTPUT_RPT; - hid_pkt->pkt_id = jc_hid_pkt_id_incr(); + hid_pkt->pkt_id = _jc_hid_pkt_id_incr(); hid_pkt->subcmd = JC_HID_SUBCMD_RUMBLE_CTL; hid_pkt->subcmd_data[0] = 0; memcpy(hid_pkt->rumble, rumble_neutral, sizeof(rumble_neutral)); if (send_r_rumble) - jc_send_hid_output_rpt(UART_B, (u8 *)hid_pkt, 0x10, false); + _jc_send_hid_output_rpt(UART_B, (u8 *)hid_pkt, 0x10, false); if (send_l_rumble) - jc_send_hid_output_rpt(UART_C, (u8 *)hid_pkt, 0x10, false); + _jc_send_hid_output_rpt(UART_C, (u8 *)hid_pkt, 0x10, false); } else { bool crc_needed = (jc_l.uart == uart) ? (jc_l.type & JC_ID_HORI) : (jc_r.type & JC_ID_HORI); hid_pkt->cmd = JC_HID_OUTPUT_RPT; - hid_pkt->pkt_id = jc_hid_pkt_id_incr(); + hid_pkt->pkt_id = _jc_hid_pkt_id_incr(); hid_pkt->subcmd = subcmd; if (data) memcpy(hid_pkt->subcmd_data, data, size); - jc_send_hid_output_rpt(uart, (u8 *)hid_pkt, sizeof(jc_hid_out_rpt_t) + size, crc_needed); + _jc_send_hid_output_rpt(uart, (u8 *)hid_pkt, sizeof(jc_hid_out_rpt_t) + size, crc_needed); } } -static void jc_charging_decider(u8 batt, u8 uart) +static void _jc_charging_decider(u8 batt, u8 uart) { u32 system_batt_enough = max17050_get_cached_batt_volt() > 4000; // Power supply control based on battery levels and charging. if ((batt >> 1 << 1) < JC_BATT_LOW) // Level without checking charging. - jc_power_supply(uart, true); + _jc_power_supply(uart, true); else if (batt > (system_batt_enough ? JC_BATT_FULL : JC_BATT_MID)) // Addresses the charging bit. - jc_power_supply(uart, false); + _jc_power_supply(uart, false); } -static void jc_parse_wired_hid(joycon_ctxt_t *jc, const u8* packet, u32 size) +static void _jc_parse_wired_hid(joycon_ctxt_t *jc, const u8* packet, u32 size) { u32 btn_tmp; jc_hid_in_rpt_t *hid_pkt = (jc_hid_in_rpt_t *)packet; @@ -390,7 +469,7 @@ static void jc_parse_wired_hid(joycon_ctxt_t *jc, const u8* packet, u32 size) jc_gamepad.conn_l = jc_l.connected; jc_gamepad.conn_r = jc_r.connected; - jc_charging_decider(hid_pkt->batt_info, jc->uart); + _jc_charging_decider(hid_pkt->batt_info, jc->uart); break; case JC_HID_SUBMCD_RPT: if (hid_pkt->subcmd == JC_HID_SUBCMD_SPI_READ) @@ -405,7 +484,7 @@ static void jc_parse_wired_hid(joycon_ctxt_t *jc, const u8* packet, u32 size) jc_hid_in_spi_read_t *spi_info = (jc_hid_in_spi_read_t *)hid_pkt->subcmd_data; jc_hid_in_pair_data_t *pair_data = (jc_hid_in_pair_data_t *)spi_info->data; - // Check if we reply is pairing info. + // Check if the reply is pairing info. if (spi_info->size == 0x1A && pair_data->magic == 0x95 && pair_data->size == 0x22) { bt_conn->type = jc->type; @@ -423,11 +502,11 @@ static void jc_parse_wired_hid(joycon_ctxt_t *jc, const u8* packet, u32 size) jc->last_received_time = get_tmr_ms(); } -static void jc_parse_wired_init(joycon_ctxt_t *jc, const u8* data, u32 size) +static void _jc_parse_wired_init(joycon_ctxt_t *jc, const u8* data, u32 size) { switch (data[0]) { - case JC_WIRED_CMD_MAC: + case JC_WIRED_CMD_GET_INFO: for (int i = 12; i > 6; i--) jc->mac[12 - i] = data[i]; jc->type = data[6]; @@ -444,17 +523,17 @@ static void jc_uart_pkt_parse(joycon_ctxt_t *jc, const u8* packet, size_t size) { case JC_HORI_INPUT_RPT_CMD: case JC_WIRED_HID: - jc_parse_wired_hid(jc, pkt->payload, (pkt->data[0] << 8) | pkt->data[1]); + _jc_parse_wired_hid(jc, pkt->payload, (pkt->data[0] << 8) | pkt->data[1]); break; case JC_WIRED_INIT_REPLY: - jc_parse_wired_init(jc, pkt->data, size - sizeof(jc_uart_hdr_t) - 1); + _jc_parse_wired_init(jc, pkt->data, size - sizeof(jc_uart_hdr_t) - 1); break; default: break; } } -static void jc_rcv_pkt(joycon_ctxt_t *jc) +static void _jc_rcv_pkt(joycon_ctxt_t *jc) { if (gpio_read(GPIO_PORT_E, GPIO_PIN_6) && jc->uart == UART_C) return; @@ -477,7 +556,7 @@ static void jc_rcv_pkt(joycon_ctxt_t *jc) } } -static bool jc_send_init_rumble(joycon_ctxt_t *jc) +static bool _jc_send_init_rumble(joycon_ctxt_t *jc) { // Send init rumble or request nx pad status report. if ((jc_r.connected && !jc_r.rumble_sent) || (jc_l.connected && !jc_l.rumble_sent)) @@ -485,7 +564,7 @@ static bool jc_send_init_rumble(joycon_ctxt_t *jc) gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_SPIO); gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_SPIO); - jc_send_hid_cmd(jc->uart, JC_HID_SUBCMD_SND_RUMBLE, NULL, 0); + _jc_send_hid_cmd(jc->uart, JC_HID_SUBCMD_SND_RUMBLE, NULL, 0); if (jc_l.connected) jc_l.rumble_sent = true; @@ -503,13 +582,13 @@ static bool jc_send_init_rumble(joycon_ctxt_t *jc) return 0; } -static void jc_req_nx_pad_status(joycon_ctxt_t *jc) +static void _jc_req_nx_pad_status(joycon_ctxt_t *jc) { bool is_nxpad = !(jc->type & JC_ID_HORI); if (is_nxpad) { - bool sent_rumble = jc_send_init_rumble(jc); + bool sent_rumble = _jc_send_init_rumble(jc); if (sent_rumble) return; @@ -525,9 +604,9 @@ static void jc_req_nx_pad_status(joycon_ctxt_t *jc) gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_SPIO); if (is_nxpad) - joycon_send_raw(jc->uart, nx_pad_status, sizeof(nx_pad_status)); + _joycon_send_raw(jc->uart, nx_pad_status, sizeof(nx_pad_status)); else - joycon_send_raw(jc->uart, hori_pad_status, sizeof(hori_pad_status)); + _joycon_send_raw(jc->uart, hori_pad_status, sizeof(hori_pad_status)); // Turn Joy-Con detect on. if (jc->uart == UART_B) @@ -576,8 +655,8 @@ jc_gamepad_rpt_t *jc_get_bt_pairing_info(bool *is_l_hos, bool *is_r_hos) while (jc_l.last_status_req_time > get_tmr_ms()) { - jc_rcv_pkt(&jc_r); - jc_rcv_pkt(&jc_l); + _jc_rcv_pkt(&jc_r); + _jc_rcv_pkt(&jc_l); } jc_hid_in_spi_read_t subcmd_data_l; @@ -605,13 +684,13 @@ retry: { if (!jc_l_found) { - jc_send_hid_cmd(jc_l.uart, JC_HID_SUBCMD_SPI_READ, (u8 *)&subcmd_data_l, 5); + _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) { - jc_send_hid_cmd(jc_r.uart, JC_HID_SUBCMD_SPI_READ, (u8 *)&subcmd_data_r, 5); + _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; } @@ -621,7 +700,7 @@ retry: if (!jc_l_found) { memset(jc_l.buf, 0, 0x100); - jc_rcv_pkt(&jc_l); + _jc_rcv_pkt(&jc_l); bool is_hos = false; if (_jc_validate_pairing_info(&jc_l.buf[SPI_READ_OFFSET], &is_hos)) @@ -641,7 +720,7 @@ retry: if (!jc_r_found) { memset(jc_r.buf, 0, 0x100); - jc_rcv_pkt(&jc_r); + _jc_rcv_pkt(&jc_r); bool is_hos = false; if (_jc_validate_pairing_info(&jc_r.buf[SPI_READ_OFFSET], &is_hos)) @@ -692,40 +771,11 @@ retry: return &jc_gamepad; } -void jc_deinit() -{ - // Disable power. - jc_power_supply(UART_B, false); - jc_power_supply(UART_C, false); - - // Turn off Joy-Con detect. - gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_SPIO); - gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_SPIO); - - // Send sleep command. - u8 data = HCI_STATE_SLEEP; - - if (jc_r.connected && !(jc_r.type & JC_ID_HORI)) - { - jc_send_hid_cmd(UART_B, JC_HID_SUBCMD_HCI_STATE, &data, 1); - jc_rcv_pkt(&jc_r); - } - if (jc_l.connected && !(jc_l.type & JC_ID_HORI)) - { - jc_send_hid_cmd(UART_C, JC_HID_SUBCMD_HCI_STATE, &data, 1); - jc_rcv_pkt(&jc_l); - } - - // Disable UART B and C clocks. - clock_disable_uart(UART_B); - clock_disable_uart(UART_C); -} - -static void jc_init_conn(joycon_ctxt_t *jc) +static void _jc_init_conn(joycon_ctxt_t *jc) { if (((u32)get_tmr_ms() - jc->last_received_time) > 1000) { - jc_power_supply(jc->uart, true); + _jc_power_supply(jc->uart, true); // Turn off Joy-Con detect. if (jc->uart == UART_B) @@ -746,21 +796,21 @@ static void jc_init_conn(joycon_ctxt_t *jc) uart_invert(jc->uart, true, UART_INVERT_TXD); uart_set_IIR(jc->uart); - joycon_send_raw(jc->uart, init_jc, 4); - joycon_send_raw(jc->uart, init_handshake, sizeof(init_handshake)); + _joycon_send_raw(jc->uart, init_wake, sizeof(init_wake)); + _joycon_send_raw(jc->uart, init_handshake, sizeof(init_handshake)); msleep(5); - jc_rcv_pkt(jc); + _jc_rcv_pkt(jc); - joycon_send_raw(jc->uart, init_get_info, sizeof(init_get_info)); + _joycon_send_raw(jc->uart, init_get_info, sizeof(init_get_info)); msleep(5); - jc_rcv_pkt(jc); + _jc_rcv_pkt(jc); if (!(jc->type & JC_ID_HORI)) { - joycon_send_raw(jc->uart, init_finalize, sizeof(init_finalize)); + _joycon_send_raw(jc->uart, init_finalize, sizeof(init_finalize)); msleep(5); - jc_rcv_pkt(jc); + _jc_rcv_pkt(jc); } // Turn Joy-Con detect on. @@ -772,92 +822,7 @@ static void jc_init_conn(joycon_ctxt_t *jc) jc->last_received_time = get_tmr_ms(); if (jc->connected) - jc_power_supply(jc->uart, false); - } -} - -static void jc_conn_check() -{ - // Check if a Joy-Con was disconnected. - if (gpio_read(GPIO_PORT_E, GPIO_PIN_6)) - { - jc_power_supply(UART_C, false); - - hid_pkt_inc = 0; - - jc_l.connected = false; - jc_l.rumble_sent = false; - - jc_gamepad.buttons &= ~JC_BTN_MASK_L; - jc_gamepad.conn_l = false; - - jc_gamepad.batt_info_l = 0; - jc_gamepad.bt_conn_l.type = 0; - } - - if (gpio_read(GPIO_PORT_H, GPIO_PIN_6)) - { - jc_power_supply(UART_B, false); - - hid_pkt_inc = 0; - - jc_r.connected = false; - jc_r.rumble_sent = false; - - jc_gamepad.buttons &= ~JC_BTN_MASK_R; - jc_gamepad.conn_r = false; - - jc_gamepad.batt_info_r = 0; - jc_gamepad.bt_conn_r.type = 0; - } -} - -void jc_power_supply(u8 uart, bool enable) -{ - if (enable) - { - if (regulator_5v_get_dev_enabled(1 << uart)) - return; - - regulator_5v_enable(1 << uart); - - if (jc_init_done) - { - if (uart == UART_C) - gpio_write(GPIO_PORT_CC, GPIO_PIN_3, GPIO_HIGH); - else - gpio_write(GPIO_PORT_K, GPIO_PIN_3, GPIO_HIGH); - return; - } - - if (uart == UART_C) - { - // Joy-Con(L) Charge Detect. - PINMUX_AUX(PINMUX_AUX_SPDIF_IN) = PINMUX_PULL_DOWN | 1; - gpio_config(GPIO_PORT_CC, GPIO_PIN_3, GPIO_MODE_GPIO); - gpio_output_enable(GPIO_PORT_CC, GPIO_PIN_3, GPIO_OUTPUT_ENABLE); - gpio_write(GPIO_PORT_CC, GPIO_PIN_3, GPIO_HIGH); - } - else - { - // Joy-Con(R) Charge Detect. - PINMUX_AUX(PINMUX_AUX_GPIO_PK3) = PINMUX_DRIVE_4X | PINMUX_PULL_DOWN | 2; - gpio_config(GPIO_PORT_K, GPIO_PIN_3, GPIO_MODE_GPIO); - gpio_output_enable(GPIO_PORT_K, GPIO_PIN_3, GPIO_OUTPUT_ENABLE); - gpio_write(GPIO_PORT_K, GPIO_PIN_3, GPIO_HIGH); - } - } - else - { - if (!regulator_5v_get_dev_enabled(1 << uart)) - return; - - regulator_5v_disable(1 << uart); - - if (uart == UART_C) - gpio_write(GPIO_PORT_CC, GPIO_PIN_3, GPIO_LOW); - else - gpio_write(GPIO_PORT_K, GPIO_PIN_3, GPIO_LOW); + _jc_power_supply(jc->uart, false); } } @@ -870,14 +835,14 @@ void jc_init_hw() if (fuse_read_hw_type() == FUSE_NX_HW_TYPE_HOAG) return; - jc_power_supply(UART_C, true); - jc_power_supply(UART_B, true); + _jc_power_supply(UART_C, true); + _jc_power_supply(UART_B, true); - // Joy-Con (R) IsAttached. + // Joy-Con (R) IsAttached. Shared with UARTB TX. PINMUX_AUX(PINMUX_AUX_GPIO_PH6) = PINMUX_INPUT_ENABLE | PINMUX_TRISTATE; gpio_config(GPIO_PORT_H, GPIO_PIN_6, GPIO_MODE_GPIO); - // Joy-Con (L) IsAttached. + // Joy-Con (L) IsAttached. Shared with UARTC TX. PINMUX_AUX(PINMUX_AUX_GPIO_PE6) = PINMUX_INPUT_ENABLE | PINMUX_TRISTATE; gpio_config(GPIO_PORT_E, GPIO_PIN_6, GPIO_MODE_GPIO); @@ -903,27 +868,56 @@ void jc_init_hw() #endif } +void jc_deinit() +{ + // Disable power. + _jc_power_supply(UART_B, false); + _jc_power_supply(UART_C, false); + + // Turn off Joy-Con detect. + gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_SPIO); + gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_SPIO); + + // Send sleep command. + u8 data = HCI_STATE_SLEEP; + + if (jc_r.connected && !(jc_r.type & JC_ID_HORI)) + { + _jc_send_hid_cmd(UART_B, JC_HID_SUBCMD_HCI_STATE, &data, 1); + _jc_rcv_pkt(&jc_r); + } + if (jc_l.connected && !(jc_l.type & JC_ID_HORI)) + { + _jc_send_hid_cmd(UART_C, JC_HID_SUBCMD_HCI_STATE, &data, 1); + _jc_rcv_pkt(&jc_l); + } + + // Disable UART B and C clocks. + clock_disable_uart(UART_B); + clock_disable_uart(UART_C); +} + jc_gamepad_rpt_t *joycon_poll() { if (!jc_init_done) return NULL; if (!gpio_read(GPIO_PORT_H, GPIO_PIN_6)) - jc_init_conn(&jc_r); + _jc_init_conn(&jc_r); if (!gpio_read(GPIO_PORT_E, GPIO_PIN_6)) - jc_init_conn(&jc_l); + _jc_init_conn(&jc_l); if (!gpio_read(GPIO_PORT_H, GPIO_PIN_6)) - jc_req_nx_pad_status(&jc_r); + _jc_req_nx_pad_status(&jc_r); if (!gpio_read(GPIO_PORT_E, GPIO_PIN_6)) - jc_req_nx_pad_status(&jc_l); + _jc_req_nx_pad_status(&jc_l); if (!gpio_read(GPIO_PORT_H, GPIO_PIN_6)) - jc_rcv_pkt(&jc_r); + _jc_rcv_pkt(&jc_r); if (!gpio_read(GPIO_PORT_E, GPIO_PIN_6)) - jc_rcv_pkt(&jc_l); + _jc_rcv_pkt(&jc_l); - jc_conn_check(); + _jc_conn_check(); return &jc_gamepad; } diff --git a/bdk/input/joycon.h b/bdk/input/joycon.h index 932c836..3a16237 100644 --- a/bdk/input/joycon.h +++ b/bdk/input/joycon.h @@ -89,7 +89,6 @@ typedef struct _jc_gamepad_rpt_t jc_bt_conn_t bt_conn_r; } jc_gamepad_rpt_t; -void jc_power_supply(u8 uart, bool enable); void jc_init_hw(); void jc_deinit(); jc_gamepad_rpt_t *joycon_poll();