bdk: joycon: refactor driver

This commit is contained in:
CTCaer 2022-05-09 05:32:32 +03:00
parent 62d68b33c3
commit 9f30c51bd1
2 changed files with 208 additions and 215 deletions

View file

@ -42,8 +42,8 @@
#define JC_HORI_INPUT_RPT_CMD 0x9A #define JC_HORI_INPUT_RPT_CMD 0x9A
#define JC_HORI_INPUT_RPT 0x00 #define JC_HORI_INPUT_RPT 0x00
#define JC_WIRED_CMD_MAC 0x01 #define JC_WIRED_CMD_GET_INFO 0x01
#define JC_WIRED_CMD_10 0x10 #define JC_WIRED_CMD_INIT_DONE 0x10
#define JC_HID_OUTPUT_RPT 0x01 #define JC_HID_OUTPUT_RPT 0x01
#define JC_HID_RUMBLE_RPT 0x10 #define JC_HID_RUMBLE_RPT 0x10
@ -68,8 +68,7 @@
#define JC_ID_R 0x02 #define JC_ID_R 0x02
#define JC_ID_HORI 0x20 #define JC_ID_HORI 0x20
#define JC_CRC8_INIT 0x00 #define JC_CRC8_POLY 0x8D
#define JC_CRC8_POLY 0x8D
enum enum
{ {
@ -80,7 +79,7 @@ enum
JC_BATT_FULL = 8 JC_BATT_FULL = 8
}; };
static const u8 init_jc[] = { static const u8 init_wake[] = {
0xA1, 0xA2, 0xA3, 0xA4 0xA1, 0xA2, 0xA3, 0xA4
}; };
@ -91,21 +90,21 @@ static const u8 init_handshake[] = {
}; };
static const u8 init_get_info[] = { static const u8 init_get_info[] = {
0x19, 0x01, 0x03, 0x07, 0x00, // Uart header. 0x19, 0x01, 0x03, 0x07, 0x00, // Uart header.
JC_WIRED_CMD, JC_WIRED_CMD_MAC, // Wired cmd and subcmd. JC_WIRED_CMD, JC_WIRED_CMD_GET_INFO, // Wired cmd and subcmd.
0x00, 0x00, 0x00, 0x00, 0x24 // Wired subcmd data and crc. 0x00, 0x00, 0x00, 0x00, 0x24 // Wired subcmd data and crc.
}; };
static const u8 init_finalize[] = { static const u8 init_finalize[] = {
0x19, 0x01, 0x03, 0x07, 0x00, // Uart header. 0x19, 0x01, 0x03, 0x07, 0x00, // Uart header.
JC_WIRED_CMD, JC_WIRED_CMD_10, // Wired cmd and subcmd. JC_WIRED_CMD, JC_WIRED_CMD_INIT_DONE, // Wired cmd and subcmd.
0x00, 0x00, 0x00, 0x00, 0x3D // Wired subcmd data and crc. 0x00, 0x00, 0x00, 0x00, 0x3D // Wired subcmd data and crc.
}; };
static const u8 nx_pad_status[] = { static const u8 nx_pad_status[] = {
0x19, 0x01, 0x03, 0x08, 0x00, // Uart header. 0x19, 0x01, 0x03, 0x08, 0x00, // Uart header.
JC_WIRED_HID, 0x00, // Wired cmd and hid cmd. 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[] = { static const u8 hori_pad_status[] = {
@ -187,15 +186,14 @@ typedef struct _jc_hid_in_pair_data_t
typedef struct _joycon_ctxt_t typedef struct _joycon_ctxt_t
{ {
u8 buf[0x100]; //FIXME: If heap is used, dumping breaks. u8 buf[0x100]; //FIXME: If heap is used, dumping breaks.
u8 uart; u8 uart;
u8 type; u8 type;
u8 mac[6]; u8 mac[6];
u32 hw_init_done;
u32 last_received_time; u32 last_received_time;
u32 last_status_req_time; u32 last_status_req_time;
u8 rumble_sent; u8 rumble_sent;
u8 connected; u8 connected;
} joycon_ctxt_t; } joycon_ctxt_t;
static joycon_ctxt_t jc_l = {0}; static joycon_ctxt_t jc_l = {0};
@ -206,15 +204,14 @@ static u32 hid_pkt_inc = 0;
static jc_gamepad_rpt_t jc_gamepad; static jc_gamepad_rpt_t jc_gamepad;
void jc_power_supply(u8 uart, bool enable); static u8 _jc_crc(u8 *data, u16 len, u8 init)
static u8 jc_crc(u8 *data, u16 len)
{ {
u8 crc = JC_CRC8_INIT; u8 crc = init;
u16 i, j; for (u16 i = 0; i < len; i++)
for (i = 0; i < len; i++) { {
crc ^= data[i]; crc ^= data[i];
for (j = 0; j < 8; j++) { for (u16 j = 0; j < 8; j++)
{
if ((crc & 0x80) != 0) if ((crc & 0x80) != 0)
crc = (u8)((crc << 1) ^ JC_CRC8_POLY); crc = (u8)((crc << 1) ^ JC_CRC8_POLY);
else else
@ -224,13 +221,98 @@ static u8 jc_crc(u8 *data, u16 len)
return crc; 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_send(uart_port, buf, size);
uart_wait_xfer(uart_port, UART_TX_IDLE); 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[0] = 0x19;
out->uart_hdr.magic[1] = 0x01; 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) if (data)
memcpy(out->data, data, size); 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); 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; pkt_size += size;
rpt->uart_hdr.total_size_lsb += 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; 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]; u8 rpt[0x50];
memset(rpt, 0, sizeof(rpt)); 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; return (hid_pkt_inc++ & 0xF);
hid_pkt_inc++;
return (curr_id & 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]; const u8 rumble_neutral[8] = { 0x00, 0x01, 0x40, 0x40, 0x00, 0x01, 0x40, 0x40 };
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 };
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; jc_hid_out_rpt_t *hid_pkt = (jc_hid_out_rpt_t *)temp;
memcpy(hid_pkt->rumble, rumble_neutral, sizeof(rumble_neutral)); memcpy(hid_pkt->rumble, rumble_neutral, sizeof(rumble_neutral));
if (subcmd == JC_HID_SUBCMD_SND_RUMBLE) 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. // Enable rumble.
hid_pkt->cmd = JC_HID_OUTPUT_RPT; 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 = JC_HID_SUBCMD_RUMBLE_CTL;
hid_pkt->subcmd_data[0] = 1; hid_pkt->subcmd_data[0] = 1;
if (send_r_rumble) 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) 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. // Send rumble.
hid_pkt->cmd = JC_HID_RUMBLE_RPT; 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)); memcpy(hid_pkt->rumble, rumble_init, sizeof(rumble_init));
if (send_r_rumble) 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) 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); msleep(15);
// Disable rumble. // Disable rumble.
hid_pkt->cmd = JC_HID_OUTPUT_RPT; 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 = JC_HID_SUBCMD_RUMBLE_CTL;
hid_pkt->subcmd_data[0] = 0; hid_pkt->subcmd_data[0] = 0;
memcpy(hid_pkt->rumble, rumble_neutral, sizeof(rumble_neutral)); memcpy(hid_pkt->rumble, rumble_neutral, sizeof(rumble_neutral));
if (send_r_rumble) 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) 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 else
{ {
bool crc_needed = (jc_l.uart == uart) ? (jc_l.type & JC_ID_HORI) : (jc_r.type & JC_ID_HORI); 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->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; hid_pkt->subcmd = subcmd;
if (data) if (data)
memcpy(hid_pkt->subcmd_data, data, size); 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; u32 system_batt_enough = max17050_get_cached_batt_volt() > 4000;
// Power supply control based on battery levels and charging. // Power supply control based on battery levels and charging.
if ((batt >> 1 << 1) < JC_BATT_LOW) // Level without checking 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. 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; u32 btn_tmp;
jc_hid_in_rpt_t *hid_pkt = (jc_hid_in_rpt_t *)packet; 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_l = jc_l.connected;
jc_gamepad.conn_r = jc_r.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; break;
case JC_HID_SUBMCD_RPT: case JC_HID_SUBMCD_RPT:
if (hid_pkt->subcmd == JC_HID_SUBCMD_SPI_READ) 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_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; 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) if (spi_info->size == 0x1A && pair_data->magic == 0x95 && pair_data->size == 0x22)
{ {
bt_conn->type = jc->type; 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(); 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]) switch (data[0])
{ {
case JC_WIRED_CMD_MAC: case JC_WIRED_CMD_GET_INFO:
for (int i = 12; i > 6; i--) for (int i = 12; i > 6; i--)
jc->mac[12 - i] = data[i]; jc->mac[12 - i] = data[i];
jc->type = data[6]; 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_HORI_INPUT_RPT_CMD:
case JC_WIRED_HID: 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; break;
case JC_WIRED_INIT_REPLY: 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; break;
default: default:
break; 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) if (gpio_read(GPIO_PORT_E, GPIO_PIN_6) && jc->uart == UART_C)
return; 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. // 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 ((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_G, GPIO_PIN_0, GPIO_MODE_SPIO);
gpio_config(GPIO_PORT_D, GPIO_PIN_1, 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) if (jc_l.connected)
jc_l.rumble_sent = true; jc_l.rumble_sent = true;
@ -503,13 +582,13 @@ static bool jc_send_init_rumble(joycon_ctxt_t *jc)
return 0; 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); bool is_nxpad = !(jc->type & JC_ID_HORI);
if (is_nxpad) if (is_nxpad)
{ {
bool sent_rumble = jc_send_init_rumble(jc); bool sent_rumble = _jc_send_init_rumble(jc);
if (sent_rumble) if (sent_rumble)
return; 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); gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_SPIO);
if (is_nxpad) 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 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. // Turn Joy-Con detect on.
if (jc->uart == UART_B) 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()) while (jc_l.last_status_req_time > get_tmr_ms())
{ {
jc_rcv_pkt(&jc_r); _jc_rcv_pkt(&jc_r);
jc_rcv_pkt(&jc_l); _jc_rcv_pkt(&jc_l);
} }
jc_hid_in_spi_read_t subcmd_data_l; jc_hid_in_spi_read_t subcmd_data_l;
@ -605,13 +684,13 @@ retry:
{ {
if (!jc_l_found) 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; jc_l.last_status_req_time = get_tmr_ms() + 15;
} }
if (!jc_r_found) 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; jc_r.last_status_req_time = get_tmr_ms() + 15;
} }
@ -621,7 +700,7 @@ retry:
if (!jc_l_found) if (!jc_l_found)
{ {
memset(jc_l.buf, 0, 0x100); memset(jc_l.buf, 0, 0x100);
jc_rcv_pkt(&jc_l); _jc_rcv_pkt(&jc_l);
bool is_hos = false; bool is_hos = false;
if (_jc_validate_pairing_info(&jc_l.buf[SPI_READ_OFFSET], &is_hos)) if (_jc_validate_pairing_info(&jc_l.buf[SPI_READ_OFFSET], &is_hos))
@ -641,7 +720,7 @@ retry:
if (!jc_r_found) if (!jc_r_found)
{ {
memset(jc_r.buf, 0, 0x100); memset(jc_r.buf, 0, 0x100);
jc_rcv_pkt(&jc_r); _jc_rcv_pkt(&jc_r);
bool is_hos = false; bool is_hos = false;
if (_jc_validate_pairing_info(&jc_r.buf[SPI_READ_OFFSET], &is_hos)) if (_jc_validate_pairing_info(&jc_r.buf[SPI_READ_OFFSET], &is_hos))
@ -692,40 +771,11 @@ retry:
return &jc_gamepad; return &jc_gamepad;
} }
void jc_deinit() static void _jc_init_conn(joycon_ctxt_t *jc)
{
// 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)
{ {
if (((u32)get_tmr_ms() - jc->last_received_time) > 1000) 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. // Turn off Joy-Con detect.
if (jc->uart == UART_B) 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_invert(jc->uart, true, UART_INVERT_TXD);
uart_set_IIR(jc->uart); uart_set_IIR(jc->uart);
joycon_send_raw(jc->uart, init_jc, 4); _joycon_send_raw(jc->uart, init_wake, sizeof(init_wake));
joycon_send_raw(jc->uart, init_handshake, sizeof(init_handshake)); _joycon_send_raw(jc->uart, init_handshake, sizeof(init_handshake));
msleep(5); 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); msleep(5);
jc_rcv_pkt(jc); _jc_rcv_pkt(jc);
if (!(jc->type & JC_ID_HORI)) 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); msleep(5);
jc_rcv_pkt(jc); _jc_rcv_pkt(jc);
} }
// Turn Joy-Con detect on. // 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(); jc->last_received_time = get_tmr_ms();
if (jc->connected) if (jc->connected)
jc_power_supply(jc->uart, false); _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);
} }
} }
@ -870,14 +835,14 @@ void jc_init_hw()
if (fuse_read_hw_type() == FUSE_NX_HW_TYPE_HOAG) if (fuse_read_hw_type() == FUSE_NX_HW_TYPE_HOAG)
return; return;
jc_power_supply(UART_C, true); _jc_power_supply(UART_C, true);
jc_power_supply(UART_B, 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; PINMUX_AUX(PINMUX_AUX_GPIO_PH6) = PINMUX_INPUT_ENABLE | PINMUX_TRISTATE;
gpio_config(GPIO_PORT_H, GPIO_PIN_6, GPIO_MODE_GPIO); 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; PINMUX_AUX(PINMUX_AUX_GPIO_PE6) = PINMUX_INPUT_ENABLE | PINMUX_TRISTATE;
gpio_config(GPIO_PORT_E, GPIO_PIN_6, GPIO_MODE_GPIO); gpio_config(GPIO_PORT_E, GPIO_PIN_6, GPIO_MODE_GPIO);
@ -903,27 +868,56 @@ void jc_init_hw()
#endif #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() jc_gamepad_rpt_t *joycon_poll()
{ {
if (!jc_init_done) if (!jc_init_done)
return NULL; return NULL;
if (!gpio_read(GPIO_PORT_H, GPIO_PIN_6)) 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)) 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)) 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)) 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)) 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)) 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; return &jc_gamepad;
} }

View file

@ -89,7 +89,6 @@ typedef struct _jc_gamepad_rpt_t
jc_bt_conn_t bt_conn_r; jc_bt_conn_t bt_conn_r;
} jc_gamepad_rpt_t; } jc_gamepad_rpt_t;
void jc_power_supply(u8 uart, bool enable);
void jc_init_hw(); void jc_init_hw();
void jc_deinit(); void jc_deinit();
jc_gamepad_rpt_t *joycon_poll(); jc_gamepad_rpt_t *joycon_poll();