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 command #0x00020101. For cleanup, DisableJoyPollingReceiveMode is used then on success EnableExternalDevice(flag=false) is used.

Commands (input data for EnableJoyPollingReceiveMode / SendAndReceive) start with the u32 cmd. 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
#0x00021104 0x4 0x8
#0x00021204 0x4 0x8
#0x00021304 0x4 0x8
#0x00021A04 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. This seems to be the current sensor state.

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.

0x00021104

This is implemented in the app with the cmd to use being specified via an input param.

See #0x00021A04.

Reply:

Offset Size Description
0x4 0x2 Data, copied to an output struct as an 16bit value.
0x6 0x1 #CRC over the previous 0x3-bytes.

0x00021204

See #0x00021104.

0x00021304

See #0x00021104.

0x00021A04

The app uses cmd #0x00020000 first. When u8 reply_x5 from that cmd is >=0x20, it proceeds to use cmd 0x00021A04. Otherwise, the following cmds are used, with the output being written to the output_struct: #0x00021104 (output_struct+0x0), #0x00021204 (output_struct+0x2), #0x00021304 (output_struct+0x4).

Then, if any of the 16bit output fields in output_struct are set to 0xCAFE, a flag is set to 0x2 (same field used for invalid-CRC, which uses value 0x1 for that). Then this returns 0.

Reply:

Offset Size Description
0x4 0x2 16bit value copied to output_struct+0x0.
0x6 0x1 #CRC over the previous 0x2-bytes.
0x7 0x1 Padding.
0x8 0x2 16bit value copied to output_struct+0x2.
0xA 0x1 #CRC over the previous 0x2-bytes.
0xB 0x1 Padding.
0xC 0x2 16bit value copied to output_struct+0x4.
0xE 0x1 #CRC over the previous 0x2-bytes.
0xF 0x1 Padding.

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 0x2-bytes.
0x7 0x1 Padding.
0x8 0x2 (Func input param >> 16) & 0xFFFF
0xA 0x1 #CRC over the previous 0x2-bytes.
0xB 0x1 Padding.
0xC 0x2 (Func input param >> 32) & 0xFFFF
0xE 0x1 #CRC over the previous 0x2-bytes.
0xF 0x1 Padding.
0x10 0x4 Zeros