joycon: add support for the hori split pad pro

This commit is contained in:
Franz-Josef Haider 2020-12-31 11:32:25 +01:00 committed by Frajo Haider
parent 2fba9848ae
commit e491a4cf57

View file

@ -39,6 +39,9 @@
#define JC_WIRED_INIT_REPLY 0x94 #define JC_WIRED_INIT_REPLY 0x94
#define JC_INIT_HANDSHAKE 0xA5 #define JC_INIT_HANDSHAKE 0xA5
#define JC_HORI_INPUT_RPT_CMD 0x9A
#define JC_HORI_INPUT_RPT 0x00
#define JC_WIRED_CMD_MAC 0x01 #define JC_WIRED_CMD_MAC 0x01
#define JC_WIRED_CMD_10 0x10 #define JC_WIRED_CMD_10 0x10
@ -61,8 +64,12 @@
#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
#define JC_ID_L 1 #define JC_ID_L 0x01
#define JC_ID_R 2 #define JC_ID_R 0x02
#define JC_ID_HORI 0x20
#define JC_CRC8_INIT 0x00
#define JC_CRC8_POLY 0x8D
enum enum
{ {
@ -80,25 +87,31 @@ static const u8 init_jc[] = {
static const u8 init_handshake[] = { static const u8 init_handshake[] = {
0x19, 0x01, 0x03, 0x07, 0x00, // Uart header. 0x19, 0x01, 0x03, 0x07, 0x00, // Uart header.
JC_INIT_HANDSHAKE, 0x02, // Wired cmd and wired subcmd. JC_INIT_HANDSHAKE, 0x02, // Wired cmd and wired subcmd.
0x01, 0x7E, 0x00, 0x00, 0x00 // Wired subcmd data. 0x01, 0x7E, 0x00, 0x00, 0x00 // Wired subcmd data and crc.
}; };
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_MAC, // Wired cmd and subcmd.
0x00, 0x00, 0x00, 0x00, 0x24 // Wired subcmd data. 0x00, 0x00, 0x00, 0x00, 0x24 // Wired subcmd data and crc.
}; };
static const u8 init_finilize[] = { static const u8 init_finilize[] = {
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_10, // Wired cmd and subcmd.
0x00, 0x00, 0x00, 0x00, 0x3D // Wired subcmd data. 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. 0x01, 0x00, 0x00, 0x69, 0x2D, 0x1F // hid data and crc.
};
static const u8 hori_pad_status[] = {
0x19, 0x01, 0x03, 0x07, 0x00, // Uart header.
JC_HORI_INPUT_RPT_CMD, 0x01, // Hori cmd and hori subcmd.
0x00, 0x00, 0x00, 0x00, 0x48 // Hori cmd data and crc.
}; };
typedef struct _jc_uart_hdr_t typedef struct _jc_uart_hdr_t
@ -201,6 +214,22 @@ void joycon_send_raw(u8 uart_port, const u8 *buf, u16 size)
uart_wait_idle(uart_port, UART_TX_IDLE); uart_wait_idle(uart_port, UART_TX_IDLE);
} }
static u8 jc_crc(u8 *data, u16 len)
{
u8 crc = JC_CRC8_INIT;
u16 i, j;
for (i = 0; i < len; i++) {
crc ^= data[i];
for (j = 0; j < 8; j++) {
if ((crc & 0x80) != 0)
crc = (u8)((crc << 1) ^ JC_CRC8_POLY);
else
crc <<= 1;
}
}
return crc;
}
static u16 jc_packet_add_uart_hdr(jc_wired_hdr_t *out, u8 wired_cmd, u8 *data, u16 size) static u16 jc_packet_add_uart_hdr(jc_wired_hdr_t *out, u8 wired_cmd, u8 *data, u16 size)
{ {
out->uart_hdr.magic[0] = 0x19; out->uart_hdr.magic[0] = 0x19;
@ -214,7 +243,7 @@ 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 = 0; // wired crc8ccit can be skipped. out->crc = jc_crc(&out->uart_hdr.total_size_msb, sizeof(out->uart_hdr.total_size_msb) + sizeof(out->cmd) + sizeof(out->data));
return sizeof(jc_wired_hdr_t); return sizeof(jc_wired_hdr_t);
} }
@ -333,6 +362,7 @@ static void jc_parse_wired_hid(joycon_ctxt_t *jc, const u8* packet, u32 size)
switch (hid_pkt->cmd) switch (hid_pkt->cmd)
{ {
case JC_HORI_INPUT_RPT:
case JC_HID_INPUT_RPT: case JC_HID_INPUT_RPT:
btn_tmp = hid_pkt->btn_right | hid_pkt->btn_shared << 8 | hid_pkt->btn_left << 16; btn_tmp = hid_pkt->btn_right | hid_pkt->btn_shared << 8 | hid_pkt->btn_left << 16;
@ -412,6 +442,7 @@ static void jc_uart_pkt_parse(joycon_ctxt_t *jc, const u8* packet, size_t size)
jc_wired_hdr_t *pkt = (jc_wired_hdr_t *)packet; jc_wired_hdr_t *pkt = (jc_wired_hdr_t *)packet;
switch (pkt->cmd) switch (pkt->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;
@ -499,6 +530,28 @@ static void jc_req_nx_pad_status(joycon_ctxt_t *jc)
jc->last_status_req_time = get_tmr_ms() + 15; jc->last_status_req_time = get_tmr_ms() + 15;
} }
static void jc_req_hori_pad_status(joycon_ctxt_t *jc)
{
if (jc->last_status_req_time > get_tmr_ms() || !jc->connected)
return;
// Turn off Joy-Con detect.
if (jc->uart == UART_B)
gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_SPIO);
else
gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_SPIO);
joycon_send_raw(jc->uart, hori_pad_status, sizeof(hori_pad_status));
// Turn Joy-Con detect on.
if (jc->uart == UART_B)
gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_GPIO);
else
gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_GPIO);
jc->last_status_req_time = get_tmr_ms() + 15;
}
static bool _jc_validate_pairing_info(u8 *buf, bool *is_hos) static bool _jc_validate_pairing_info(u8 *buf, bool *is_hos)
{ {
u8 crc = 0; u8 crc = 0;
@ -662,13 +715,17 @@ void jc_deinit()
if (jc_r.connected) if (jc_r.connected)
{ {
jc_send_hid_cmd(UART_B, JC_HID_SUBCMD_HCI_STATE, &data, 1); if (!(jc_r.type & JC_ID_HORI)) {
jc_rcv_pkt(&jc_r); jc_send_hid_cmd(UART_B, JC_HID_SUBCMD_HCI_STATE, &data, 1);
jc_rcv_pkt(&jc_r);
}
} }
if (jc_l.connected) if (jc_l.connected)
{ {
jc_send_hid_cmd(UART_C, JC_HID_SUBCMD_HCI_STATE, &data, 1); if (!(jc_l.type & JC_ID_HORI)) {
jc_rcv_pkt(&jc_l); jc_send_hid_cmd(UART_C, JC_HID_SUBCMD_HCI_STATE, &data, 1);
jc_rcv_pkt(&jc_l);
}
} }
jc_power_supply(UART_B, false); jc_power_supply(UART_B, false);
@ -709,9 +766,11 @@ static void jc_init_conn(joycon_ctxt_t *jc)
msleep(5); msleep(5);
jc_rcv_pkt(jc); jc_rcv_pkt(jc);
joycon_send_raw(jc->uart, init_finilize, sizeof(init_finilize)); if (!(jc->type & JC_ID_HORI)) {
msleep(5); joycon_send_raw(jc->uart, init_finilize, sizeof(init_finilize));
jc_rcv_pkt(jc); msleep(5);
jc_rcv_pkt(jc);
}
// Turn Joy-Con detect on. // Turn Joy-Con detect on.
if (jc->uart == UART_B) if (jc->uart == UART_B)
@ -863,10 +922,20 @@ jc_gamepad_rpt_t *joycon_poll()
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); if (jc_r.type & JC_ID_HORI) {
if (!gpio_read(GPIO_PORT_E, GPIO_PIN_6)) jc_req_hori_pad_status(&jc_r);
jc_req_nx_pad_status(&jc_l); } else {
jc_req_nx_pad_status(&jc_r);
}
}
if (!gpio_read(GPIO_PORT_E, GPIO_PIN_6)) {
if (jc_l.type & JC_ID_HORI) {
jc_req_hori_pad_status(&jc_l);
} else {
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);