Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
CTCaer
GitHub Repository: CTCaer/hekate
Path: blob/master/bdk/input/joycon.c
1476 views
1
/*
2
* Joy-Con UART driver for Nintendo Switch
3
*
4
* Copyright (c) 2019-2024 CTCaer
5
*
6
* This program is free software; you can redistribute it and/or modify it
7
* under the terms and conditions of the GNU General Public License,
8
* version 2, as published by the Free Software Foundation.
9
*
10
* This program is distributed in the hope it will be useful, but WITHOUT
11
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13
* more details.
14
*
15
* You should have received a copy of the GNU General Public License
16
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17
*/
18
19
#include <string.h>
20
21
#include "joycon.h"
22
#include <gfx_utils.h>
23
#include <power/max17050.h>
24
#include <power/regulator_5v.h>
25
#include <soc/clock.h>
26
#include <soc/fuse.h>
27
#include <soc/gpio.h>
28
#include <soc/pinmux.h>
29
#include <soc/timer.h>
30
#include <soc/uart.h>
31
#include <soc/t210.h>
32
33
// For disabling driver when logging is enabled.
34
#include <libs/lv_conf.h>
35
36
#define JC_WIRED_CMD 0x91
37
#define JC_WIRED_HID 0x92
38
#define JC_WIRED_INIT_REPLY 0x94
39
#define JC_INIT_HANDSHAKE 0xA5
40
41
#define JC_HORI_INPUT_RPT_CMD 0x9A
42
#define JC_HORI_INPUT_RPT 0x00
43
44
#define JC_WIRED_CMD_GET_INFO 0x01
45
#define JC_WIRED_CMD_SET_CHARGER 0x02
46
#define JC_WIRED_CMD_GET_CHARGER 0x03
47
#define JC_WIRED_CMD_BATT_VOLT 0x06
48
#define JC_WIRED_CMD_WAKE_REASON 0x07
49
#define JC_WIRED_CMD_HID_CONN 0x10
50
#define JC_WIRED_CMD_HID_DISC 0x11
51
#define JC_WIRED_CMD_SET_HIDRATE 0x12 // Output report rate.
52
#define JC_WIRED_CMD_SET_BRATE 0x20
53
54
#define JC_HID_OUTPUT_RPT 0x01
55
#define JC_HID_RUMBLE_RPT 0x10
56
57
#define JC_HID_INPUT_RPT 0x30
58
#define JC_HID_SUBMCD_RPT 0x21
59
60
#define JC_HID_SUBCMD_HCI_STATE 0x06
61
#define HCI_STATE_SLEEP 0x00
62
#define HCI_STATE_RECONNECT 0x01
63
#define HCI_STATE_PAIR 0x02
64
#define HCI_STATE_HOME 0x04
65
#define JC_HID_SUBCMD_SPI_READ 0x10
66
#define SPI_READ_OFFSET 0x20
67
#define JC_HID_SUBCMD_RUMBLE_CTL 0x48
68
#define JC_HID_SUBCMD_SND_RUMBLE 0xFF
69
70
#define JC_SIO_OUTPUT_RPT 0x91
71
#define JC_SIO_INPUT_RPT 0x92
72
#define JC_SIO_CMD_ACK 0x80
73
74
#define JC_SIO_CMD_INIT 0x01
75
#define JC_SIO_CMD_UNK02 0x02
76
#define JC_SIO_CMD_VER_RPT 0x03
77
#define JC_SIO_CMD_UNK20 0x20 // JC_WIRED_CMD_SET_BRATE
78
#define JC_SIO_CMD_UNK21 0x21
79
#define JC_SIO_CMD_UNK22 0x22
80
#define JC_SIO_CMD_UNK40 0x40
81
#define JC_SIO_CMD_STATUS 0x41
82
#define JC_SIO_CMD_IAP_VER 0x42
83
84
85
#define JC_BTN_MASK_L 0xFF2900 // 0xFFE900: with charge status.
86
#define JC_BTN_MASK_R 0x0056FF
87
88
#define JC_ID_L 0x01 // Joycon (L). Mask for Hori (L).
89
#define JC_ID_R 0x02 // Joycon (R). Mask for Hori (R).
90
#define JC_ID_HORI 0x20 // Mask for Hori. Actual ids: 0x21, 0x22.
91
92
#define JC_CRC8_POLY 0x8D
93
94
enum
95
{
96
JC_STATE_START = 0,
97
JC_STATE_HANDSHAKED = 1,
98
JC_STATE_BRATE_CHANGED = 2,
99
JC_STATE_BRATE_OK = 3,
100
JC_STATE_INIT_DONE = 4
101
};
102
103
enum
104
{
105
JC_BATT_EMTPY = 0,
106
JC_BATT_CRIT = 1,
107
JC_BATT_LOW = 2,
108
JC_BATT_MID = 3,
109
JC_BATT_FULL = 4
110
};
111
112
static const u8 sio_init[] = {
113
JC_SIO_OUTPUT_RPT, JC_SIO_CMD_INIT,
114
0x00, 0x00, 0x00, 0x00, 0x00, 0x95
115
};
116
117
static const u8 sio_set_rpt_version[] = {
118
JC_SIO_OUTPUT_RPT, JC_SIO_CMD_VER_RPT,
119
// old fw: 0x00, 0x0D (0.13). New 3.4.
120
// force_update_en: 0x01
121
0x00, 0x00, 0x03, 0x04, 0x00, 0xDA
122
};
123
124
// Every 8ms.
125
static const u8 sio_pad_status[] = {
126
JC_SIO_OUTPUT_RPT, JC_SIO_CMD_STATUS,
127
0x00, 0x00, 0x00, 0x00, 0x00, 0xB0
128
};
129
130
static const u8 init_wake[] = {
131
0xA1, 0xA2, 0xA3, 0xA4
132
};
133
134
static const u8 init_handshake[] = {
135
0x19, 0x01, 0x03, 0x07, 0x00, // Uart header.
136
JC_INIT_HANDSHAKE, 0x02, // Wired cmd and wired subcmd.
137
0x01, 0x7E, 0x00, 0x00, 0x00 // Wired subcmd data and crc.
138
};
139
140
static const u8 init_get_info[] = {
141
0x19, 0x01, 0x03, 0x07, 0x00, // Uart header.
142
JC_WIRED_CMD, JC_WIRED_CMD_GET_INFO, // Wired cmd and subcmd.
143
0x00, 0x00, 0x00, 0x00, 0x24 // Wired subcmd data and crc.
144
};
145
146
static const u8 init_switch_brate[] = {
147
0x19, 0x01, 0x03, 0x0F, 0x00, // Uart header.
148
JC_WIRED_CMD, JC_WIRED_CMD_SET_BRATE, // Wired cmd and subcmd.
149
0x08, 0x00, 0x00, 0xBD, 0xB1, // Wired subcmd data, data crc and crc.
150
// Baudrate 3 megabaud.
151
0xC0, 0xC6, 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00
152
};
153
154
static const u8 init_hid_disconnect[] = {
155
0x19, 0x01, 0x03, 0x07, 0x00, // Uart header.
156
JC_WIRED_CMD, JC_WIRED_CMD_HID_DISC, // Wired cmd and subcmd.
157
0x00, 0x00, 0x00, 0x00, 0x0E // Wired subcmd data and crc.
158
};
159
160
static const u8 init_set_hid_rate[] = {
161
0x19, 0x01, 0x03, 0x0B, 0x00, // Uart header.
162
JC_WIRED_CMD, JC_WIRED_CMD_SET_HIDRATE, // Wired cmd and subcmd.
163
0x04, 0x00, 0x00, 0x12, 0xA6, // Wired subcmd data, data crc and crc.
164
// Output report rate 15 ms.
165
0x0F, 0x00, 0x00, 0x00
166
167
// 5 ms.
168
// 0x04, 0x00, 0x00, 0x0E, 0xD5,
169
// 0x05, 0x00, 0x00, 0x00
170
};
171
172
static const u8 init_hid_connect[] = {
173
0x19, 0x01, 0x03, 0x07, 0x00, // Uart header.
174
JC_WIRED_CMD, JC_WIRED_CMD_HID_CONN, // Wired cmd and subcmd.
175
0x00, 0x00, 0x00, 0x00, 0x3D // Wired subcmd data and crc.
176
};
177
178
static const u8 nx_pad_status[] = {
179
0x19, 0x01, 0x03, 0x08, 0x00, // Uart header.
180
JC_WIRED_HID, 0x00, // Wired cmd and hid cmd.
181
0x01, 0x00, 0x00, 0x69, 0x2D, 0x1F // hid data, data crc and crc.
182
};
183
184
static const u8 hori_pad_status[] = {
185
0x19, 0x01, 0x03, 0x07, 0x00, // Uart header.
186
JC_HORI_INPUT_RPT_CMD, 0x01, // Hori cmd and hori subcmd.
187
0x00, 0x00, 0x00, 0x00, 0x48 // Hori cmd data and crc.
188
};
189
190
typedef struct _jc_uart_hdr_t
191
{
192
u8 magic[3];
193
u8 total_size_lsb;
194
u8 total_size_msb;
195
} jc_uart_hdr_t;
196
197
typedef struct _jc_wired_hdr_t
198
{
199
jc_uart_hdr_t uart_hdr;
200
u8 cmd;
201
u8 data[5];
202
u8 crc;
203
u8 payload[];
204
} jc_wired_hdr_t;
205
206
typedef struct _jc_hid_out_rpt_t
207
{
208
u8 cmd;
209
u8 pkt_id;
210
u8 rumble[8];
211
u8 subcmd;
212
u8 subcmd_data[];
213
} jc_hid_out_rpt_t;
214
215
typedef struct _jc_hid_out_spi_read_t
216
{
217
u32 addr;
218
u8 size;
219
} jc_hid_out_spi_read_t;
220
221
typedef struct _jc_hid_in_rpt_t
222
{
223
u8 cmd;
224
u8 pkt_id;
225
u8 conn_info:4; // Connection detect.
226
u8 batt_info:4; // Power info.
227
u8 btn_right;
228
u8 btn_shared;
229
u8 btn_left;
230
u8 stick_h_left;
231
u8 stick_m_left;
232
u8 stick_v_left;
233
u8 stick_h_right;
234
u8 stick_m_right;
235
u8 stick_v_right;
236
u8 vib_decider; // right:4, left:4 (bit3 en, bit2-0 buffer avail).
237
u8 submcd_ack;
238
u8 subcmd;
239
u8 subcmd_data[];
240
} jc_hid_in_rpt_t;
241
242
typedef struct _jc_hid_in_spi_read_t
243
{
244
u32 addr;
245
u8 size;
246
u8 data[];
247
} jc_hid_in_spi_read_t;
248
249
typedef struct _jc_hid_in_pair_data_t
250
{
251
u8 magic;
252
u8 size;
253
u16 checksum;
254
u8 mac[6];
255
u8 ltk[16];
256
u8 pad0[10];
257
u8 bt_caps; // bit3: Secure conn supported host, bit5: Paired to TBFC supported host, bit6: iTBFC page supported
258
u8 pad1;
259
} jc_hid_in_pair_data_t;
260
261
typedef struct _jc_sio_out_rpt_t
262
{
263
u8 cmd;
264
u8 subcmd;
265
u16 payload_size;
266
u8 data[2];
267
u8 crc_payload;
268
u8 crc_hdr;
269
u8 payload[];
270
} jc_sio_out_rpt_t;
271
272
typedef struct _jc_sio_in_rpt_t
273
{
274
u8 cmd;
275
u8 ack;
276
u16 payload_size;
277
u8 status;
278
u8 unk;
279
u8 crc_payload;
280
u8 crc_hdr;
281
u8 payload[];
282
} jc_sio_in_rpt_t;
283
284
typedef struct _jc_hid_in_sixaxis_rpt_t
285
{
286
s16 acc_x;
287
s16 acc_y;
288
s16 acc_z;
289
s16 gyr_x;
290
s16 gyr_y;
291
s16 gyr_z;
292
} __attribute__((packed)) jc_hid_in_sixaxis_rpt_t;
293
294
typedef struct _jc_sio_hid_in_rpt_t
295
{
296
u8 type;
297
u8 pkt_id;
298
u8 unk;
299
u8 btn_right;
300
u8 btn_shared;
301
u8 btn_left;
302
u8 stick_h_left;
303
u8 stick_m_left;
304
u8 stick_v_left;
305
u8 stick_h_right;
306
u8 stick_m_right;
307
u8 stick_v_right;
308
u8 siaxis_rpt; // bit0-3: report num. bit4-7: imu type.
309
// Each report is 800 us?
310
jc_hid_in_sixaxis_rpt_t sixaxis[15];
311
} jc_sio_hid_in_rpt_t;
312
313
typedef struct _joycon_ctxt_t
314
{
315
u8 buf[0x100]; //FIXME: If heap is used, dumping breaks.
316
u8 uart;
317
u8 type;
318
u8 state;
319
u32 last_received_time;
320
u32 last_status_req_time;
321
u8 mac[6];
322
u8 pkt_id;
323
u8 rumble_sent;
324
u8 connected;
325
u8 detected;
326
u8 sio_mode;
327
} joycon_ctxt_t;
328
329
static joycon_ctxt_t jc_l = {0};
330
static joycon_ctxt_t jc_r = {0};
331
332
static bool jc_init_done = false;
333
334
static jc_gamepad_rpt_t jc_gamepad;
335
336
static u8 _jc_crc(const u8 *data, u16 len, u8 init)
337
{
338
u8 crc = init;
339
for (u16 i = 0; i < len; i++)
340
{
341
crc ^= data[i];
342
for (u16 j = 0; j < 8; j++)
343
{
344
if ((crc & 0x80) != 0)
345
crc = (u8)((crc << 1) ^ JC_CRC8_POLY);
346
else
347
crc <<= 1;
348
}
349
}
350
return crc;
351
}
352
353
static void _jc_power_supply(u8 uart, bool enable)
354
{
355
if (enable)
356
{
357
if (regulator_5v_get_dev_enabled(1 << uart))
358
return;
359
360
regulator_5v_enable(1 << uart);
361
362
if (jc_gamepad.sio_mode)
363
return;
364
365
if (jc_init_done)
366
{
367
if (uart == UART_C)
368
gpio_write(GPIO_PORT_CC, GPIO_PIN_3, GPIO_HIGH);
369
else
370
gpio_write(GPIO_PORT_K, GPIO_PIN_3, GPIO_HIGH);
371
return;
372
}
373
374
if (uart == UART_C)
375
{
376
// Joy-Con(L) Charge Enable.
377
PINMUX_AUX(PINMUX_AUX_SPDIF_IN) = PINMUX_PULL_DOWN | 1;
378
gpio_direction_output(GPIO_PORT_CC, GPIO_PIN_3, GPIO_HIGH);
379
}
380
else
381
{
382
// Joy-Con(R) Charge Enable.
383
PINMUX_AUX(PINMUX_AUX_GPIO_PK3) = PINMUX_DRIVE_4X | PINMUX_PULL_DOWN | 2;
384
gpio_direction_output(GPIO_PORT_K, GPIO_PIN_3, GPIO_HIGH);
385
}
386
}
387
else
388
{
389
if (!regulator_5v_get_dev_enabled(1 << uart))
390
return;
391
392
regulator_5v_disable(1 << uart);
393
394
if (jc_gamepad.sio_mode)
395
return;
396
397
if (uart == UART_C)
398
gpio_write(GPIO_PORT_CC, GPIO_PIN_3, GPIO_LOW);
399
else
400
gpio_write(GPIO_PORT_K, GPIO_PIN_3, GPIO_LOW);
401
}
402
}
403
404
static void _jc_detect()
405
{
406
if (!jc_gamepad.sio_mode)
407
{
408
// Turn on Joy-Con detect. (UARTB/C TX). UART CTS also if HW flow control and irq is enabled.
409
PINMUX_AUX(PINMUX_AUX_UART2_TX) = PINMUX_INPUT_ENABLE;
410
PINMUX_AUX(PINMUX_AUX_UART3_TX) = PINMUX_INPUT_ENABLE;
411
gpio_direction_input(GPIO_PORT_G, GPIO_PIN_0);
412
gpio_direction_input(GPIO_PORT_D, GPIO_PIN_1);
413
usleep(20);
414
415
//! HW BUG: Unlatch gpio buffer.
416
(void)gpio_read(GPIO_PORT_H, GPIO_PIN_6);
417
(void)gpio_read(GPIO_PORT_E, GPIO_PIN_6);
418
419
// Read H6/E6 which are shared with UART TX pins.
420
jc_r.detected = !gpio_read(GPIO_PORT_H, GPIO_PIN_6);
421
jc_l.detected = !gpio_read(GPIO_PORT_E, GPIO_PIN_6);
422
423
// Turn off Joy-Con detect. (UARTB/C TX).
424
PINMUX_AUX(PINMUX_AUX_UART2_TX) = 0;
425
PINMUX_AUX(PINMUX_AUX_UART3_TX) = 0;
426
gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_SPIO);
427
gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_SPIO);
428
usleep(20);
429
}
430
else
431
{
432
//! TODO: Is there a way to detect a broken Sio?
433
jc_l.detected = true;
434
}
435
}
436
437
static void _jc_conn_check()
438
{
439
_jc_detect();
440
441
if (jc_gamepad.sio_mode)
442
return;
443
444
// Check if a Joy-Con was disconnected.
445
if (!jc_l.detected)
446
{
447
if (jc_l.connected)
448
_jc_power_supply(UART_C, false);
449
450
jc_l.pkt_id = 0;
451
452
jc_l.connected = false;
453
jc_l.rumble_sent = false;
454
455
jc_gamepad.conn_l = false;
456
457
jc_gamepad.batt_info_l = 0;
458
jc_gamepad.bt_conn_l.type = 0;
459
jc_gamepad.buttons &= ~JC_BTN_MASK_L;
460
}
461
462
if (!jc_r.detected)
463
{
464
if (jc_r.connected)
465
_jc_power_supply(UART_B, false);
466
467
jc_r.pkt_id = 0;
468
469
jc_r.connected = false;
470
jc_r.rumble_sent = false;
471
472
jc_gamepad.conn_r = false;
473
474
jc_gamepad.batt_info_r = 0;
475
jc_gamepad.bt_conn_r.type = 0;
476
jc_gamepad.buttons &= ~JC_BTN_MASK_R;
477
}
478
}
479
480
static void _joycon_send_raw(u8 uart_port, const u8 *buf, u16 size)
481
{
482
uart_send(uart_port, buf, size);
483
uart_wait_xfer(uart_port, UART_TX_IDLE);
484
}
485
486
static u16 _jc_packet_add_uart_hdr(jc_wired_hdr_t *out, u8 wired_cmd, const u8 *data, u16 size, bool crc)
487
{
488
out->uart_hdr.magic[0] = 0x19;
489
out->uart_hdr.magic[1] = 0x01;
490
out->uart_hdr.magic[2] = 0x3;
491
492
out->uart_hdr.total_size_lsb = sizeof(jc_wired_hdr_t) - sizeof(jc_uart_hdr_t);
493
out->uart_hdr.total_size_msb = 0;
494
out->cmd = wired_cmd;
495
496
if (data)
497
memcpy(out->data, data, size);
498
499
out->crc = crc ? _jc_crc(&out->uart_hdr.total_size_msb,
500
sizeof(out->uart_hdr.total_size_msb) +
501
sizeof(out->cmd) + sizeof(out->data), 0) : 0;
502
503
return sizeof(jc_wired_hdr_t);
504
}
505
506
static u16 _jc_hid_output_rpt_craft(jc_wired_hdr_t *rpt, const u8 *payload, u16 size, bool crc)
507
{
508
u16 pkt_size = _jc_packet_add_uart_hdr(rpt, JC_WIRED_HID, NULL, 0, crc);
509
pkt_size += size;
510
511
rpt->uart_hdr.total_size_lsb += size;
512
rpt->data[0] = size >> 8;
513
rpt->data[1] = size & 0xFF;
514
515
if (payload)
516
memcpy(rpt->payload, payload, size);
517
518
return pkt_size;
519
}
520
521
static void _jc_send_hid_output_rpt(joycon_ctxt_t *jc, jc_hid_out_rpt_t *hid_pkt, u16 size, bool crc)
522
{
523
u8 rpt[0x50];
524
memset(rpt, 0, sizeof(rpt));
525
526
hid_pkt->pkt_id = (jc->pkt_id++ & 0xF);
527
u32 rpt_size = _jc_hid_output_rpt_craft((jc_wired_hdr_t *)rpt, (u8 *)hid_pkt, size, crc);
528
529
_joycon_send_raw(jc->uart, rpt, rpt_size);
530
}
531
532
static void _jc_send_hid_cmd(joycon_ctxt_t *jc, u8 subcmd, const u8 *data, u16 size)
533
{
534
static const u8 rumble_neutral[8] = { 0x00, 0x01, 0x40, 0x40, 0x00, 0x01, 0x40, 0x40 };
535
static const u8 rumble_init[8] = { 0xc2, 0xc8, 0x03, 0x72, 0xc2, 0xc8, 0x03, 0x72 };
536
537
u8 temp[0x30] = {0};
538
539
jc_hid_out_rpt_t *hid_pkt = (jc_hid_out_rpt_t *)temp;
540
memcpy(hid_pkt->rumble, rumble_neutral, sizeof(rumble_neutral));
541
542
if (subcmd == JC_HID_SUBCMD_SND_RUMBLE)
543
{
544
bool send_r_rumble = jc_r.connected && !jc_r.rumble_sent;
545
bool send_l_rumble = jc_l.connected && !jc_l.rumble_sent;
546
547
// Enable rumble.
548
hid_pkt->cmd = JC_HID_OUTPUT_RPT;
549
hid_pkt->subcmd = JC_HID_SUBCMD_RUMBLE_CTL;
550
hid_pkt->subcmd_data[0] = 1;
551
if (send_r_rumble)
552
_jc_send_hid_output_rpt(&jc_r, hid_pkt, 0x10, false);
553
if (send_l_rumble)
554
_jc_send_hid_output_rpt(&jc_l, hid_pkt, 0x10, false);
555
556
// Send rumble.
557
hid_pkt->cmd = JC_HID_RUMBLE_RPT;
558
memcpy(hid_pkt->rumble, rumble_init, sizeof(rumble_init));
559
if (send_r_rumble)
560
_jc_send_hid_output_rpt(&jc_r, hid_pkt, 10, false);
561
if (send_l_rumble)
562
_jc_send_hid_output_rpt(&jc_l, hid_pkt, 10, false);
563
564
msleep(15);
565
566
// Disable rumble.
567
hid_pkt->cmd = JC_HID_OUTPUT_RPT;
568
hid_pkt->subcmd = JC_HID_SUBCMD_RUMBLE_CTL;
569
hid_pkt->subcmd_data[0] = 0;
570
memcpy(hid_pkt->rumble, rumble_neutral, sizeof(rumble_neutral));
571
if (send_r_rumble)
572
_jc_send_hid_output_rpt(&jc_r, hid_pkt, 0x10, false);
573
if (send_l_rumble)
574
_jc_send_hid_output_rpt(&jc_l, hid_pkt, 0x10, false);
575
}
576
else
577
{
578
bool crc_needed = jc->type & JC_ID_HORI;
579
580
hid_pkt->cmd = JC_HID_OUTPUT_RPT;
581
hid_pkt->subcmd = subcmd;
582
if (data)
583
memcpy(hid_pkt->subcmd_data, data, size);
584
585
_jc_send_hid_output_rpt(jc, hid_pkt, sizeof(jc_hid_out_rpt_t) + size, crc_needed);
586
}
587
}
588
589
static void _jc_charging_decider(u8 batt, u8 uart)
590
{
591
u32 system_batt_enough = max17050_get_cached_batt_volt() > 4000;
592
593
// Power supply control based on battery levels and charging.
594
if ((batt >> 1) < JC_BATT_LOW) // Level without checking charging.
595
_jc_power_supply(uart, true);
596
else if (batt > (system_batt_enough ? JC_BATT_FULL : JC_BATT_MID) << 1) // Addresses the charging bit.
597
_jc_power_supply(uart, false);
598
}
599
600
static void _jc_parse_wired_hid(joycon_ctxt_t *jc, const u8 *packet, int size)
601
{
602
u32 btn_tmp;
603
jc_hid_in_rpt_t *hid_pkt = (jc_hid_in_rpt_t *)packet;
604
605
switch (hid_pkt->cmd)
606
{
607
case JC_HORI_INPUT_RPT:
608
if (!(jc->type & JC_ID_HORI))
609
return;
610
611
case JC_HID_INPUT_RPT:
612
// Discard incomplete hid packets.
613
if (size < 12)
614
break;
615
616
btn_tmp = hid_pkt->btn_right | hid_pkt->btn_shared << 8 | hid_pkt->btn_left << 16;
617
618
if (jc->type & JC_ID_L)
619
{
620
jc_gamepad.buttons &= ~JC_BTN_MASK_L;
621
jc_gamepad.buttons |= (btn_tmp & JC_BTN_MASK_L);
622
623
jc_gamepad.lstick_x = hid_pkt->stick_h_left | ((hid_pkt->stick_m_left & 0xF) << 8);
624
jc_gamepad.lstick_y = (hid_pkt->stick_m_left >> 4) | (hid_pkt->stick_v_left << 4);
625
626
jc_gamepad.batt_info_l = hid_pkt->batt_info;
627
}
628
else if (jc->type & JC_ID_R)
629
{
630
jc_gamepad.buttons &= ~JC_BTN_MASK_R;
631
jc_gamepad.buttons |= (btn_tmp & JC_BTN_MASK_R);
632
633
jc_gamepad.rstick_x = hid_pkt->stick_h_right | ((hid_pkt->stick_m_right & 0xF) << 8);
634
jc_gamepad.rstick_y = (hid_pkt->stick_m_right >> 4) | (hid_pkt->stick_v_right << 4);
635
636
jc_gamepad.batt_info_r = hid_pkt->batt_info;
637
}
638
639
jc_gamepad.conn_l = jc_l.connected;
640
jc_gamepad.conn_r = jc_r.connected;
641
642
if (hid_pkt->cmd == JC_HID_INPUT_RPT)
643
_jc_charging_decider(hid_pkt->batt_info, jc->uart);
644
break;
645
case JC_HID_SUBMCD_RPT:
646
if (hid_pkt->subcmd == JC_HID_SUBCMD_SPI_READ)
647
{
648
jc_bt_conn_t *bt_conn;
649
650
if (jc->type & JC_ID_L)
651
bt_conn = &jc_gamepad.bt_conn_l;
652
else
653
bt_conn = &jc_gamepad.bt_conn_r;
654
655
jc_hid_in_spi_read_t *spi_info = (jc_hid_in_spi_read_t *)hid_pkt->subcmd_data;
656
jc_hid_in_pair_data_t *pair_data = (jc_hid_in_pair_data_t *)spi_info->data;
657
658
// Check if the reply is pairing info.
659
if (spi_info->size == 0x1A && pair_data->magic == 0x95 && pair_data->size == 0x22)
660
{
661
bt_conn->type = jc->type;
662
663
memcpy(bt_conn->mac, jc->mac, 6);
664
memcpy(bt_conn->host_mac, pair_data->mac, 6);
665
for (u32 i = 16; i > 0; i--)
666
bt_conn->ltk[16 - i] = pair_data->ltk[i - 1];
667
}
668
}
669
break;
670
default:
671
break;
672
}
673
}
674
675
static void _jc_parse_wired_init(joycon_ctxt_t *jc, const u8 *data, int size)
676
{
677
// Discard empty packets.
678
if (size <= 0)
679
return;
680
681
switch (data[0])
682
{
683
case JC_WIRED_CMD_GET_INFO:
684
for (int i = 12; i > 6; i--)
685
jc->mac[12 - i] = data[i];
686
jc->type = data[6];
687
jc->connected = true;
688
break;
689
case JC_WIRED_CMD_SET_BRATE:
690
jc->state = JC_STATE_BRATE_CHANGED;
691
break;
692
case JC_WIRED_CMD_HID_DISC:
693
jc->state = JC_STATE_BRATE_OK;
694
break;
695
case JC_WIRED_CMD_HID_CONN:
696
case JC_WIRED_CMD_SET_HIDRATE:
697
// done.
698
default:
699
break;
700
}
701
}
702
703
static void _jc_uart_pkt_parse(joycon_ctxt_t *jc, const jc_wired_hdr_t *pkt, int size)
704
{
705
switch (pkt->cmd)
706
{
707
case JC_HORI_INPUT_RPT_CMD:
708
case JC_WIRED_HID:
709
_jc_parse_wired_hid(jc, pkt->payload, size - sizeof(jc_wired_hdr_t));
710
break;
711
case JC_WIRED_INIT_REPLY:
712
_jc_parse_wired_init(jc, pkt->data, size - sizeof(jc_uart_hdr_t) - 1);
713
break;
714
case JC_INIT_HANDSHAKE:
715
jc->state = JC_STATE_HANDSHAKED;
716
break;
717
default:
718
break;
719
}
720
721
jc->last_received_time = get_tmr_ms();
722
}
723
724
static void _jc_sio_parse_payload(joycon_ctxt_t *jc, u8 cmd, const u8 *payload, int size)
725
{
726
switch (cmd)
727
{
728
case JC_SIO_CMD_STATUS:
729
// Discard incomplete packets.
730
if (size < 12)
731
break;
732
733
jc_sio_hid_in_rpt_t *hid_pkt = (jc_sio_hid_in_rpt_t *)payload;
734
jc_gamepad.buttons = hid_pkt->btn_right | hid_pkt->btn_shared << 8 | hid_pkt->btn_left << 16;
735
jc_gamepad.home = !gpio_read(GPIO_PORT_V, GPIO_PIN_3);
736
737
jc_gamepad.lstick_x = hid_pkt->stick_h_left | ((hid_pkt->stick_m_left & 0xF) << 8);
738
jc_gamepad.lstick_y = (hid_pkt->stick_m_left >> 4) | (hid_pkt->stick_v_left << 4);
739
jc_gamepad.rstick_x = hid_pkt->stick_h_right | ((hid_pkt->stick_m_right & 0xF) << 8);
740
jc_gamepad.rstick_y = (hid_pkt->stick_m_right >> 4) | (hid_pkt->stick_v_right << 4);
741
742
jc_gamepad.batt_info_l = jc_l.connected;
743
jc_gamepad.batt_info_r = gpio_read(GPIO_PORT_E, GPIO_PIN_7); // Set IRQ status.
744
745
jc_gamepad.conn_l = jc_l.connected;
746
jc_gamepad.conn_r = jc_l.connected;
747
break;
748
default:
749
break;
750
}
751
}
752
753
static void _jc_sio_uart_pkt_parse(joycon_ctxt_t *jc, const jc_sio_in_rpt_t *pkt, int size)
754
{
755
if (pkt->crc_hdr != _jc_crc((u8 *)pkt, sizeof(jc_sio_in_rpt_t) - 1, 0))
756
return;
757
758
u8 cmd = pkt->ack & (~JC_SIO_CMD_ACK);
759
switch (cmd)
760
{
761
case JC_SIO_CMD_INIT:
762
jc->connected = pkt->status == 0;
763
break;
764
case JC_SIO_CMD_VER_RPT:
765
if (jc->connected)
766
jc->connected = pkt->status == 0;
767
break;
768
case JC_SIO_CMD_IAP_VER:
769
case JC_SIO_CMD_STATUS:
770
_jc_sio_parse_payload(jc, cmd, pkt->payload, size - sizeof(jc_sio_in_rpt_t));
771
break;
772
case JC_SIO_CMD_UNK02:
773
case JC_SIO_CMD_UNK20:
774
case JC_SIO_CMD_UNK21:
775
case JC_SIO_CMD_UNK22:
776
case JC_SIO_CMD_UNK40:
777
default:
778
break;
779
}
780
781
jc->last_received_time = get_tmr_ms();
782
}
783
784
static void _jc_rcv_pkt(joycon_ctxt_t *jc)
785
{
786
if (!jc->detected)
787
return;
788
789
u32 len = uart_recv(jc->uart, (u8 *)jc->buf, 0x100);
790
if (len < 8)
791
return;
792
793
// For Joycon, check uart reply magic.
794
jc_wired_hdr_t *jc_pkt = (jc_wired_hdr_t *)jc->buf;
795
if (!jc->sio_mode && !memcmp(jc_pkt->uart_hdr.magic, "\x19\x81\x03", 3))
796
{
797
_jc_uart_pkt_parse(jc, jc_pkt, len);
798
799
return;
800
}
801
802
// For Sio, check uart output report and command ack.
803
jc_sio_in_rpt_t *sio_pkt = (jc_sio_in_rpt_t *)(jc->buf);
804
if (jc->sio_mode && sio_pkt->cmd == JC_SIO_INPUT_RPT && (sio_pkt->ack & JC_SIO_CMD_ACK) == JC_SIO_CMD_ACK)
805
{
806
_jc_sio_uart_pkt_parse(jc, sio_pkt, len);
807
808
return;
809
}
810
}
811
812
static bool _jc_send_init_rumble(joycon_ctxt_t *jc)
813
{
814
// Send init rumble or request nx pad status report.
815
if ((jc_r.connected && !jc_r.rumble_sent) || (jc_l.connected && !jc_l.rumble_sent))
816
{
817
_jc_send_hid_cmd(jc, JC_HID_SUBCMD_SND_RUMBLE, NULL, 0);
818
819
if (jc_l.connected)
820
jc_l.rumble_sent = true;
821
if (jc_r.connected)
822
jc_r.rumble_sent = true;
823
824
return 1;
825
}
826
827
return 0;
828
}
829
830
static void _jc_req_nx_pad_status(joycon_ctxt_t *jc)
831
{
832
if (!jc->detected)
833
return;
834
835
bool is_nxpad = !(jc->type & JC_ID_HORI) && !jc->sio_mode;
836
837
if (jc->last_status_req_time > get_tmr_ms() || !jc->connected)
838
return;
839
840
if (is_nxpad)
841
{
842
bool sent_rumble = _jc_send_init_rumble(jc);
843
844
if (sent_rumble)
845
return;
846
}
847
848
if (is_nxpad)
849
_joycon_send_raw(jc->uart, nx_pad_status, sizeof(nx_pad_status));
850
else if (jc->sio_mode)
851
_joycon_send_raw(jc->uart, sio_pad_status, sizeof(sio_pad_status));
852
else
853
_joycon_send_raw(jc->uart, hori_pad_status, sizeof(hori_pad_status));
854
855
jc->last_status_req_time = get_tmr_ms() + (!jc->sio_mode ? 15 : 7);
856
}
857
858
static bool _jc_validate_pairing_info(const u8 *buf, bool *is_hos)
859
{
860
u8 crc = 0;
861
for (u32 i = 0; i < 0x22; i++)
862
crc += buf[4 + i];
863
864
crc += 0x68; // Host is Switch.
865
866
if ((crc ^ 0x55) == buf[2])
867
*is_hos = true;
868
869
crc -= 0x68;
870
crc += 0x08; // Host is PC.
871
872
if (*is_hos || (crc ^ 0x55) == buf[2])
873
return true;
874
875
return false;
876
}
877
878
jc_gamepad_rpt_t *jc_get_bt_pairing_info(bool *is_l_hos, bool *is_r_hos)
879
{
880
u8 retries;
881
jc_bt_conn_t *bt_conn;
882
883
if (!jc_init_done || jc_gamepad.sio_mode)
884
return NULL;
885
886
bt_conn = &jc_gamepad.bt_conn_l;
887
memset(bt_conn->host_mac, 0, 6);
888
memset(bt_conn->ltk, 0, 16);
889
890
bt_conn = &jc_gamepad.bt_conn_r;
891
memset(bt_conn->host_mac, 0, 6);
892
memset(bt_conn->ltk, 0, 16);
893
894
_jc_conn_check();
895
896
while (jc_l.last_status_req_time > get_tmr_ms())
897
{
898
_jc_rcv_pkt(&jc_r);
899
_jc_rcv_pkt(&jc_l);
900
}
901
902
jc_hid_in_spi_read_t subcmd_data_l;
903
subcmd_data_l.addr = 0x2000;
904
subcmd_data_l.size = 0x1A;
905
906
jc_hid_in_spi_read_t subcmd_data_r;
907
subcmd_data_r.addr = 0x2000;
908
subcmd_data_r.size = 0x1A;
909
910
bool jc_r_found = jc_r.connected ? false : true;
911
bool jc_l_found = jc_l.connected ? false : true;
912
913
// Set mode to HW controlled RTS.
914
uart_set_mode(jc_l.uart, UART_AO_TX_HW_RX);
915
uart_set_mode(jc_r.uart, UART_AO_TX_HW_RX);
916
917
u32 total_retries = 10;
918
retry:
919
retries = 10;
920
while (retries)
921
{
922
u32 time_now = get_tmr_ms();
923
if ((!jc_l_found && jc_l.last_status_req_time < time_now) || (!jc_r_found && jc_r.last_status_req_time < time_now))
924
{
925
if (!jc_l_found)
926
{
927
_jc_send_hid_cmd(&jc_l, JC_HID_SUBCMD_SPI_READ, (u8 *)&subcmd_data_l, 5);
928
jc_l.last_status_req_time = get_tmr_ms() + 15;
929
}
930
931
if (!jc_r_found)
932
{
933
_jc_send_hid_cmd(&jc_r, JC_HID_SUBCMD_SPI_READ, (u8 *)&subcmd_data_r, 5);
934
jc_r.last_status_req_time = get_tmr_ms() + 15;
935
}
936
937
retries--;
938
}
939
940
// Wait for the first 36 bytes to arrive.
941
msleep(5);
942
943
if (!jc_l_found)
944
{
945
memset(jc_l.buf, 0, 0x100);
946
_jc_rcv_pkt(&jc_l);
947
948
bool is_hos = false;
949
if (_jc_validate_pairing_info(&jc_l.buf[SPI_READ_OFFSET], &is_hos))
950
{
951
bool is_active = jc_l.buf[SPI_READ_OFFSET] == 0x95;
952
953
if (!is_active)
954
subcmd_data_l.addr += 0x26; // Get next slot.
955
else
956
jc_l_found = true; // Entry is active.
957
958
if (jc_l_found && is_hos)
959
*is_l_hos = true;
960
}
961
}
962
963
if (!jc_r_found)
964
{
965
memset(jc_r.buf, 0, 0x100);
966
_jc_rcv_pkt(&jc_r);
967
968
bool is_hos = false;
969
if (_jc_validate_pairing_info(&jc_r.buf[SPI_READ_OFFSET], &is_hos))
970
{
971
bool is_active = jc_r.buf[SPI_READ_OFFSET] == 0x95;
972
973
if (!is_active)
974
subcmd_data_r.addr += 0x26; // Get next slot.
975
else
976
jc_r_found = true; // Entry is active.
977
978
if (jc_r_found && is_hos)
979
*is_r_hos = true;
980
}
981
}
982
983
if (jc_l_found && jc_r_found)
984
break;
985
}
986
987
if (!jc_l_found || !jc_r_found)
988
{
989
if (total_retries)
990
{
991
total_retries--;
992
goto retry;
993
}
994
995
if (!jc_l_found)
996
{
997
bt_conn = &jc_gamepad.bt_conn_l;
998
memset(bt_conn->host_mac, 0, 6);
999
memset(bt_conn->ltk, 0, 16);
1000
}
1001
1002
if (!jc_r_found)
1003
{
1004
bt_conn = &jc_gamepad.bt_conn_r;
1005
memset(bt_conn->host_mac, 0, 6);
1006
memset(bt_conn->ltk, 0, 16);
1007
}
1008
}
1009
1010
// Restore mode to manual RTS.
1011
uart_set_mode(jc_l.uart, UART_AO_TX_MN_RX);
1012
uart_set_mode(jc_r.uart, UART_AO_TX_MN_RX);
1013
1014
return &jc_gamepad;
1015
}
1016
1017
static void _jc_init_conn(joycon_ctxt_t *jc)
1018
{
1019
if (!jc->detected)
1020
return;
1021
1022
if (((u32)get_tmr_ms() - jc->last_received_time) > 1000)
1023
{
1024
_jc_power_supply(jc->uart, true);
1025
1026
// Mask out buttons and set connected to false.
1027
if (jc->uart == UART_B)
1028
{
1029
jc_gamepad.buttons &= ~JC_BTN_MASK_R;
1030
jc_gamepad.conn_r = false;
1031
}
1032
else
1033
{
1034
jc_gamepad.buttons &= ~JC_BTN_MASK_L;
1035
jc_gamepad.conn_l = false;
1036
}
1037
1038
// Initialize uart to 1 megabaud and manual RTS.
1039
uart_init(jc->uart, 1000000, UART_AO_TX_MN_RX);
1040
1041
if (!jc->sio_mode)
1042
{
1043
jc->state = JC_STATE_START;
1044
1045
// Set TX and RTS inversion for Joycon.
1046
uart_invert(jc->uart, true, UART_INVERT_TXD | UART_INVERT_RTS);
1047
1048
// Wake up the controller.
1049
_joycon_send_raw(jc->uart, init_wake, sizeof(init_wake));
1050
_jc_rcv_pkt(jc); // Clear RX FIFO.
1051
1052
// Do a handshake.
1053
u32 retries = 10;
1054
while (retries && jc->state != JC_STATE_HANDSHAKED)
1055
{
1056
_joycon_send_raw(jc->uart, init_handshake, sizeof(init_handshake));
1057
msleep(5);
1058
_jc_rcv_pkt(jc);
1059
retries--;
1060
}
1061
1062
if (jc->state != JC_STATE_HANDSHAKED)
1063
goto out;
1064
1065
// Get info about the controller.
1066
_joycon_send_raw(jc->uart, init_get_info, sizeof(init_get_info));
1067
msleep(2);
1068
_jc_rcv_pkt(jc);
1069
1070
if (!(jc->type & JC_ID_HORI))
1071
{
1072
// Request 3 megabaud change.
1073
_joycon_send_raw(jc->uart, init_switch_brate, sizeof(init_switch_brate));
1074
msleep(2);
1075
_jc_rcv_pkt(jc);
1076
1077
if (jc->state == JC_STATE_BRATE_CHANGED)
1078
{
1079
// Reinitialize uart to 3 megabaud and manual RTS.
1080
uart_init(jc->uart, 3000000, UART_AO_TX_MN_RX);
1081
uart_invert(jc->uart, true, UART_INVERT_TXD | UART_INVERT_RTS);
1082
1083
// Disconnect HID.
1084
retries = 10;
1085
while (retries && jc->state != JC_STATE_BRATE_OK)
1086
{
1087
_joycon_send_raw(jc->uart, init_hid_disconnect, sizeof(init_hid_disconnect));
1088
msleep(5);
1089
_jc_rcv_pkt(jc);
1090
retries--;
1091
}
1092
1093
if (jc->state != JC_STATE_BRATE_OK)
1094
goto out;
1095
}
1096
1097
// Create HID connection with the new rate.
1098
_joycon_send_raw(jc->uart, init_hid_connect, sizeof(init_hid_connect));
1099
msleep(2);
1100
_jc_rcv_pkt(jc);
1101
1102
// Set hid packet rate.
1103
_joycon_send_raw(jc->uart, init_set_hid_rate, sizeof(init_set_hid_rate));
1104
msleep(2);
1105
_jc_rcv_pkt(jc);
1106
}
1107
else // Hori. Unset RTS inversion.
1108
uart_invert(jc->uart, false, UART_INVERT_RTS);
1109
}
1110
else
1111
{
1112
// Set Sio NPOR low to configure BOOT0 mode.
1113
gpio_write(GPIO_PORT_CC, GPIO_PIN_5, GPIO_LOW);
1114
usleep(300);
1115
gpio_write(GPIO_PORT_T, GPIO_PIN_0, GPIO_LOW);
1116
gpio_write(GPIO_PORT_CC, GPIO_PIN_5, GPIO_HIGH);
1117
msleep(100);
1118
1119
// Clear RX FIFO.
1120
_jc_rcv_pkt(jc);
1121
1122
// Initialize the controller.
1123
u32 retries = 10;
1124
while (!jc->connected && retries)
1125
{
1126
_joycon_send_raw(jc->uart, sio_init, sizeof(sio_init));
1127
msleep(5);
1128
_jc_rcv_pkt(jc);
1129
retries--;
1130
}
1131
1132
if (!jc->connected)
1133
goto out;
1134
1135
// Set output report version.
1136
_joycon_send_raw(jc->uart, sio_set_rpt_version, sizeof(sio_set_rpt_version));
1137
msleep(5);
1138
_jc_rcv_pkt(jc);
1139
}
1140
1141
// Initialization done.
1142
jc->state = JC_STATE_INIT_DONE;
1143
1144
out:
1145
jc->last_received_time = get_tmr_ms();
1146
1147
if (!jc->sio_mode && jc->connected && !(jc->type & JC_ID_HORI))
1148
_jc_power_supply(jc->uart, false);
1149
}
1150
}
1151
1152
void jc_init_hw()
1153
{
1154
jc_l.uart = UART_C;
1155
jc_r.uart = UART_B;
1156
1157
jc_l.sio_mode = fuse_read_hw_type() == FUSE_NX_HW_TYPE_HOAG;
1158
jc_gamepad.sio_mode = jc_l.sio_mode;
1159
1160
#if !defined(DEBUG_UART_PORT) || !(DEBUG_UART_PORT)
1161
_jc_power_supply(UART_C, true);
1162
_jc_power_supply(UART_B, true);
1163
1164
// Sio Initialization.
1165
if (jc_gamepad.sio_mode)
1166
{
1167
// Enable 4 MHz clock to Sio.
1168
clock_enable_extperiph2();
1169
PINMUX_AUX(PINMUX_AUX_TOUCH_CLK) = PINMUX_PULL_DOWN;
1170
1171
// Configure Sio HOME BUTTON.
1172
PINMUX_AUX(PINMUX_AUX_LCD_GPIO1) = PINMUX_INPUT_ENABLE | PINMUX_TRISTATE | PINMUX_PULL_UP | 1;
1173
gpio_direction_input(GPIO_PORT_V, GPIO_PIN_3);
1174
1175
// Configure Sio IRQ
1176
PINMUX_AUX(PINMUX_AUX_GPIO_PE7) = PINMUX_INPUT_ENABLE | PINMUX_TRISTATE | PINMUX_PULL_UP;
1177
gpio_direction_input(GPIO_PORT_E, GPIO_PIN_7);
1178
1179
// Configure Sio NRST and BOOT0.
1180
PINMUX_AUX(PINMUX_AUX_CAM1_STROBE) = PINMUX_PULL_DOWN | 1;
1181
PINMUX_AUX(PINMUX_AUX_CAM2_PWDN) = PINMUX_PULL_DOWN | 1;
1182
1183
// Set BOOT0 to flash mode. (output high is sram mode).
1184
gpio_direction_output(GPIO_PORT_T, GPIO_PIN_0, GPIO_LOW);
1185
1186
// NRST to pull down.
1187
gpio_direction_input(GPIO_PORT_T, GPIO_PIN_1);
1188
1189
// Configure Sio NPOR.
1190
PINMUX_AUX(PINMUX_AUX_USB_VBUS_EN1) = PINMUX_IO_HV | PINMUX_LPDR | 1;
1191
gpio_direction_output(GPIO_PORT_CC, GPIO_PIN_5, GPIO_LOW);
1192
}
1193
1194
#if 0 // Already set by hw init.
1195
// Set Joy-Con IsAttached pinmux. Shared with UARTB/UARTC TX.
1196
PINMUX_AUX(PINMUX_AUX_GPIO_PE6) = PINMUX_INPUT_ENABLE | PINMUX_TRISTATE;
1197
PINMUX_AUX(PINMUX_AUX_GPIO_PH6) = PINMUX_INPUT_ENABLE | PINMUX_TRISTATE;
1198
1199
// Set Joy-Con IsAttached mode. Shared with UARTB/UARTC TX.
1200
gpio_config(GPIO_PORT_E, GPIO_PIN_6, GPIO_MODE_GPIO);
1201
gpio_config(GPIO_PORT_H, GPIO_PIN_6, GPIO_MODE_GPIO);
1202
#endif
1203
1204
// Configure pinmuxing for UART B and C.
1205
if (!jc_gamepad.sio_mode)
1206
pinmux_config_uart(UART_B);
1207
pinmux_config_uart(UART_C);
1208
1209
// Enable UART B and C clocks.
1210
if (!jc_gamepad.sio_mode)
1211
clock_enable_uart(UART_B);
1212
clock_enable_uart(UART_C);
1213
1214
jc_init_done = true;
1215
#endif
1216
}
1217
1218
void jc_deinit()
1219
{
1220
if (!jc_init_done)
1221
return;
1222
1223
// Disable power.
1224
_jc_power_supply(UART_B, false);
1225
_jc_power_supply(UART_C, false);
1226
1227
if (!jc_gamepad.sio_mode)
1228
{
1229
// Send sleep command.
1230
u8 data = HCI_STATE_SLEEP;
1231
if (jc_r.connected && !(jc_r.type & JC_ID_HORI))
1232
{
1233
_jc_send_hid_cmd(&jc_r, JC_HID_SUBCMD_HCI_STATE, &data, 1);
1234
_jc_rcv_pkt(&jc_r);
1235
}
1236
if (jc_l.connected && !(jc_l.type & JC_ID_HORI))
1237
{
1238
_jc_send_hid_cmd(&jc_l, JC_HID_SUBCMD_HCI_STATE, &data, 1);
1239
_jc_rcv_pkt(&jc_l);
1240
}
1241
}
1242
else
1243
{
1244
// Disable Sio NPOR.
1245
gpio_write(GPIO_PORT_CC, GPIO_PIN_5, GPIO_LOW);
1246
1247
// Disable 4 MHz clock to Sio.
1248
clock_disable_extperiph2();
1249
}
1250
1251
// Disable UART B and C clocks.
1252
if (!jc_gamepad.sio_mode)
1253
clock_disable_uart(UART_B);
1254
clock_disable_uart(UART_C);
1255
}
1256
1257
jc_gamepad_rpt_t *joycon_poll()
1258
{
1259
if (!jc_init_done)
1260
return NULL;
1261
1262
_jc_conn_check();
1263
1264
_jc_init_conn(&jc_r);
1265
_jc_init_conn(&jc_l);
1266
1267
_jc_req_nx_pad_status(&jc_r);
1268
_jc_req_nx_pad_status(&jc_l);
1269
1270
_jc_rcv_pkt(&jc_r);
1271
_jc_rcv_pkt(&jc_l);
1272
1273
return &jc_gamepad;
1274
}
1275
1276