Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
CTCaer
GitHub Repository: CTCaer/hekate
Path: blob/master/bdk/input/joycon.c
3694 views
1
/*
2
* Joy-Con UART driver for Nintendo Switch
3
*
4
* Copyright (c) 2019-2026 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_SND_MAGIC "\x19\x01\x03" // Type, Destination, Group.
37
#define JC_WIRED_RCV_MAGIC "\x19\x81\x03" // Type, Destination, Group.
38
39
#define JC_WIRED_RPT_OUT 0x43
40
#define JC_WIRED_RPT_IN 0x53
41
#define JC_WIRED_CMD 0x91
42
#define JC_WIRED_HID 0x92
43
#define JC_WIRED_ECHO 0x93 // CMD OUT.
44
#define JC_WIRED_INIT_REPLY 0x94
45
#define JC_WIRED_HANDSHAKE 0xA5 // Enable Wired CMDs (JC_WIRED_CMD).
46
47
#define JC_HORI_INPUT_RPT_CMD 0x9A
48
#define JC_HORI_INPUT_RPT 0x00
49
50
#define JC_WIRED_CMD_GET_INFO 0x01
51
#define JC_WIRED_CMD_SET_CHARGER 0x02
52
#define JC_WIRED_CMD_GET_CHARGER 0x03
53
#define JC_WIRED_CMD_SET_ATTACH 0x04
54
#define JC_WIRED_CMD_GET_ATTACH 0x05
55
#define JC_WIRED_CMD_BATT_VOLT 0x06
56
#define JC_WIRED_CMD_WAKE_REASON 0x07
57
#define JC_WIRED_CMD_HID_CONN 0x10 // Enable HID CMDs (JC_WIRED_HID).
58
#define JC_WIRED_CMD_HID_DISC 0x11
59
#define JC_WIRED_CMD_SET_HIDRATE 0x12 // Output report rate.
60
#define JC_WIRED_CMD_GET_HIDRATE 0x13
61
#define JC_WIRED_CMD_SET_PAIRING 0x18 // Manual pairing.
62
#define JC_WIRED_CMD_SET_BRATE 0x20
63
#define JC_WIRED_CMD_ECHO_TEST 0x40
64
65
#define JC_HID_OUTPUT_RPT 0x01
66
#define JC_HID_RUMBLE_RPT 0x10
67
68
#define JC_HID_INPUT_RPT 0x30
69
#define JC_HID_SUBMCD_RPT 0x21
70
71
#define JC_HID_SUBCMD_HCI_STATE 0x06
72
#define HCI_STATE_SLEEP 0x00
73
#define HCI_STATE_RECONNECT 0x01
74
#define HCI_STATE_PAIR 0x02
75
#define HCI_STATE_HOME 0x04
76
#define JC_HID_SUBCMD_SPI_READ 0x10
77
#define SPI_READ_OFFSET 0x20
78
#define JC_HID_SUBCMD_RUMBLE_CTL 0x48
79
#define JC_HID_SUBCMD_CHARGE_SET 0x51
80
#define JC_HID_SUBCMD_SND_RUMBLE 0xFF // Custom.
81
82
#define JC_SIO_OUTPUT_RPT 0x91
83
#define JC_SIO_INPUT_RPT 0x92
84
#define JC_SIO_CMD_ACK 0x80
85
86
#define JC_SIO_CMD_INIT 0x01
87
#define JC_SIO_CMD_UNK02 0x02
88
#define JC_SIO_CMD_VER_RPT 0x03
89
#define JC_SIO_CMD_UNK20 0x20 // JC_WIRED_CMD_SET_BRATE
90
#define JC_SIO_CMD_UNK21 0x21
91
#define JC_SIO_CMD_UNK22 0x22
92
#define JC_SIO_CMD_UNK40 0x40
93
#define JC_SIO_CMD_STATUS 0x41
94
#define JC_SIO_CMD_IAP_VER 0x42
95
96
97
#define JC_BTN_MASK_L 0xFF2900 // 0xFFE900: with charge status.
98
#define JC_BTN_MASK_R 0x0056FF
99
100
#define JC_ID_L 0x01 // Joycon (L). Mask for Hori (L).
101
#define JC_ID_R 0x02 // Joycon (R). Mask for Hori (R).
102
#define JC_ID_HORI 0x20 // Mask for Hori. Actual ids: 0x21, 0x22.
103
104
#define JC_CRC8_POLY 0x8D
105
106
enum
107
{
108
JC_STATE_START = 0,
109
JC_STATE_HANDSHAKED = 1,
110
JC_STATE_INFO_PARSED = 2,
111
JC_STATE_BRATE_CHANGED = 3,
112
JC_STATE_HID_NO_CONN = 4,
113
JC_STATE_HID_CONN = 5,
114
JC_STATE_INIT_DONE = 6
115
};
116
117
enum
118
{
119
JC_BATT_EMPTY = 0,
120
JC_BATT_CRIT = 1, // 3300 - 3599 mV.
121
JC_BATT_LOW = 2, // 3600 - 3759 mV.
122
JC_BATT_MID = 3, // 3760 - 3899 mV.
123
JC_BATT_FULL = 4 // 3900 - 4200 mV.
124
};
125
126
#define JC_CHRG_CFG_SUPL0 0x00 // 100 OFF.
127
#define JC_CHRG_CFG_SUPL1 0x10 // EN2 OFF.
128
#define JC_CHRG_CFG_100MA 0x04 // 100 ON.
129
#define JC_CHRG_CFG_200MA 0x14 // EN2 ON.
130
131
enum
132
{
133
JC_CHRG_STATE_INIT = 0,
134
JC_CHRG_STATE_OFF = 1, // Regulator OFF.
135
JC_CHRG_STATE_SUPL = 2, // JC_CHRG_CFG_SUPL0.
136
JC_CHRG_STATE_SLOW = 3, // JC_CHRG_CFG_100MA.
137
JC_CHRG_STATE_FAST = 4, // JC_CHRG_CFG_200MA.
138
};
139
140
static const u8 _sio_init[] = {
141
JC_SIO_OUTPUT_RPT, JC_SIO_CMD_INIT,
142
0x00, 0x00, 0x00, 0x00, 0x00, 0x95
143
};
144
145
static const u8 _sio_set_rpt_version[] = {
146
JC_SIO_OUTPUT_RPT, JC_SIO_CMD_VER_RPT,
147
// old fw: 0x00, 0x0D (0.13). New 3.4.
148
// force_update_en: 0x01
149
0x00, 0x00, 0x03, 0x04, 0x00, 0xDA
150
};
151
152
// Every 8ms.
153
static const u8 _sio_pad_status[] = {
154
JC_SIO_OUTPUT_RPT, JC_SIO_CMD_STATUS,
155
0x00, 0x00, 0x00, 0x00, 0x00, 0xB0
156
};
157
158
static const u8 _jc_init_wake[] = {
159
0xA1, 0xA2, 0xA3, 0xA4
160
};
161
162
static const u8 _jc_init_handshake[] = {
163
0x19, 0x01, 0x03, 0x07, 0x00, // Uart header.
164
JC_WIRED_HANDSHAKE, 0x02, // Cmd and Replay echo.
165
0x01, 0x7E, 0x00, 0x00, 0x00 // Handshake magic.
166
};
167
168
static const u8 _jc_init_get_info[] = {
169
0x19, 0x01, 0x03, 0x07, 0x00, // Uart header.
170
JC_WIRED_CMD, JC_WIRED_CMD_GET_INFO, // Cmd and subcmd.
171
0x00, 0x00, 0x00, 0x00, 0x24 // Hdr crc.
172
};
173
174
static const u8 _jc_init_switch_brate[] = {
175
0x19, 0x01, 0x03, 0x0F, 0x00, // Uart header.
176
JC_WIRED_CMD, JC_WIRED_CMD_SET_BRATE, // Cmd and subcmd.
177
0x08, 0x00, 0x00, 0xBD, 0xB1, // Subcmd data size, data crc and hdr crc.
178
// Baudrate 3 megabaud.
179
0xC0, 0xC6, 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00
180
};
181
182
static const u8 _jc_init_hid_disconnect[] = {
183
0x19, 0x01, 0x03, 0x07, 0x00, // Uart header.
184
JC_WIRED_CMD, JC_WIRED_CMD_HID_DISC, // Cmd and subcmd.
185
0x00, 0x00, 0x00, 0x00, 0x0E // Hdr crc.
186
};
187
188
static const u8 _jc_init_set_hid_rate[] = {
189
0x19, 0x01, 0x03, 0x0B, 0x00, // Uart header.
190
JC_WIRED_CMD, JC_WIRED_CMD_SET_HIDRATE, // Cmd and subcmd.
191
0x04, 0x00, 0x00, 0x12, 0xA6, // Subcmd data size, data crc and hdr crc.
192
// Output report rate 15 ms. (5/10/15 supported).
193
0x0F, 0x00, 0x00, 0x00
194
195
// 5 ms.
196
// 0x04, 0x00, 0x00, 0x0E, 0xD5,
197
// 0x05, 0x00, 0x00, 0x00
198
};
199
200
static const u8 _jc_init_hid_connect[] = {
201
0x19, 0x01, 0x03, 0x07, 0x00, // Uart header.
202
JC_WIRED_CMD, JC_WIRED_CMD_HID_CONN, // Cmd and subcmd.
203
0x00, 0x00, 0x00, 0x00, 0x3D // Hdr crc.
204
};
205
206
static const u8 _jc_nx_pad_status[] = {
207
0x19, 0x01, 0x03, 0x08, 0x00, // Uart header.
208
JC_WIRED_HID, 0x00, // Cmd.
209
0x01, 0x00, 0x00, 0x69, 0x2D, // Subcmd data size, data crc and hdr crc.
210
// Contents do not matter.
211
0x1F
212
};
213
214
static const u8 _jc_hori_pad_status[] = {
215
0x19, 0x01, 0x03, 0x07, 0x00, // Uart header.
216
JC_HORI_INPUT_RPT_CMD, 0x01, // Hori cmd and hori subcmd.
217
0x00, 0x00, 0x00, 0x00, 0x48 // Hdr crc.
218
};
219
220
// code: Channel code. vibc: Vibration code.
221
typedef struct
222
{
223
u32 rsvd :20;
224
u32 code_hi:5;
225
u32 code_lo:5;
226
u32 fmt :2; // Must be 1.
227
} jc_rumble_fmt1_t;
228
229
typedef struct
230
{
231
u32 rsvd :2;
232
u32 freq_hi:7;
233
u32 amp_hi :7;
234
u32 freq_lo:7;
235
u32 amp_lo :7;
236
u32 fmt :2; // Must be 1.
237
} jc_rumble_fmt1_28bit_t;
238
239
typedef struct
240
{
241
u32 rdvd :10;
242
u32 vibc_hi:5;
243
u32 vibc_lo:5;
244
u32 code_hi:5;
245
u32 code_lo:5;
246
u32 fmt :2; // Must be 2.
247
} jc_rumble_fmt2_t;
248
249
typedef struct
250
{
251
u32 is_high :1;
252
u32 freq :7;
253
u32 vibc_hi1:5;
254
u32 vibc_lo1:5;
255
u32 vibc_hi0:5; // vibc_lo0 if is_high.
256
u32 amp :7;
257
u32 fmt :2; // Must be 2.
258
} jc_rumble_fmt2_14bit_t;
259
260
typedef struct
261
{
262
u32 vibc_hi1:5;
263
u32 vibc_lo1:5;
264
u32 vibc_hi0:5;
265
u32 vibc_lo0:5;
266
u32 code_hi :5;
267
u32 code_lo :5;
268
u32 fmt :2; // Must be 3.
269
} jc_rumble_fmt3_t;
270
271
typedef struct
272
{
273
u32 is_high:1;
274
u32 is_7bit:1;
275
u32 is_fm :1;
276
u32 vibc_hi1:5;
277
u32 vibc_lo1:5;
278
u32 vibc_hi0:5;
279
u32 vibc_lo0:5;
280
u32 amfm :7;
281
u32 fmt :2; // Must be 1 (not 3).
282
} jc_rumble_fmt3_7bit_t;
283
284
typedef struct _jc_rumble_t
285
{
286
union {
287
jc_rumble_fmt1_t fmt1;
288
jc_rumble_fmt1_28bit_t fmt1_28b;
289
jc_rumble_fmt3_7bit_t fmt3_7b;
290
jc_rumble_fmt2_t fmt2;
291
jc_rumble_fmt2_14bit_t fmt2_14b;
292
jc_rumble_fmt3_t fmt3;
293
u32 r32;
294
u8 r8[4];
295
};
296
} __attribute__((packed)) jc_rumble_t;
297
298
typedef struct _jc_uart_hdr_t
299
{
300
u8 magic[3]; // Type, Destination, Group.
301
u16 total_size;
302
} __attribute__((packed)) jc_uart_hdr_t;
303
304
typedef struct _jc_wired_hdr_t
305
{
306
jc_uart_hdr_t uart_hdr;
307
u8 cmd;
308
u8 subcmd;
309
u16 payload_size;
310
// As out, cmd exists query. For JC_WIRED_CMD_SET_PAIRING: 1-4 pairing step.
311
// As in, 0xF: exists. For JC_WIRED_CMD_SET_HIDRATE: 1 wrong rate.
312
u8 status;
313
u8 crc_payload;
314
u8 crc_hdr;
315
u8 payload[];
316
} __attribute__((packed)) jc_wired_hdr_t;
317
318
typedef struct _jc_hid_out_rpt_t
319
{
320
u8 cmd;
321
u8 pkt_id;
322
jc_rumble_t rumble[2];
323
u8 subcmd;
324
u8 subcmd_data[];
325
} __attribute__((packed)) jc_hid_out_rpt_t;
326
327
typedef struct _jc_hid_in_rpt_t
328
{
329
u8 cmd;
330
u8 pkt_id; // Latency timer. 5 ms lsb (every 4 x 1.25 ms).
331
u8 conn_info:4; // Connection detect.
332
u8 batt_info:4; // Power info.
333
u16 btn_right:12;
334
u16 btn_left:12;
335
u16 stick_left_x:12;
336
u16 stick_left_y:12;
337
u16 stick_right_x:12;
338
u16 stick_right_y:12;
339
u8 vib_decider; // right:4, left:4 (bit3 en, bit2-0 buffer avail).
340
u8 submcd_ack;
341
u8 subcmd;
342
u8 subcmd_data[];
343
} __attribute__((packed)) jc_hid_in_rpt_t;
344
345
typedef struct _jc_hid_out_spi_read_t
346
{
347
u32 addr;
348
u8 size;
349
} __attribute__((packed)) jc_hid_out_spi_read_t;
350
351
typedef struct _jc_hid_in_spi_read_t
352
{
353
u32 addr;
354
u8 size;
355
u8 data[];
356
} __attribute__((packed)) jc_hid_in_spi_read_t;
357
358
typedef struct _jc_hid_in_pair_data_t
359
{
360
u8 magic;
361
u8 size;
362
u16 checksum;
363
u8 mac[6];
364
u8 ltk[16];
365
u8 pad0[10];
366
u8 bt_caps; // bit3: Secure conn supported host, bit5: Paired to TBFC supported host, bit6: iTBFC page supported
367
u8 pad1;
368
} __attribute__((packed)) jc_hid_in_pair_data_t;
369
370
typedef struct _jc_sio_out_rpt_t
371
{
372
u8 cmd;
373
u8 subcmd;
374
u16 payload_size;
375
u8 data[2];
376
u8 crc_payload;
377
u8 crc_hdr;
378
u8 payload[];
379
} __attribute__((packed)) jc_sio_out_rpt_t;
380
381
typedef struct _jc_sio_in_rpt_t
382
{
383
u8 cmd;
384
u8 subcmd;
385
u16 payload_size;
386
u8 status;
387
u8 unk;
388
u8 crc_payload;
389
u8 crc_hdr;
390
u8 payload[];
391
} __attribute__((packed)) jc_sio_in_rpt_t;
392
393
typedef struct _jc_hid_in_sixaxis_rpt_t
394
{
395
s16 acc_x;
396
s16 acc_y;
397
s16 acc_z;
398
s16 gyr_x;
399
s16 gyr_y;
400
s16 gyr_z;
401
} __attribute__((packed)) jc_hid_in_sixaxis_rpt_t;
402
403
typedef struct _jc_sio_hid_in_rpt_t
404
{
405
u8 type;
406
u8 pkt_id; // Latency timer.
407
u8 unk;
408
u16 btn_right:12;
409
u16 btn_left:12;
410
u16 stick_left_x:12;
411
u16 stick_left_y:12;
412
u16 stick_right_x:12;
413
u16 stick_right_y:12;
414
u8 sixaxis_rpt; // bit0-3: report num. bit4-7: imu type.
415
// Each report is 800 us?
416
jc_hid_in_sixaxis_rpt_t sixaxis[15];
417
} __attribute__((packed)) jc_sio_hid_in_rpt_t;
418
419
typedef struct _jc_dev_t
420
{
421
u8 buf[0x100];
422
u8 uart;
423
u8 type;
424
u8 state;
425
u8 pkt_id;
426
bool detected;
427
bool connected;
428
bool sio_mode;
429
bool rumble_sent;
430
bool charger_req;
431
u32 last_received_time; // Reset with JC_WIRED_CMD_HID_CONN/JC_WIRED_HID. If exceeded HID mode disconnects.
432
u32 last_status_req_time;
433
u32 last_chrger_chk_time;
434
u8 mac[6];
435
} jc_dev_t;
436
437
static jc_dev_t jc_l = {0};
438
static jc_dev_t jc_r = {0};
439
440
static bool jc_init_done = false;
441
442
static jc_gamepad_rpt_t jc_gamepad;
443
444
static void _jc_rcv_pkt(jc_dev_t *jc);
445
446
static u8 _jc_crc(const u8 *data, u16 len)
447
{
448
u8 crc = 0;
449
for (u16 i = 0; i < len; i++)
450
{
451
crc ^= data[i];
452
for (u16 j = 0; j < 8; j++)
453
{
454
if ((crc & 0x80) != 0)
455
crc = (u8)((crc << 1) ^ JC_CRC8_POLY);
456
else
457
crc <<= 1;
458
}
459
}
460
return crc;
461
}
462
463
static void _jc_power_supply(u8 uart, bool enable)
464
{
465
if (enable)
466
{
467
if (regulator_5v_get_dev_enabled(1 << uart))
468
return;
469
470
regulator_5v_enable(1 << uart);
471
472
if (jc_gamepad.sio_mode)
473
return;
474
475
if (jc_init_done)
476
{
477
if (uart == UART_C)
478
gpio_write(GPIO_PORT_CC, GPIO_PIN_3, GPIO_HIGH);
479
else
480
gpio_write(GPIO_PORT_K, GPIO_PIN_3, GPIO_HIGH);
481
return;
482
}
483
484
if (uart == UART_C)
485
{
486
// Joy-Con(L) Charge Enable.
487
PINMUX_AUX(PINMUX_AUX_SPDIF_IN) = PINMUX_PULL_DOWN | 1;
488
gpio_direction_output(GPIO_PORT_CC, GPIO_PIN_3, GPIO_HIGH);
489
}
490
else
491
{
492
// Joy-Con(R) Charge Enable.
493
PINMUX_AUX(PINMUX_AUX_GPIO_PK3) = PINMUX_DRIVE_4X | PINMUX_PULL_DOWN | 2;
494
gpio_direction_output(GPIO_PORT_K, GPIO_PIN_3, GPIO_HIGH);
495
}
496
}
497
else
498
{
499
if (!regulator_5v_get_dev_enabled(1 << uart))
500
return;
501
502
regulator_5v_disable(1 << uart);
503
504
if (jc_gamepad.sio_mode)
505
return;
506
507
if (uart == UART_C)
508
gpio_write(GPIO_PORT_CC, GPIO_PIN_3, GPIO_LOW);
509
else
510
gpio_write(GPIO_PORT_K, GPIO_PIN_3, GPIO_LOW);
511
}
512
}
513
514
static void _jc_rail_detect()
515
{
516
// Turn on Joy-Con detect. (UARTB/C TX). UART CTS also if HW flow control and irq is enabled.
517
PINMUX_AUX(PINMUX_AUX_UART2_TX) = PINMUX_INPUT_ENABLE;
518
PINMUX_AUX(PINMUX_AUX_UART3_TX) = PINMUX_INPUT_ENABLE;
519
gpio_direction_input(GPIO_PORT_G, GPIO_PIN_0);
520
gpio_direction_input(GPIO_PORT_D, GPIO_PIN_1);
521
usleep(20);
522
523
//! HW BUG: Unlatch gpio buffer.
524
(void)gpio_read(GPIO_PORT_H, GPIO_PIN_6);
525
(void)gpio_read(GPIO_PORT_E, GPIO_PIN_6);
526
527
// Read H6/E6 which are shared with UART TX pins.
528
jc_r.detected = !gpio_read(GPIO_PORT_H, GPIO_PIN_6);
529
jc_l.detected = !gpio_read(GPIO_PORT_E, GPIO_PIN_6);
530
531
// Turn off Joy-Con detect. (UARTB/C TX).
532
PINMUX_AUX(PINMUX_AUX_UART2_TX) = 0;
533
PINMUX_AUX(PINMUX_AUX_UART3_TX) = 0;
534
gpio_config(GPIO_PORT_G, GPIO_PIN_0, GPIO_MODE_SPIO);
535
gpio_config(GPIO_PORT_D, GPIO_PIN_1, GPIO_MODE_SPIO);
536
usleep(20);
537
}
538
539
static void _jc_conn_check()
540
{
541
if (jc_gamepad.sio_mode)
542
{
543
//! TODO: Is there a way to detect a broken Sio?
544
jc_l.detected = true;
545
546
return;
547
}
548
549
_jc_rail_detect();
550
551
// Check if a Joy-Con was disconnected.
552
if (!jc_l.detected)
553
{
554
jc_l.pkt_id = 0;
555
556
jc_l.connected = false;
557
jc_l.rumble_sent = false;
558
jc_l.charger_req = false;
559
jc_l.last_received_time = 0;
560
561
jc_gamepad.conn_l = false;
562
563
jc_gamepad.batt_info_l = 0;
564
jc_gamepad.batt_chrg_l = 0;
565
jc_gamepad.bt_conn_l.type = 0;
566
jc_gamepad.buttons &= ~JC_BTN_MASK_L;
567
}
568
569
if (!jc_r.detected)
570
{
571
jc_r.pkt_id = 0;
572
573
jc_r.connected = false;
574
jc_r.rumble_sent = false;
575
jc_r.charger_req = false;
576
jc_r.last_received_time = 0;
577
578
jc_gamepad.conn_r = false;
579
580
jc_gamepad.batt_info_r = 0;
581
jc_gamepad.batt_chrg_r = 0;
582
jc_gamepad.bt_conn_r.type = 0;
583
jc_gamepad.buttons &= ~JC_BTN_MASK_R;
584
}
585
}
586
587
static void _joycon_send_raw(u8 uart_port, const u8 *buf, u16 size)
588
{
589
uart_send(uart_port, buf, size);
590
uart_wait_xfer(uart_port, UART_TX_IDLE);
591
}
592
593
static u16 _jc_packet_add_uart_hdr(jc_wired_hdr_t *rpt, u8 cmd, u8 subcmd, u16 payload_size, bool crc)
594
{
595
memcpy(rpt->uart_hdr.magic, JC_WIRED_SND_MAGIC, sizeof(rpt->uart_hdr.magic));
596
597
rpt->uart_hdr.total_size = sizeof(jc_wired_hdr_t) - sizeof(jc_uart_hdr_t) + payload_size;
598
rpt->cmd = cmd;
599
rpt->subcmd = subcmd;
600
rpt->payload_size = payload_size; // Only used if JC_WIRED_CMD.
601
602
// Only calculated if JC_WIRED_CMD/JC_WIRED_HID and result never checked?
603
// if (payload_size)
604
// rpt->crc_payload = crc ? _jc_crc(rpt->payload, payload_size) : 0;
605
606
// Only calculated if JC_WIRED_CMD/JC_WIRED_HID/JC_WIRED_HANDSHAKE and result only checked on HORI.
607
rpt->crc_hdr = crc ? _jc_crc(&rpt->cmd, sizeof(jc_wired_hdr_t) - sizeof(jc_uart_hdr_t) - 1) : 0;
608
609
return (rpt->uart_hdr.total_size + sizeof(jc_uart_hdr_t));
610
}
611
612
static u16 _jc_hid_output_rpt_craft(jc_wired_hdr_t *rpt, const u8 *payload, u16 payload_size, bool crc)
613
{
614
if (payload)
615
memcpy(rpt->payload, payload, payload_size);
616
617
return _jc_packet_add_uart_hdr(rpt, JC_WIRED_HID, 0, payload_size, crc);
618
}
619
620
static void _jc_send_hid_output_rpt(jc_dev_t *jc, jc_hid_out_rpt_t *hid_pkt, u16 size, bool crc)
621
{
622
u8 rpt[0x50];
623
memset(rpt, 0, sizeof(rpt));
624
625
hid_pkt->pkt_id = (jc->pkt_id++ & 0xF);
626
u32 rpt_size = _jc_hid_output_rpt_craft((jc_wired_hdr_t *)rpt, (u8 *)hid_pkt, size, crc);
627
628
_joycon_send_raw(jc->uart, rpt, rpt_size);
629
}
630
631
static void _jc_send_hid_cmd(jc_dev_t *jc, u8 subcmd, const u8 *data, u16 size)
632
{
633
static const u8 rumble_mute[8] = { 0x00, 0x01, 0x40, 0x40, 0x00, 0x01, 0x40, 0x40 };
634
static const u8 rumble_init[8] = { 0xc2, 0xc8, 0x03, 0x72, 0xc2, 0xc8, 0x03, 0x72 };
635
636
u8 temp[0x30] = {0};
637
638
jc_hid_out_rpt_t *hid_pkt = (jc_hid_out_rpt_t *)temp;
639
memcpy(hid_pkt->rumble, rumble_mute, sizeof(rumble_mute));
640
641
if (subcmd == JC_HID_SUBCMD_SND_RUMBLE)
642
{
643
bool send_r_rumble = jc_r.connected && !jc_r.rumble_sent;
644
bool send_l_rumble = jc_l.connected && !jc_l.rumble_sent;
645
646
// Enable rumble.
647
hid_pkt->cmd = JC_HID_OUTPUT_RPT;
648
hid_pkt->subcmd = JC_HID_SUBCMD_RUMBLE_CTL;
649
hid_pkt->subcmd_data[0] = 1;
650
if (send_r_rumble)
651
_jc_send_hid_output_rpt(&jc_r, hid_pkt, 0x10, false);
652
if (send_l_rumble)
653
_jc_send_hid_output_rpt(&jc_l, hid_pkt, 0x10, false);
654
655
msleep(15);
656
657
if (send_r_rumble)
658
_jc_rcv_pkt(&jc_r);
659
if (send_l_rumble)
660
_jc_rcv_pkt(&jc_l);
661
662
// Send rumble.
663
hid_pkt->cmd = JC_HID_RUMBLE_RPT;
664
memcpy(hid_pkt->rumble, rumble_init, sizeof(rumble_init));
665
if (send_r_rumble)
666
_jc_send_hid_output_rpt(&jc_r, hid_pkt, 10, false);
667
if (send_l_rumble)
668
_jc_send_hid_output_rpt(&jc_l, hid_pkt, 10, false);
669
670
msleep(15);
671
672
// Mute rumble.
673
hid_pkt->cmd = JC_HID_RUMBLE_RPT;
674
memcpy(hid_pkt->rumble, rumble_mute, sizeof(rumble_mute));
675
if (send_r_rumble)
676
_jc_send_hid_output_rpt(&jc_r, hid_pkt, 10, false);
677
if (send_l_rumble)
678
_jc_send_hid_output_rpt(&jc_l, hid_pkt, 10, false);
679
}
680
else
681
{
682
bool crc_needed = jc->type & JC_ID_HORI;
683
684
hid_pkt->cmd = JC_HID_OUTPUT_RPT;
685
hid_pkt->subcmd = subcmd;
686
if (data)
687
memcpy(hid_pkt->subcmd_data, data, size);
688
689
_jc_send_hid_output_rpt(jc, hid_pkt, sizeof(jc_hid_out_rpt_t) + size, crc_needed);
690
}
691
}
692
693
static void _jc_parse_input(jc_dev_t *jc, const jc_hid_in_rpt_t *hid_pkt)
694
{
695
u32 btn_tmp;
696
btn_tmp = hid_pkt->btn_right | hid_pkt->btn_left << 12;
697
698
if (jc->type & JC_ID_L)
699
{
700
jc_gamepad.buttons &= ~JC_BTN_MASK_L;
701
jc_gamepad.buttons |= (btn_tmp & JC_BTN_MASK_L);
702
703
jc_gamepad.lstick_x = hid_pkt->stick_left_x;
704
jc_gamepad.lstick_y = hid_pkt->stick_left_y;
705
706
jc_gamepad.batt_info_l = hid_pkt->batt_info;
707
}
708
else if (jc->type & JC_ID_R)
709
{
710
jc_gamepad.buttons &= ~JC_BTN_MASK_R;
711
jc_gamepad.buttons |= (btn_tmp & JC_BTN_MASK_R);
712
713
jc_gamepad.rstick_x = hid_pkt->stick_right_x;
714
jc_gamepad.rstick_y = hid_pkt->stick_right_y;
715
716
jc_gamepad.batt_info_r = hid_pkt->batt_info;
717
}
718
}
719
720
static void _jc_parse_wired_hid(jc_dev_t *jc, const u8 *packet, int size)
721
{
722
const jc_hid_in_rpt_t *hid_pkt = (jc_hid_in_rpt_t *)packet;
723
724
switch (hid_pkt->cmd)
725
{
726
case JC_HORI_INPUT_RPT:
727
if (!(jc->type & JC_ID_HORI))
728
return;
729
730
case JC_HID_INPUT_RPT:
731
// Discard incomplete hid packets.
732
if (size < 12)
733
break;
734
735
_jc_parse_input(jc, hid_pkt);
736
737
jc_gamepad.conn_l = jc_l.connected;
738
jc_gamepad.conn_r = jc_r.connected;
739
break;
740
741
case JC_HID_SUBMCD_RPT:
742
if (hid_pkt->subcmd == JC_HID_SUBCMD_SPI_READ)
743
{
744
jc_bt_conn_t *bt_conn;
745
746
if (jc->type & JC_ID_L)
747
bt_conn = &jc_gamepad.bt_conn_l;
748
else
749
bt_conn = &jc_gamepad.bt_conn_r;
750
751
jc_hid_in_spi_read_t *spi_info = (jc_hid_in_spi_read_t *)hid_pkt->subcmd_data;
752
jc_hid_in_pair_data_t *pair_data = (jc_hid_in_pair_data_t *)spi_info->data;
753
754
// Check if the reply is pairing info.
755
if (spi_info->size == 0x1A && pair_data->magic == 0x95 && pair_data->size == 0x22)
756
{
757
bt_conn->type = jc->type;
758
759
memcpy(bt_conn->mac, jc->mac, 6);
760
memcpy(bt_conn->host_mac, pair_data->mac, 6);
761
for (u32 i = 16; i > 0; i--)
762
bt_conn->ltk[16 - i] = pair_data->ltk[i - 1];
763
}
764
}
765
else if (hid_pkt->subcmd == JC_HID_SUBCMD_CHARGE_SET)
766
jc->charger_req = false;
767
768
_jc_parse_input(jc, hid_pkt);
769
770
break;
771
772
default:
773
break;
774
}
775
}
776
777
static void _jc_parse_wired_init(jc_dev_t *jc, const jc_wired_hdr_t *pkt, int size)
778
{
779
// Discard empty packets.
780
if (size <= 0)
781
return;
782
783
const u8 *payload = pkt->payload;
784
785
switch (pkt->subcmd)
786
{
787
case JC_WIRED_CMD_GET_INFO:
788
if (!pkt->status)
789
{
790
for (int i = 6; i > 0; i--)
791
jc->mac[6 - i] = payload[i];
792
jc->type = payload[0];
793
jc->state = JC_STATE_INFO_PARSED;
794
}
795
break;
796
797
case JC_WIRED_CMD_SET_BRATE:
798
if (!pkt->status)
799
jc->state = JC_STATE_BRATE_CHANGED;
800
break;
801
802
case JC_WIRED_CMD_HID_DISC:
803
if (pkt->status == 0xF)
804
jc->state = JC_STATE_HID_NO_CONN;
805
break;
806
807
case JC_WIRED_CMD_HID_CONN:
808
if (!pkt->status)
809
jc->state = JC_STATE_HID_CONN;
810
break;
811
812
case JC_WIRED_CMD_SET_HIDRATE:
813
jc->state = JC_STATE_INIT_DONE;
814
jc->connected = true;
815
break;
816
817
default:
818
break;
819
}
820
}
821
822
static void _jc_uart_pkt_parse(jc_dev_t *jc, const jc_wired_hdr_t *pkt, int size)
823
{
824
switch (pkt->cmd)
825
{
826
case JC_HORI_INPUT_RPT_CMD:
827
case JC_WIRED_HID:
828
_jc_parse_wired_hid(jc, pkt->payload, size - sizeof(jc_wired_hdr_t));
829
break;
830
831
case JC_WIRED_INIT_REPLY:
832
_jc_parse_wired_init(jc, pkt, size);
833
break;
834
835
case JC_WIRED_HANDSHAKE:
836
jc->state = JC_STATE_HANDSHAKED;
837
break;
838
839
default:
840
break;
841
}
842
843
jc->last_received_time = get_tmr_ms();
844
}
845
846
static void _jc_sio_parse_payload(jc_dev_t *jc, u8 cmd, const u8 *payload, int size)
847
{
848
switch (cmd)
849
{
850
case JC_SIO_CMD_STATUS:
851
// Discard incomplete packets.
852
if (size < 12)
853
break;
854
855
jc_sio_hid_in_rpt_t *hid_pkt = (jc_sio_hid_in_rpt_t *)payload;
856
jc_gamepad.buttons = hid_pkt->btn_right | hid_pkt->btn_left << 12;
857
jc_gamepad.home = !gpio_read(GPIO_PORT_V, GPIO_PIN_3);
858
859
jc_gamepad.lstick_x = hid_pkt->stick_left_x;
860
jc_gamepad.lstick_y = hid_pkt->stick_left_y;
861
jc_gamepad.rstick_x = hid_pkt->stick_right_x;
862
jc_gamepad.rstick_y = hid_pkt->stick_right_y;
863
864
jc_gamepad.batt_info_l = jc_l.connected;
865
jc_gamepad.batt_info_r = gpio_read(GPIO_PORT_E, GPIO_PIN_7); // Set IRQ status.
866
867
jc_gamepad.conn_l = jc_l.connected;
868
jc_gamepad.conn_r = jc_l.connected;
869
break;
870
871
default:
872
break;
873
}
874
}
875
876
static void _jc_sio_uart_pkt_parse(jc_dev_t *jc, const jc_sio_in_rpt_t *pkt, int size)
877
{
878
if (pkt->crc_hdr != _jc_crc((u8 *)pkt, sizeof(jc_sio_in_rpt_t) - 1))
879
return;
880
881
u8 cmd = pkt->subcmd & (~JC_SIO_CMD_ACK);
882
switch (cmd)
883
{
884
case JC_SIO_CMD_INIT:
885
if (!pkt->status)
886
jc->state = JC_STATE_HANDSHAKED;
887
break;
888
889
case JC_SIO_CMD_VER_RPT:
890
if (!pkt->status)
891
jc->state = JC_STATE_HID_CONN;
892
break;
893
894
case JC_SIO_CMD_IAP_VER:
895
case JC_SIO_CMD_STATUS:
896
_jc_sio_parse_payload(jc, cmd, pkt->payload, size - sizeof(jc_sio_in_rpt_t));
897
break;
898
899
case JC_SIO_CMD_UNK02:
900
case JC_SIO_CMD_UNK20:
901
case JC_SIO_CMD_UNK21:
902
case JC_SIO_CMD_UNK22:
903
case JC_SIO_CMD_UNK40:
904
default:
905
break;
906
}
907
908
jc->last_received_time = get_tmr_ms();
909
}
910
911
static void _jc_rcv_pkt(jc_dev_t *jc)
912
{
913
if (!jc->detected)
914
return;
915
916
u32 len = uart_recv(jc->uart, (u8 *)jc->buf, sizeof(jc->buf));
917
if (len < 8)
918
return;
919
920
// For Joycon, check uart reply magic.
921
jc_wired_hdr_t *jc_pkt = (jc_wired_hdr_t *)jc->buf;
922
if (!jc->sio_mode && !memcmp(jc_pkt->uart_hdr.magic, JC_WIRED_RCV_MAGIC, sizeof(jc_pkt->uart_hdr.magic)))
923
{
924
_jc_uart_pkt_parse(jc, jc_pkt, len);
925
926
return;
927
}
928
929
// For Sio, check uart output report and command ack.
930
jc_sio_in_rpt_t *sio_pkt = (jc_sio_in_rpt_t *)(jc->buf);
931
if (jc->sio_mode && sio_pkt->cmd == JC_SIO_INPUT_RPT && (sio_pkt->subcmd & JC_SIO_CMD_ACK) == JC_SIO_CMD_ACK)
932
{
933
_jc_sio_uart_pkt_parse(jc, sio_pkt, len);
934
935
return;
936
}
937
}
938
939
static bool _jc_handle_charging(jc_dev_t *jc)
940
{
941
if (jc->last_chrger_chk_time > get_tmr_ms())
942
return false;
943
944
jc->last_chrger_chk_time = get_tmr_ms() + 5000;
945
946
u8 old_mode, batt_now;
947
if (jc->uart == UART_B)
948
{
949
batt_now = jc_gamepad.batt_info_r;
950
old_mode = jc_gamepad.batt_chrg_r;
951
}
952
else
953
{
954
batt_now = jc_gamepad.batt_info_l;
955
old_mode = jc_gamepad.batt_chrg_l;
956
}
957
958
// Let battery level settle if init.
959
u8 new_mode = old_mode;
960
if (!old_mode)
961
{
962
new_mode = JC_CHRG_STATE_SUPL;
963
if ((batt_now >> 1) <= JC_BATT_LOW)
964
new_mode = JC_CHRG_STATE_SLOW;
965
966
goto set_mode;
967
}
968
969
// Resend if no reply.
970
if (jc->charger_req)
971
goto set_mode;
972
973
// Power supply control based on battery levels and charging.
974
switch (batt_now >> 1)
975
{
976
case JC_BATT_EMPTY:
977
case JC_BATT_CRIT:
978
case JC_BATT_LOW:
979
new_mode = JC_CHRG_STATE_SLOW;
980
break;
981
982
case JC_BATT_MID:
983
if (!(batt_now & 1))
984
new_mode = JC_CHRG_STATE_SUPL;
985
break;
986
987
case JC_BATT_FULL:
988
new_mode = JC_CHRG_STATE_OFF;
989
break;
990
}
991
992
// Check if already configured.
993
if (new_mode == old_mode)
994
return false;
995
996
set_mode:
997
if (jc->uart == UART_B)
998
jc_gamepad.batt_chrg_r = new_mode;
999
else
1000
jc_gamepad.batt_chrg_l = new_mode;
1001
1002
switch (new_mode)
1003
{
1004
case JC_CHRG_STATE_OFF:
1005
_jc_power_supply(jc->uart, false);
1006
new_mode = JC_CHRG_CFG_SUPL0;
1007
break;
1008
case JC_CHRG_STATE_SUPL:
1009
_jc_power_supply(jc->uart, true);
1010
new_mode = JC_CHRG_CFG_SUPL0;
1011
break;
1012
case JC_CHRG_STATE_SLOW:
1013
_jc_power_supply(jc->uart, true);
1014
new_mode = JC_CHRG_CFG_100MA;
1015
break;
1016
}
1017
1018
_jc_send_hid_cmd(jc, JC_HID_SUBCMD_CHARGE_SET, (u8 *)&new_mode, sizeof(new_mode));
1019
jc->charger_req = true;
1020
1021
jc->last_status_req_time = get_tmr_ms() + 15;
1022
1023
return true;
1024
}
1025
1026
static bool _jc_send_enable_rumble(jc_dev_t *jc)
1027
{
1028
bool send_r_rumble = jc_r.connected && !jc_r.rumble_sent;
1029
bool send_l_rumble = jc_l.connected && !jc_l.rumble_sent;
1030
1031
// Do not sent report yet if second Joy-Con is expected to be initialized.
1032
if ((send_r_rumble && !jc_l.rumble_sent && jc_l.state == JC_STATE_HID_CONN) ||
1033
(send_l_rumble && !jc_r.rumble_sent && jc_r.state == JC_STATE_HID_CONN))
1034
return 1;
1035
1036
// Send init rumble or request nx pad status report.
1037
if (send_r_rumble || send_l_rumble)
1038
{
1039
_jc_send_hid_cmd(jc, JC_HID_SUBCMD_SND_RUMBLE, NULL, 0);
1040
1041
if (jc_l.connected)
1042
jc_l.rumble_sent = true;
1043
if (jc_r.connected)
1044
jc_r.rumble_sent = true;
1045
1046
jc->last_chrger_chk_time = get_tmr_ms() + 5000;
1047
1048
return 1;
1049
}
1050
1051
return 0;
1052
}
1053
1054
static void _jc_req_status(jc_dev_t *jc)
1055
{
1056
if (!jc->connected)
1057
return;
1058
1059
if (jc->last_status_req_time > get_tmr_ms())
1060
return;
1061
1062
bool is_nxpad = !(jc->type & JC_ID_HORI) && !jc->sio_mode;
1063
1064
// Init/maintenance for Joy-Con.
1065
if (is_nxpad)
1066
{
1067
if (_jc_send_enable_rumble(jc))
1068
return;
1069
1070
if (_jc_handle_charging(jc))
1071
return;
1072
}
1073
1074
if (is_nxpad)
1075
_joycon_send_raw(jc->uart, _jc_nx_pad_status, sizeof(_jc_nx_pad_status));
1076
else if (jc->sio_mode)
1077
_joycon_send_raw(jc->uart, _sio_pad_status, sizeof(_sio_pad_status));
1078
else
1079
_joycon_send_raw(jc->uart, _jc_hori_pad_status, sizeof(_jc_hori_pad_status));
1080
1081
jc->last_status_req_time = get_tmr_ms() + (!jc->sio_mode ? 15 : 7);
1082
}
1083
1084
static bool _jc_validate_pairing_info(const u8 *buf, bool *is_hos)
1085
{
1086
u8 crc = 0;
1087
for (u32 i = 0; i < 0x22; i++)
1088
crc += buf[4 + i];
1089
1090
crc += 0x68; // Host is Switch.
1091
1092
if ((crc ^ 0x55) == buf[2])
1093
*is_hos = true;
1094
1095
crc -= 0x68;
1096
crc += 0x08; // Host is PC.
1097
1098
if (*is_hos || (crc ^ 0x55) == buf[2])
1099
return true;
1100
1101
return false;
1102
}
1103
1104
jc_gamepad_rpt_t *jc_get_bt_pairing_info(bool *is_l_hos, bool *is_r_hos)
1105
{
1106
u8 retries;
1107
jc_bt_conn_t *bt_conn;
1108
1109
if (!jc_init_done || jc_gamepad.sio_mode)
1110
return NULL;
1111
1112
// Detect and init if needed.
1113
for (u32 i = 0; i < 6; i++)
1114
{
1115
joycon_poll();
1116
msleep(15);
1117
}
1118
1119
while ((jc_l.last_status_req_time + 15) > get_tmr_ms())
1120
{
1121
_jc_rcv_pkt(&jc_r);
1122
_jc_rcv_pkt(&jc_l);
1123
}
1124
1125
bt_conn = &jc_gamepad.bt_conn_l;
1126
memset(bt_conn->host_mac, 0, 6);
1127
memset(bt_conn->ltk, 0, 16);
1128
1129
bt_conn = &jc_gamepad.bt_conn_r;
1130
memset(bt_conn->host_mac, 0, 6);
1131
memset(bt_conn->ltk, 0, 16);
1132
1133
// Setup initial SPI address.
1134
jc_hid_out_spi_read_t subcmd_data_l;
1135
subcmd_data_l.addr = 0x2000;
1136
subcmd_data_l.size = 0x1A;
1137
1138
jc_hid_out_spi_read_t subcmd_data_r;
1139
subcmd_data_r.addr = 0x2000;
1140
subcmd_data_r.size = 0x1A;
1141
1142
bool jc_r_found = jc_r.connected ? false : true;
1143
bool jc_l_found = jc_l.connected ? false : true;
1144
1145
// Set mode to HW controlled RTS.
1146
uart_set_mode(jc_l.uart, UART_AO_TX_HW_RX);
1147
uart_set_mode(jc_r.uart, UART_AO_TX_HW_RX);
1148
1149
u32 total_retries = 10;
1150
retry:
1151
retries = 10;
1152
while (retries)
1153
{
1154
u32 time_now = get_tmr_ms();
1155
if ((!jc_l_found && jc_l.last_status_req_time < time_now) || (!jc_r_found && jc_r.last_status_req_time < time_now))
1156
{
1157
if (!jc_l_found)
1158
{
1159
_jc_send_hid_cmd(&jc_l, JC_HID_SUBCMD_SPI_READ, (u8 *)&subcmd_data_l, sizeof(jc_hid_out_spi_read_t));
1160
jc_l.last_status_req_time = get_tmr_ms() + 15;
1161
}
1162
1163
if (!jc_r_found)
1164
{
1165
_jc_send_hid_cmd(&jc_r, JC_HID_SUBCMD_SPI_READ, (u8 *)&subcmd_data_r, sizeof(jc_hid_out_spi_read_t));
1166
jc_r.last_status_req_time = get_tmr_ms() + 15;
1167
}
1168
1169
retries--;
1170
}
1171
1172
// Wait for the first 36 bytes to arrive.
1173
msleep(5);
1174
1175
if (!jc_l_found)
1176
{
1177
memset(jc_l.buf, 0, 0x100);
1178
_jc_rcv_pkt(&jc_l);
1179
1180
bool is_hos = false;
1181
if (_jc_validate_pairing_info(&jc_l.buf[SPI_READ_OFFSET], &is_hos))
1182
{
1183
bool is_active = jc_l.buf[SPI_READ_OFFSET] == 0x95;
1184
1185
if (!is_active)
1186
subcmd_data_l.addr += sizeof(jc_hid_in_pair_data_t); // Get next slot.
1187
else
1188
jc_l_found = true; // Entry is active.
1189
1190
if (jc_l_found && is_hos)
1191
*is_l_hos = true;
1192
}
1193
}
1194
1195
if (!jc_r_found)
1196
{
1197
memset(jc_r.buf, 0, 0x100);
1198
_jc_rcv_pkt(&jc_r);
1199
1200
bool is_hos = false;
1201
if (_jc_validate_pairing_info(&jc_r.buf[SPI_READ_OFFSET], &is_hos))
1202
{
1203
bool is_active = jc_r.buf[SPI_READ_OFFSET] == 0x95;
1204
1205
if (!is_active)
1206
subcmd_data_r.addr += sizeof(jc_hid_in_pair_data_t); // Get next slot.
1207
else
1208
jc_r_found = true; // Entry is active.
1209
1210
if (jc_r_found && is_hos)
1211
*is_r_hos = true;
1212
}
1213
}
1214
1215
if (jc_l_found && jc_r_found)
1216
break;
1217
}
1218
1219
if (!jc_l_found || !jc_r_found)
1220
{
1221
if (total_retries)
1222
{
1223
total_retries--;
1224
goto retry;
1225
}
1226
1227
if (!jc_l_found)
1228
{
1229
bt_conn = &jc_gamepad.bt_conn_l;
1230
memset(bt_conn->host_mac, 0, 6);
1231
memset(bt_conn->ltk, 0, 16);
1232
}
1233
1234
if (!jc_r_found)
1235
{
1236
bt_conn = &jc_gamepad.bt_conn_r;
1237
memset(bt_conn->host_mac, 0, 6);
1238
memset(bt_conn->ltk, 0, 16);
1239
}
1240
}
1241
1242
// Restore mode to manual RTS.
1243
uart_set_mode(jc_l.uart, UART_AO_TX_MN_RX);
1244
uart_set_mode(jc_r.uart, UART_AO_TX_MN_RX);
1245
1246
return &jc_gamepad;
1247
}
1248
1249
static void _jc_conn_init(jc_dev_t *jc)
1250
{
1251
if (!jc->detected)
1252
return;
1253
1254
/*
1255
* Try to reinit if no input report.
1256
* Actual connection timeout is 2000ms on official Joycon.
1257
* Based on last JC_WIRED_CMD_HID_CONN/JC_WIRED_HID sent.
1258
*/
1259
if (((u32)get_tmr_ms() - jc->last_received_time) > 1800)
1260
{
1261
if (!jc->sio_mode)
1262
_jc_power_supply(jc->uart, true);
1263
1264
// Mask out buttons and set connected to false.
1265
if (jc->uart == UART_B)
1266
{
1267
jc_gamepad.batt_chrg_r = 0;
1268
jc_gamepad.buttons &= ~JC_BTN_MASK_R;
1269
jc_gamepad.conn_r = false;
1270
}
1271
else
1272
{
1273
jc_gamepad.batt_chrg_l = 0;
1274
jc_gamepad.buttons &= ~JC_BTN_MASK_L;
1275
jc_gamepad.conn_l = false;
1276
}
1277
1278
// Initialize uart to 1 megabaud and manual RTS.
1279
uart_init(jc->uart, 1000000, UART_AO_TX_MN_RX);
1280
1281
jc->state = JC_STATE_START;
1282
1283
if (!jc->sio_mode)
1284
{
1285
jc_gamepad.buttons = 0;
1286
1287
// Set TX and RTS inversion for Joycon.
1288
uart_invert(jc->uart, true, UART_INVERT_TXD | UART_INVERT_RTS);
1289
1290
// Initialize.
1291
u32 retries = 10;
1292
while (retries && jc->state != JC_STATE_HANDSHAKED)
1293
{
1294
// Wake up the controller.
1295
_joycon_send_raw(jc->uart, _jc_init_wake, sizeof(_jc_init_wake));
1296
_jc_rcv_pkt(jc); // Clear RX FIFO.
1297
1298
// Do a handshake.
1299
_joycon_send_raw(jc->uart, _jc_init_handshake, sizeof(_jc_init_handshake));
1300
msleep(4);
1301
_jc_rcv_pkt(jc);
1302
retries--;
1303
}
1304
1305
if (jc->state != JC_STATE_HANDSHAKED)
1306
goto out;
1307
1308
// Get info about the controller.
1309
_joycon_send_raw(jc->uart, _jc_init_get_info, sizeof(_jc_init_get_info));
1310
msleep(2);
1311
_jc_rcv_pkt(jc);
1312
1313
if (jc->state != JC_STATE_INFO_PARSED)
1314
goto out;
1315
1316
if (!(jc->type & JC_ID_HORI))
1317
{
1318
// Request 3 megabaud change.
1319
_joycon_send_raw(jc->uart, _jc_init_switch_brate, sizeof(_jc_init_switch_brate));
1320
msleep(2);
1321
_jc_rcv_pkt(jc);
1322
1323
if (jc->state == JC_STATE_BRATE_CHANGED)
1324
{
1325
// Reinitialize uart to 3 megabaud and manual RTS.
1326
uart_init(jc->uart, 3000000, UART_AO_TX_MN_RX);
1327
uart_invert(jc->uart, true, UART_INVERT_TXD | UART_INVERT_RTS);
1328
1329
// Make sure HID is disconnected and check connection. Reply expected after 30ms.
1330
retries = 10;
1331
while (retries && jc->state != JC_STATE_HID_NO_CONN)
1332
{
1333
_joycon_send_raw(jc->uart, _jc_init_hid_disconnect, sizeof(_jc_init_hid_disconnect));
1334
msleep(5);
1335
_jc_rcv_pkt(jc);
1336
retries--;
1337
}
1338
1339
// Was connected before or no response. Do a reinit.
1340
if (jc->state != JC_STATE_HID_NO_CONN)
1341
goto out;
1342
}
1343
1344
// Create HID connection with the new rate.
1345
_joycon_send_raw(jc->uart, _jc_init_hid_connect, sizeof(_jc_init_hid_connect));
1346
msleep(2);
1347
_jc_rcv_pkt(jc);
1348
1349
if (jc->state != JC_STATE_HID_CONN)
1350
goto out;
1351
1352
// Set hid packet rate.
1353
_joycon_send_raw(jc->uart, _jc_init_set_hid_rate, sizeof(_jc_init_set_hid_rate));
1354
msleep(2);
1355
_jc_rcv_pkt(jc);
1356
1357
goto out; // Wait for set hid rate reply.
1358
}
1359
else // Hori. Unset RTS inversion.
1360
uart_invert(jc->uart, false, UART_INVERT_RTS);
1361
}
1362
else
1363
{
1364
// Set Sio NPOR low to configure BOOT0 mode.
1365
gpio_write(GPIO_PORT_CC, GPIO_PIN_5, GPIO_LOW);
1366
usleep(300);
1367
gpio_write(GPIO_PORT_T, GPIO_PIN_0, GPIO_LOW);
1368
gpio_write(GPIO_PORT_CC, GPIO_PIN_5, GPIO_HIGH);
1369
msleep(100);
1370
1371
// Clear RX FIFO.
1372
_jc_rcv_pkt(jc);
1373
1374
// Initialize the controller.
1375
u32 retries = 10;
1376
while (retries && jc->state != JC_STATE_HANDSHAKED)
1377
{
1378
_joycon_send_raw(jc->uart, _sio_init, sizeof(_sio_init));
1379
msleep(5);
1380
_jc_rcv_pkt(jc);
1381
retries--;
1382
}
1383
1384
if (jc->state != JC_STATE_HANDSHAKED)
1385
goto out;
1386
1387
// Set output report version.
1388
retries = 10;
1389
while (retries && jc->state != JC_STATE_HID_CONN)
1390
{
1391
_joycon_send_raw(jc->uart, _sio_set_rpt_version, sizeof(_sio_set_rpt_version));
1392
msleep(5);
1393
_jc_rcv_pkt(jc);
1394
retries--;
1395
}
1396
1397
if (jc->state != JC_STATE_HID_CONN)
1398
goto out;
1399
}
1400
1401
// Initialization done.
1402
jc->state = JC_STATE_INIT_DONE;
1403
jc->connected = true;
1404
1405
out:
1406
jc->last_received_time = get_tmr_ms();
1407
}
1408
}
1409
1410
void jc_init_hw()
1411
{
1412
jc_l.uart = UART_C;
1413
jc_r.uart = UART_B;
1414
1415
jc_l.sio_mode = fuse_read_hw_type() == FUSE_NX_HW_TYPE_HOAG;
1416
jc_gamepad.sio_mode = jc_l.sio_mode;
1417
1418
#if !defined(DEBUG_UART_PORT) || !(DEBUG_UART_PORT)
1419
// Sio Initialization.
1420
if (jc_gamepad.sio_mode)
1421
{
1422
// Enable 4 MHz clock to Sio.
1423
clock_enable_extperiph2();
1424
PINMUX_AUX(PINMUX_AUX_TOUCH_CLK) = PINMUX_PULL_DOWN;
1425
1426
// Configure Sio HOME BUTTON.
1427
PINMUX_AUX(PINMUX_AUX_LCD_GPIO1) = PINMUX_INPUT_ENABLE | PINMUX_TRISTATE | PINMUX_PULL_UP | 1;
1428
gpio_direction_input(GPIO_PORT_V, GPIO_PIN_3);
1429
1430
// Configure Sio IRQ
1431
PINMUX_AUX(PINMUX_AUX_GPIO_PE7) = PINMUX_INPUT_ENABLE | PINMUX_TRISTATE | PINMUX_PULL_UP;
1432
gpio_direction_input(GPIO_PORT_E, GPIO_PIN_7);
1433
1434
// Configure Sio NRST and BOOT0.
1435
PINMUX_AUX(PINMUX_AUX_CAM1_STROBE) = PINMUX_PULL_DOWN | 1;
1436
PINMUX_AUX(PINMUX_AUX_CAM2_PWDN) = PINMUX_PULL_DOWN | 1;
1437
1438
// Set BOOT0 to flash mode. (output high is sram mode).
1439
gpio_direction_output(GPIO_PORT_T, GPIO_PIN_0, GPIO_LOW);
1440
1441
// NRST to pull down.
1442
gpio_direction_input(GPIO_PORT_T, GPIO_PIN_1);
1443
1444
// Configure Sio NPOR.
1445
PINMUX_AUX(PINMUX_AUX_USB_VBUS_EN1) = PINMUX_IO_HV | PINMUX_LPDR | 1;
1446
gpio_direction_output(GPIO_PORT_CC, GPIO_PIN_5, GPIO_LOW);
1447
}
1448
else
1449
{
1450
_jc_power_supply(UART_C, true);
1451
_jc_power_supply(UART_B, true);
1452
}
1453
1454
#if 0 // Already set by hw init.
1455
// Set Joy-Con IsAttached pinmux. Shared with UARTB/UARTC TX.
1456
PINMUX_AUX(PINMUX_AUX_GPIO_PE6) = PINMUX_INPUT_ENABLE | PINMUX_TRISTATE;
1457
PINMUX_AUX(PINMUX_AUX_GPIO_PH6) = PINMUX_INPUT_ENABLE | PINMUX_TRISTATE;
1458
1459
// Set Joy-Con IsAttached mode. Shared with UARTB/UARTC TX.
1460
gpio_config(GPIO_PORT_E, GPIO_PIN_6, GPIO_MODE_GPIO);
1461
gpio_config(GPIO_PORT_H, GPIO_PIN_6, GPIO_MODE_GPIO);
1462
#endif
1463
1464
// Configure pinmuxing for UART B and C.
1465
if (!jc_gamepad.sio_mode)
1466
pinmux_config_uart(UART_B);
1467
pinmux_config_uart(UART_C);
1468
1469
// Enable UART B and C clocks.
1470
if (!jc_gamepad.sio_mode)
1471
clock_enable_uart(UART_B);
1472
clock_enable_uart(UART_C);
1473
1474
jc_init_done = true;
1475
#endif
1476
}
1477
1478
void jc_deinit()
1479
{
1480
if (!jc_init_done)
1481
return;
1482
1483
jc_init_done = false;
1484
1485
if (!jc_gamepad.sio_mode)
1486
{
1487
// Disable power.
1488
_jc_power_supply(UART_B, false);
1489
_jc_power_supply(UART_C, false);
1490
1491
// Send sleep command.
1492
u8 data = HCI_STATE_SLEEP;
1493
if (jc_r.connected && !(jc_r.type & JC_ID_HORI))
1494
{
1495
_jc_send_hid_cmd(&jc_r, JC_HID_SUBCMD_HCI_STATE, &data, 1);
1496
_jc_rcv_pkt(&jc_r);
1497
}
1498
if (jc_l.connected && !(jc_l.type & JC_ID_HORI))
1499
{
1500
_jc_send_hid_cmd(&jc_l, JC_HID_SUBCMD_HCI_STATE, &data, 1);
1501
_jc_rcv_pkt(&jc_l);
1502
}
1503
}
1504
else
1505
{
1506
// Disable Sio NPOR.
1507
gpio_write(GPIO_PORT_CC, GPIO_PIN_5, GPIO_LOW);
1508
1509
// Disable 4 MHz clock to Sio.
1510
clock_disable_extperiph2();
1511
}
1512
1513
// Disable UART B and C clocks.
1514
if (!jc_gamepad.sio_mode)
1515
clock_disable_uart(UART_B);
1516
clock_disable_uart(UART_C);
1517
}
1518
1519
jc_gamepad_rpt_t *joycon_poll()
1520
{
1521
if (!jc_init_done)
1522
return NULL;
1523
1524
_jc_conn_check();
1525
1526
_jc_conn_init(&jc_r);
1527
_jc_conn_init(&jc_l);
1528
1529
_jc_req_status(&jc_r);
1530
_jc_req_status(&jc_l);
1531
1532
_jc_rcv_pkt(&jc_r);
1533
_jc_rcv_pkt(&jc_l);
1534
1535
return &jc_gamepad;
1536
}
1537
1538