mirror of
https://github.com/CTCaer/hekate
synced 2024-12-22 11:21:23 +00:00
bdk: joycon: add Sio support (for Hoag)
This commit is contained in:
parent
f452d916c9
commit
f31170bb51
2 changed files with 335 additions and 85 deletions
|
@ -64,6 +64,21 @@
|
||||||
#define JC_HID_SUBCMD_RUMBLE_CTL 0x48
|
#define JC_HID_SUBCMD_RUMBLE_CTL 0x48
|
||||||
#define JC_HID_SUBCMD_SND_RUMBLE 0xFF
|
#define JC_HID_SUBCMD_SND_RUMBLE 0xFF
|
||||||
|
|
||||||
|
#define JC_SIO_OUTPUT_RPT 0x91
|
||||||
|
#define JC_SIO_INPUT_RPT 0x92
|
||||||
|
#define JC_SIO_CMD_ACK 0x80
|
||||||
|
|
||||||
|
#define JC_SIO_CMD_INIT 0x01
|
||||||
|
#define JC_SIO_CMD_UNK02 0x02
|
||||||
|
#define JC_SIO_CMD_VER_RPT 0x03
|
||||||
|
#define JC_SIO_CMD_UNK20 0x20 // JC_WIRED_CMD_SET_BRATE
|
||||||
|
#define JC_SIO_CMD_UNK21 0x21
|
||||||
|
#define JC_SIO_CMD_UNK22 0x22
|
||||||
|
#define JC_SIO_CMD_UNK40 0x40
|
||||||
|
#define JC_SIO_CMD_STATUS 0x41
|
||||||
|
#define JC_SIO_CMD_IAP_VER 0x42
|
||||||
|
|
||||||
|
|
||||||
#define JC_BTN_MASK_L 0xFF2900 // 0xFFE900: with charge status.
|
#define JC_BTN_MASK_L 0xFF2900 // 0xFFE900: with charge status.
|
||||||
#define JC_BTN_MASK_R 0x0056FF
|
#define JC_BTN_MASK_R 0x0056FF
|
||||||
|
|
||||||
|
@ -91,6 +106,24 @@ enum
|
||||||
JC_BATT_FULL = 8
|
JC_BATT_FULL = 8
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const u8 sio_init[] = {
|
||||||
|
JC_SIO_OUTPUT_RPT, JC_SIO_CMD_INIT,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x95
|
||||||
|
};
|
||||||
|
|
||||||
|
static const u8 sio_set_rpt_version[] = {
|
||||||
|
JC_SIO_OUTPUT_RPT, JC_SIO_CMD_VER_RPT,
|
||||||
|
// old fw: 0x00, 0x0D (0.13). New 3.4.
|
||||||
|
// force_update_en: 0x01
|
||||||
|
0x00, 0x00, 0x03, 0x04, 0x00, 0xDA
|
||||||
|
};
|
||||||
|
|
||||||
|
// Every 8ms.
|
||||||
|
static const u8 sio_pad_status[] = {
|
||||||
|
JC_SIO_OUTPUT_RPT, JC_SIO_CMD_STATUS,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xB0
|
||||||
|
};
|
||||||
|
|
||||||
static const u8 init_wake[] = {
|
static const u8 init_wake[] = {
|
||||||
0xA1, 0xA2, 0xA3, 0xA4
|
0xA1, 0xA2, 0xA3, 0xA4
|
||||||
};
|
};
|
||||||
|
@ -234,8 +267,61 @@ typedef struct _joycon_ctxt_t
|
||||||
u8 rumble_sent;
|
u8 rumble_sent;
|
||||||
u8 connected;
|
u8 connected;
|
||||||
u8 detected;
|
u8 detected;
|
||||||
|
u8 sio_mode;
|
||||||
} joycon_ctxt_t;
|
} joycon_ctxt_t;
|
||||||
|
|
||||||
|
typedef struct _jc_sio_out_rpt_t
|
||||||
|
{
|
||||||
|
u8 cmd;
|
||||||
|
u8 subcmd;
|
||||||
|
u16 len;
|
||||||
|
u8 unk[2];
|
||||||
|
u8 crc_payload;
|
||||||
|
u8 crc_hdr;
|
||||||
|
u8 payload[];
|
||||||
|
} jc_sio_out_rpt_t;
|
||||||
|
|
||||||
|
typedef struct _jc_sio_in_rpt_t
|
||||||
|
{
|
||||||
|
u8 cmd;
|
||||||
|
u8 ack;
|
||||||
|
u16 payload_size;
|
||||||
|
u8 status;
|
||||||
|
u8 unk;
|
||||||
|
u8 payload_crc;
|
||||||
|
u8 hdr_crc;
|
||||||
|
u8 payload[];
|
||||||
|
} jc_sio_in_rpt_t;
|
||||||
|
|
||||||
|
typedef struct _jc_hid_in_sixaxis_rpt_t
|
||||||
|
{
|
||||||
|
s16 acc_x;
|
||||||
|
s16 acc_y;
|
||||||
|
s16 acc_z;
|
||||||
|
s16 gyr_x;
|
||||||
|
s16 gyr_y;
|
||||||
|
s16 gyr_z;
|
||||||
|
} __attribute__((packed)) jc_hid_in_sixaxis_rpt_t;
|
||||||
|
|
||||||
|
typedef struct _jc_sio_hid_in_rpt_t
|
||||||
|
{
|
||||||
|
u8 cmd;
|
||||||
|
u8 pkt_id;
|
||||||
|
u8 unk;
|
||||||
|
u8 btn_right;
|
||||||
|
u8 btn_shared;
|
||||||
|
u8 btn_left;
|
||||||
|
u8 stick_h_left;
|
||||||
|
u8 stick_m_left;
|
||||||
|
u8 stick_v_left;
|
||||||
|
u8 stick_h_right;
|
||||||
|
u8 stick_m_right;
|
||||||
|
u8 stick_v_right;
|
||||||
|
u8 siaxis_rpt_num; // Max 15.
|
||||||
|
// Each report is 800 us?
|
||||||
|
jc_hid_in_sixaxis_rpt_t sixaxis[15];
|
||||||
|
} jc_sio_hid_in_rpt_t;
|
||||||
|
|
||||||
static joycon_ctxt_t jc_l = {0};
|
static joycon_ctxt_t jc_l = {0};
|
||||||
static joycon_ctxt_t jc_r = {0};
|
static joycon_ctxt_t jc_r = {0};
|
||||||
|
|
||||||
|
@ -270,6 +356,9 @@ static void _jc_power_supply(u8 uart, bool enable)
|
||||||
|
|
||||||
regulator_5v_enable(1 << uart);
|
regulator_5v_enable(1 << uart);
|
||||||
|
|
||||||
|
if (jc_gamepad.sio_mode)
|
||||||
|
return;
|
||||||
|
|
||||||
if (jc_init_done)
|
if (jc_init_done)
|
||||||
{
|
{
|
||||||
if (uart == UART_C)
|
if (uart == UART_C)
|
||||||
|
@ -303,6 +392,9 @@ static void _jc_power_supply(u8 uart, bool enable)
|
||||||
|
|
||||||
regulator_5v_disable(1 << uart);
|
regulator_5v_disable(1 << uart);
|
||||||
|
|
||||||
|
if (jc_gamepad.sio_mode)
|
||||||
|
return;
|
||||||
|
|
||||||
if (uart == UART_C)
|
if (uart == UART_C)
|
||||||
gpio_write(GPIO_PORT_CC, GPIO_PIN_3, GPIO_LOW);
|
gpio_write(GPIO_PORT_CC, GPIO_PIN_3, GPIO_LOW);
|
||||||
else
|
else
|
||||||
|
@ -312,25 +404,36 @@ static void _jc_power_supply(u8 uart, bool enable)
|
||||||
|
|
||||||
static void _jc_detect()
|
static void _jc_detect()
|
||||||
{
|
{
|
||||||
// Turn on Joy-Con detect. (UARTB/C TX).
|
if (!jc_gamepad.sio_mode)
|
||||||
gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_GPIO);
|
{
|
||||||
gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_GPIO);
|
// Turn on Joy-Con detect. (UARTB/C TX).
|
||||||
usleep(20);
|
gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_GPIO);
|
||||||
|
gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_GPIO);
|
||||||
|
usleep(20);
|
||||||
|
|
||||||
// Read H6/E6 which are shared with UART TX pins.
|
// Read H6/E6 which are shared with UART TX pins.
|
||||||
jc_r.detected = !gpio_read(GPIO_PORT_H, GPIO_PIN_6);
|
jc_r.detected = !gpio_read(GPIO_PORT_H, GPIO_PIN_6);
|
||||||
jc_l.detected = !gpio_read(GPIO_PORT_E, GPIO_PIN_6);
|
jc_l.detected = !gpio_read(GPIO_PORT_E, GPIO_PIN_6);
|
||||||
|
|
||||||
// Turn off Joy-Con detect. (UARTB/C TX).
|
// Turn off Joy-Con detect. (UARTB/C TX).
|
||||||
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);
|
||||||
usleep(20);
|
usleep(20);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//! TODO: Is there a way to detect a broken Sio?
|
||||||
|
jc_l.detected = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _jc_conn_check()
|
static void _jc_conn_check()
|
||||||
{
|
{
|
||||||
_jc_detect();
|
_jc_detect();
|
||||||
|
|
||||||
|
if (jc_gamepad.sio_mode)
|
||||||
|
return;
|
||||||
|
|
||||||
// Check if a Joy-Con was disconnected.
|
// Check if a Joy-Con was disconnected.
|
||||||
if (!jc_l.detected)
|
if (!jc_l.detected)
|
||||||
{
|
{
|
||||||
|
@ -607,6 +710,61 @@ static void _jc_uart_pkt_parse(joycon_ctxt_t *jc, const jc_wired_hdr_t *pkt, siz
|
||||||
jc->last_received_time = get_tmr_ms();
|
jc->last_received_time = get_tmr_ms();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _jc_sio_parse_payload(joycon_ctxt_t *jc, u8 cmd, const u8* payload, u32 size)
|
||||||
|
{
|
||||||
|
switch (cmd)
|
||||||
|
{
|
||||||
|
case JC_SIO_CMD_STATUS:
|
||||||
|
jc_sio_hid_in_rpt_t *hid_pkt = (jc_sio_hid_in_rpt_t *)payload;
|
||||||
|
jc_gamepad.buttons = hid_pkt->btn_right | hid_pkt->btn_shared << 8 | hid_pkt->btn_left << 16;
|
||||||
|
jc_gamepad.home = !gpio_read(GPIO_PORT_V, GPIO_PIN_3);
|
||||||
|
|
||||||
|
jc_gamepad.lstick_x = hid_pkt->stick_h_left | ((hid_pkt->stick_m_left & 0xF) << 8);
|
||||||
|
jc_gamepad.lstick_y = (hid_pkt->stick_m_left >> 4) | (hid_pkt->stick_v_left << 4);
|
||||||
|
jc_gamepad.rstick_x = hid_pkt->stick_h_right | ((hid_pkt->stick_m_right & 0xF) << 8);
|
||||||
|
jc_gamepad.rstick_y = (hid_pkt->stick_m_right >> 4) | (hid_pkt->stick_v_right << 4);
|
||||||
|
|
||||||
|
jc_gamepad.batt_info_l = jc_l.connected;
|
||||||
|
jc_gamepad.batt_info_r = gpio_read(GPIO_PORT_E, GPIO_PIN_7); // Set IRQ status.
|
||||||
|
jc_gamepad.conn_l = jc_l.connected;
|
||||||
|
jc_gamepad.conn_r = jc_l.connected;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _jc_sio_uart_pkt_parse(joycon_ctxt_t *jc, const jc_sio_in_rpt_t *pkt, u32 size)
|
||||||
|
{
|
||||||
|
if (pkt->hdr_crc != _jc_crc((u8 *)pkt, sizeof(jc_sio_in_rpt_t) - 1, 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
u8 cmd = pkt->ack & (~JC_SIO_CMD_ACK);
|
||||||
|
switch (cmd)
|
||||||
|
{
|
||||||
|
case JC_SIO_CMD_INIT:
|
||||||
|
jc->connected = pkt->status == 0;
|
||||||
|
break;
|
||||||
|
case JC_SIO_CMD_VER_RPT:
|
||||||
|
if (jc->connected)
|
||||||
|
jc->connected = pkt->status == 0;
|
||||||
|
break;
|
||||||
|
case JC_SIO_CMD_IAP_VER:
|
||||||
|
case JC_SIO_CMD_STATUS:
|
||||||
|
_jc_sio_parse_payload(jc, cmd, pkt->payload, pkt->payload_size);
|
||||||
|
break;
|
||||||
|
case JC_SIO_CMD_UNK02:
|
||||||
|
case JC_SIO_CMD_UNK20:
|
||||||
|
case JC_SIO_CMD_UNK21:
|
||||||
|
case JC_SIO_CMD_UNK22:
|
||||||
|
case JC_SIO_CMD_UNK40:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
jc->last_received_time = get_tmr_ms();
|
||||||
|
}
|
||||||
|
|
||||||
static void _jc_rcv_pkt(joycon_ctxt_t *jc)
|
static void _jc_rcv_pkt(joycon_ctxt_t *jc)
|
||||||
{
|
{
|
||||||
if (!jc->detected)
|
if (!jc->detected)
|
||||||
|
@ -616,10 +774,23 @@ static void _jc_rcv_pkt(joycon_ctxt_t *jc)
|
||||||
if (len < 8)
|
if (len < 8)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Check valid size and uart reply magic.
|
// For Joycon, check uart reply magic.
|
||||||
jc_wired_hdr_t *jc_pkt = (jc_wired_hdr_t *)jc->buf;
|
jc_wired_hdr_t *jc_pkt = (jc_wired_hdr_t *)jc->buf;
|
||||||
if (!memcmp(jc_pkt->uart_hdr.magic, "\x19\x81\x03", 3))
|
if (!jc->sio_mode && !memcmp(jc_pkt->uart_hdr.magic, "\x19\x81\x03", 3))
|
||||||
|
{
|
||||||
_jc_uart_pkt_parse(jc, jc_pkt, jc_pkt->uart_hdr.total_size_lsb + sizeof(jc_uart_hdr_t));
|
_jc_uart_pkt_parse(jc, jc_pkt, jc_pkt->uart_hdr.total_size_lsb + sizeof(jc_uart_hdr_t));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For Sio, check uart output report and command ack.
|
||||||
|
jc_sio_in_rpt_t *sio_pkt = (jc_sio_in_rpt_t *)(jc->buf);
|
||||||
|
if (jc->sio_mode && sio_pkt->cmd == 0x92 && (sio_pkt->ack & JC_SIO_CMD_ACK) == JC_SIO_CMD_ACK)
|
||||||
|
{
|
||||||
|
_jc_sio_uart_pkt_parse(jc, sio_pkt, sio_pkt->payload_size + sizeof(jc_sio_in_rpt_t));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _jc_send_init_rumble(joycon_ctxt_t *jc)
|
static bool _jc_send_init_rumble(joycon_ctxt_t *jc)
|
||||||
|
@ -645,7 +816,7 @@ static void _jc_req_nx_pad_status(joycon_ctxt_t *jc)
|
||||||
if (!jc->detected)
|
if (!jc->detected)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool is_nxpad = !(jc->type & JC_ID_HORI);
|
bool is_nxpad = !(jc->type & JC_ID_HORI) && !jc->sio_mode;
|
||||||
|
|
||||||
if (jc->last_status_req_time > get_tmr_ms() || !jc->connected)
|
if (jc->last_status_req_time > get_tmr_ms() || !jc->connected)
|
||||||
return;
|
return;
|
||||||
|
@ -660,6 +831,8 @@ static void _jc_req_nx_pad_status(joycon_ctxt_t *jc)
|
||||||
|
|
||||||
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 if (jc->sio_mode)
|
||||||
|
_joycon_send_raw(jc->uart, sio_pad_status, sizeof(sio_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));
|
||||||
|
|
||||||
|
@ -691,7 +864,7 @@ jc_gamepad_rpt_t *jc_get_bt_pairing_info(bool *is_l_hos, bool *is_r_hos)
|
||||||
u8 retries;
|
u8 retries;
|
||||||
jc_bt_conn_t *bt_conn;
|
jc_bt_conn_t *bt_conn;
|
||||||
|
|
||||||
if (!jc_init_done)
|
if (!jc_init_done || jc_gamepad.sio_mode)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
bt_conn = &jc_gamepad.bt_conn_l;
|
bt_conn = &jc_gamepad.bt_conn_l;
|
||||||
|
@ -848,69 +1021,103 @@ static void _jc_init_conn(joycon_ctxt_t *jc)
|
||||||
|
|
||||||
// Initialize uart to 1 megabaud and manual RTS.
|
// Initialize uart to 1 megabaud and manual RTS.
|
||||||
uart_init(jc->uart, 1000000, UART_AO_TX_MN_RX);
|
uart_init(jc->uart, 1000000, UART_AO_TX_MN_RX);
|
||||||
jc->state = JC_STATE_START;
|
|
||||||
|
|
||||||
// Set TX and RTS inversion for Joycon.
|
if (!jc->sio_mode)
|
||||||
uart_invert(jc->uart, true, UART_INVERT_TXD | UART_INVERT_RTS);
|
|
||||||
|
|
||||||
// Wake up the controller.
|
|
||||||
_joycon_send_raw(jc->uart, init_wake, sizeof(init_wake));
|
|
||||||
_jc_rcv_pkt(jc); // Clear RX FIFO.
|
|
||||||
|
|
||||||
// Do a handshake.
|
|
||||||
u32 retries = 10;
|
|
||||||
while (retries && jc->state != JC_STATE_HANDSHAKED)
|
|
||||||
{
|
{
|
||||||
_joycon_send_raw(jc->uart, init_handshake, sizeof(init_handshake));
|
jc->state = JC_STATE_START;
|
||||||
|
|
||||||
msleep(5);
|
// Set TX and RTS inversion for Joycon.
|
||||||
_jc_rcv_pkt(jc);
|
uart_invert(jc->uart, true, UART_INVERT_TXD | UART_INVERT_RTS);
|
||||||
retries--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jc->state != JC_STATE_HANDSHAKED)
|
// Wake up the controller.
|
||||||
goto out;
|
_joycon_send_raw(jc->uart, init_wake, sizeof(init_wake));
|
||||||
|
_jc_rcv_pkt(jc); // Clear RX FIFO.
|
||||||
|
|
||||||
// Get info about the controller.
|
// Do a handshake.
|
||||||
_joycon_send_raw(jc->uart, init_get_info, sizeof(init_get_info));
|
u32 retries = 10;
|
||||||
msleep(2);
|
while (retries && jc->state != JC_STATE_HANDSHAKED)
|
||||||
_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.
|
_joycon_send_raw(jc->uart, init_handshake, sizeof(init_handshake));
|
||||||
uart_init(jc->uart, 3000000, UART_AO_TX_MN_RX);
|
msleep(5);
|
||||||
uart_invert(jc->uart, true, UART_INVERT_TXD | UART_INVERT_RTS);
|
_jc_rcv_pkt(jc);
|
||||||
|
retries--;
|
||||||
// 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.
|
if (jc->state != JC_STATE_HANDSHAKED)
|
||||||
_joycon_send_raw(jc->uart, init_finalize, sizeof(init_finalize));
|
goto out;
|
||||||
|
|
||||||
|
// Get info about the controller.
|
||||||
|
_joycon_send_raw(jc->uart, init_get_info, sizeof(init_get_info));
|
||||||
msleep(2);
|
msleep(2);
|
||||||
_jc_rcv_pkt(jc);
|
_jc_rcv_pkt(jc);
|
||||||
|
|
||||||
// Set packet rate.
|
if (!(jc->type & JC_ID_HORI))
|
||||||
_joycon_send_raw(jc->uart, init_set_rpt_rate, sizeof(init_set_rpt_rate));
|
{
|
||||||
msleep(2);
|
// 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(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Set Sio POR low to configure BOOT0 mode.
|
||||||
|
gpio_write(GPIO_PORT_CC, GPIO_PIN_5, GPIO_LOW);
|
||||||
|
usleep(300);
|
||||||
|
gpio_write(GPIO_PORT_T, GPIO_PIN_0, GPIO_LOW);
|
||||||
|
gpio_output_enable(GPIO_PORT_T, GPIO_PIN_1, GPIO_OUTPUT_DISABLE);
|
||||||
|
gpio_write(GPIO_PORT_CC, GPIO_PIN_5, GPIO_HIGH);
|
||||||
|
msleep(100);
|
||||||
|
|
||||||
|
// Clear RX FIFO.
|
||||||
|
_jc_rcv_pkt(jc);
|
||||||
|
|
||||||
|
// Initialize the controller.
|
||||||
|
u32 retries = 10;
|
||||||
|
while (!jc->connected)
|
||||||
|
{
|
||||||
|
_joycon_send_raw(jc->uart, sio_init, sizeof(sio_init));
|
||||||
|
msleep(5);
|
||||||
|
_jc_rcv_pkt(jc);
|
||||||
|
retries--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!jc->connected)
|
||||||
|
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);
|
_jc_rcv_pkt(jc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -920,7 +1127,7 @@ static void _jc_init_conn(joycon_ctxt_t *jc)
|
||||||
out:
|
out:
|
||||||
jc->last_received_time = get_tmr_ms();
|
jc->last_received_time = get_tmr_ms();
|
||||||
|
|
||||||
if (jc->connected)
|
if (!jc->sio_mode && jc->connected)
|
||||||
_jc_power_supply(jc->uart, false);
|
_jc_power_supply(jc->uart, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -930,13 +1137,42 @@ void jc_init_hw()
|
||||||
jc_l.uart = UART_C;
|
jc_l.uart = UART_C;
|
||||||
jc_r.uart = UART_B;
|
jc_r.uart = UART_B;
|
||||||
|
|
||||||
#if !defined(DEBUG_UART_PORT) || !(DEBUG_UART_PORT)
|
jc_l.sio_mode = fuse_read_hw_type() == FUSE_NX_HW_TYPE_HOAG;
|
||||||
if (fuse_read_hw_type() == FUSE_NX_HW_TYPE_HOAG)
|
jc_gamepad.sio_mode = jc_l.sio_mode;
|
||||||
return;
|
|
||||||
|
|
||||||
|
#if !defined(DEBUG_UART_PORT) || !(DEBUG_UART_PORT)
|
||||||
_jc_power_supply(UART_C, true);
|
_jc_power_supply(UART_C, true);
|
||||||
_jc_power_supply(UART_B, true);
|
_jc_power_supply(UART_B, true);
|
||||||
|
|
||||||
|
// Sio Initialization.
|
||||||
|
if (jc_gamepad.sio_mode)
|
||||||
|
{
|
||||||
|
// Enable 4 MHz clock to Sio.
|
||||||
|
clock_enable_extperiph2();
|
||||||
|
PINMUX_AUX(PINMUX_AUX_TOUCH_CLK) = PINMUX_PULL_DOWN;
|
||||||
|
|
||||||
|
// Configure Sio HOME BUTTON.
|
||||||
|
PINMUX_AUX(PINMUX_AUX_LCD_GPIO1) = PINMUX_INPUT_ENABLE | PINMUX_TRISTATE | PINMUX_PULL_UP | 1;
|
||||||
|
gpio_config(GPIO_PORT_V, GPIO_PIN_3, GPIO_MODE_GPIO);
|
||||||
|
|
||||||
|
// Configure Sio IRQ
|
||||||
|
PINMUX_AUX(PINMUX_AUX_GPIO_PE7) = PINMUX_INPUT_ENABLE | PINMUX_TRISTATE | PINMUX_PULL_UP;
|
||||||
|
gpio_config(GPIO_PORT_E, GPIO_PIN_7, GPIO_MODE_GPIO);
|
||||||
|
|
||||||
|
// Configure Sio RST and BOOT0.
|
||||||
|
PINMUX_AUX(PINMUX_AUX_CAM1_STROBE) = PINMUX_PULL_DOWN | 1;
|
||||||
|
PINMUX_AUX(PINMUX_AUX_CAM2_PWDN) = PINMUX_PULL_DOWN | 1;
|
||||||
|
gpio_config(GPIO_PORT_T, GPIO_PIN_1 | GPIO_PIN_0, GPIO_MODE_GPIO);
|
||||||
|
gpio_output_enable(GPIO_PORT_T, GPIO_PIN_1 | GPIO_PIN_0, GPIO_OUTPUT_ENABLE);
|
||||||
|
gpio_write(GPIO_PORT_T, GPIO_PIN_1 | GPIO_PIN_0, GPIO_LOW);
|
||||||
|
|
||||||
|
// Configure Sio POR.
|
||||||
|
PINMUX_AUX(PINMUX_AUX_USB_VBUS_EN1) = PINMUX_IO_HV | PINMUX_LPDR | 1;
|
||||||
|
gpio_config(GPIO_PORT_CC, GPIO_PIN_5, GPIO_MODE_GPIO);
|
||||||
|
gpio_output_enable(GPIO_PORT_CC, GPIO_PIN_5, GPIO_OUTPUT_ENABLE);
|
||||||
|
gpio_write(GPIO_PORT_CC, GPIO_PIN_5, GPIO_LOW);
|
||||||
|
}
|
||||||
|
|
||||||
// Joy-Con (R) IsAttached. Shared with UARTB TX.
|
// 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);
|
||||||
|
@ -946,14 +1182,16 @@ void jc_init_hw()
|
||||||
gpio_config(GPIO_PORT_E, GPIO_PIN_6, GPIO_MODE_GPIO);
|
gpio_config(GPIO_PORT_E, GPIO_PIN_6, GPIO_MODE_GPIO);
|
||||||
|
|
||||||
// Configure pinmuxing for UART B and C.
|
// Configure pinmuxing for UART B and C.
|
||||||
pinmux_config_uart(UART_B);
|
if (!jc_gamepad.sio_mode)
|
||||||
|
pinmux_config_uart(UART_B);
|
||||||
pinmux_config_uart(UART_C);
|
pinmux_config_uart(UART_C);
|
||||||
|
|
||||||
// Ease the stress to APB.
|
// Ease the stress to APB.
|
||||||
bpmp_freq_t prev_fid = bpmp_clk_rate_set(BPMP_CLK_NORMAL);
|
bpmp_freq_t prev_fid = bpmp_clk_rate_set(BPMP_CLK_NORMAL);
|
||||||
|
|
||||||
// Enable UART B and C clocks.
|
// Enable UART B and C clocks.
|
||||||
clock_enable_uart(UART_B);
|
if (!jc_gamepad.sio_mode)
|
||||||
|
clock_enable_uart(UART_B);
|
||||||
clock_enable_uart(UART_C);
|
clock_enable_uart(UART_C);
|
||||||
|
|
||||||
// Restore OC.
|
// Restore OC.
|
||||||
|
@ -972,22 +1210,33 @@ void jc_deinit()
|
||||||
_jc_power_supply(UART_B, false);
|
_jc_power_supply(UART_B, false);
|
||||||
_jc_power_supply(UART_C, false);
|
_jc_power_supply(UART_C, false);
|
||||||
|
|
||||||
// Send sleep command.
|
if (!jc_gamepad.sio_mode)
|
||||||
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);
|
// Send sleep command.
|
||||||
_jc_rcv_pkt(&jc_r);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (jc_l.connected && !(jc_l.type & JC_ID_HORI))
|
else
|
||||||
{
|
{
|
||||||
_jc_send_hid_cmd(UART_C, JC_HID_SUBCMD_HCI_STATE, &data, 1);
|
// Disable Sio POR.
|
||||||
_jc_rcv_pkt(&jc_l);
|
gpio_write(GPIO_PORT_CC, GPIO_PIN_5, GPIO_LOW);
|
||||||
|
|
||||||
|
// Disable 4 MHz clock to Sio.
|
||||||
|
clock_disable_extperiph2();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable UART B and C clocks.
|
// Disable UART B and C clocks.
|
||||||
clock_disable_uart(UART_B);
|
if (!jc_gamepad.sio_mode)
|
||||||
|
clock_disable_uart(UART_B);
|
||||||
clock_disable_uart(UART_C);
|
clock_disable_uart(UART_C);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,8 +83,9 @@ typedef struct _jc_gamepad_rpt_t
|
||||||
bool center_stick_r;
|
bool center_stick_r;
|
||||||
bool conn_l;
|
bool conn_l;
|
||||||
bool conn_r;
|
bool conn_r;
|
||||||
u8 batt_info_l;
|
bool sio_mode;
|
||||||
u8 batt_info_r;
|
u8 batt_info_l; // Also Sio Connected status.
|
||||||
|
u8 batt_info_r; // Also Sio IRQ.
|
||||||
jc_bt_conn_t bt_conn_l;
|
jc_bt_conn_t bt_conn_l;
|
||||||
jc_bt_conn_t bt_conn_r;
|
jc_bt_conn_t bt_conn_r;
|
||||||
} jc_gamepad_rpt_t;
|
} jc_gamepad_rpt_t;
|
||||||
|
|
Loading…
Reference in a new issue