This page documents how the Ring Fit Adventure game uses hidbus for using the Ring-Con ExternalDevice, which attaches to Joy-Cons.
The BusType used with GetBusHandle is value 0 or 1.
The ExternalDeviceId is 0x20. After EnableExternalDevice is used successfully during initialization, EnableJoyPollingReceiveMode(flag=true) is used with 4-byte command 0x020101. For cleanup, DisableJoyPollingReceiveMode is used then on success EnableExternalDevice(flag=false) is used.
Commands (input data for EnableJoyPollingReceiveMode / SendAndReceive) start with the u32 cmd, there's generally no data after that. Data in the replies are at least 0x4-bytes, with fields being 4-byte aligned. Reply+0 is the u8 status. When the output size doesn't match the expected size, or status is non-zero, error 0xEDA is returned. When status is non-zero, a func is called with the status value which updates global state.
The status field is not listed below since all replies have it.
CRC
Some commands/replies use CRC(s) for validating the previous N-bytes prior to the CRC. When the CRC is invalid, the app sets an output flag indicating invalid CRC and skips writing output-data to the output-param.
This CRC doesn't use Xor(In/Out)/Reflect(In/Out). The polynomial is 0x8D. This is CRC8.
Commands
Input cmd u32 | Input cmd size | Reply size |
---|---|---|
#0x00020000 | 0x4 | 0x8 |
#0x00020100 | 0x4 | 0x10 |
#0x00020101 | 0x4 | 0x8 |
#0x00020104 | 0x4 | 0x8 |
#0x00020105 | 0x4 | 0x8 |
#0x00020204 | 0x4 | 0x8 |
#0x00020304 | 0x4 | 0x8 |
#0x00020404 | 0x4 | 0x8 |
#0x00020504 | 0x4 | 0x8 |
#0x00020A04 | 0x4 | 0x14 |
#0x00023104 | 0x4 | 0x8 |
#0x00023204 | 0x4 | 0x8 |
#0x04013104 | 0x8 | 0x8 |
#0x04011104 | 0x8 | 0x4 |
#0x04011204 | 0x8 | 0x4 |
#0x04011304 | 0x8 | 0x4 |
#0x10011A04 | 0x14 | 0x4 |
0x00020000
The two u8s at reply+0x4 are copied to an output struct, with the order swapped in the output struct. This is also used directly by various funcs, without the struct.
This is probably some version field or similar? Funcs using this do: if (u8 reply_x5 >= 0x20) {} else {}
0x00020100
Reply:
Offset | Size | Description |
---|---|---|
0x4 | 0x8 | u64 copied to an output struct. |
0xC | 0x4 | u32 copied to an output struct. |
0x00020101
This is used with EnableJoyPollingReceiveMode/GetJoyPollingReceivedData. Reply+0x4 is an u16 which is copied to an output struct, the timestamp is also copied.
0x00020104
Reply+0x4 is the output 16bit value.
See #0x00020A04.
0x00020105
Reply+0x4 is the output u8.
0x00020204
Reply+0x4 is the output 16bit value.
See #0x00020A04.
0x00020304
Reply+0x4 is the output 16bit value.
See #0x00020A04.
0x00020404
Reply+0x4 is the output 16bit value.
See #0x00020A04.
0x00020504
Reply+0x4 is a s16. After successfully using this cmd, this calls the func for #0x00020A04. When that's successful, this does the following with the output from that func and the earlier reply data: s16 tmp = s16_struct_x2 - s16_struct_x0; if (tmp < 0) tmp++; *16bit_outparam = replydata + (tmp>>1);
Then this returns 0, regardless of whether #0x00020A04 was successful.
0x00020A04
The app uses cmd #0x00020000 first. When u8 reply_x5 from that cmd is >=0x20, it proceeds to use cmd 0x00020A04 then returns. Otherwise, the following cmds are used with the output being copied to the output struct, then returns: #0x00020104 (output_struct+0x0), #0x00020204 (output_struct+0x2), #0x00020404 (output_struct+0x6), #0x00020304 (output_struct+0x4).
The func for this is only called by the func implementing #0x00020504.
Reply:
Offset | Size | Description |
---|---|---|
0x4 | 0x2 | 16bit value copied to output_struct+0x0. |
0x8 | 0x2 | 16bit value copied to output_struct+0x2. |
0xC | 0x2 | 16bit value copied to output_struct+0x4. |
0x10 | 0x2 | 16bit value copied to output_struct+0x6. |
0x00023104
Reply:
Offset | Size | Description |
---|---|---|
0x4 | 0x3 | Data, copied to an output struct as an u32. |
0x7 | 0x1 | #CRC over the previous 0x3-bytes. |
0x00023204
The func implementing this in the app is identical to #0x00023104 except for the cmd u32.
0x04013104
The data at cmd+0x4 is all-zero. The app doesn't use reply data here besides the status.
Unlike the other cmds, this checks for output_size==0x4, instead of {output buffer size} (it's unknown whether this is intended).
0x04011104
Cmd+0x4 is the 4-byte input. The output_size is not checked.
See #0x10011A04.
0x04011204
Cmd+0x4 is the 4-byte input. The output_size is not checked.
See #0x10011A04.
0x04011304
Cmd+0x4 is the 4-byte input. The output_size is not checked.
See #0x10011A04.
0x10011A04
The app uses cmd #0x00020000 first. When u8 reply_x5 from that cmd is >=0x20, it proceeds to use cmd 0x10011A04 then returns. Otherwise, the following cmds are used, with the same 4-byte input listed in the below table: #0x04011104 (below cmd+0x4), #0x04011204 (below cmd+0x8), #0x04011304 (below cmd+0xC).
The app doesn't use reply data here besides the status, and the output_size is not checked.
Cmd:
Offset | Size | Description |
---|---|---|
0x4 | 0x2 | Func input param & 0xFFFF |
0x6 | 0x1 | #CRC over the previous 2-bytes. |
0x7 | 0x1 | Padding. |
0x8 | 0x2 | (Func input param >> 16) & 0xFFFF |
0xA | 0x1 | #CRC over the previous 2-bytes. |
0xB | 0x1 | Padding. |
0xC | 0x2 | (Func input param >> 32) & 0xFFFF |
0xE | 0x1 | #CRC over the previous 2-bytes. |
0xF | 0x1 | Padding. |
0x10 | 0x4 | Zeros |