Mario Kart Live: Home Circuit: Difference between revisions

CFSworks (talk | contribs)
m Added new information about the "product_code" parameter
Line 1: Line 1:
This page documents the Mario Kart Live: Home Circuit game.
This page documents the Mario Kart Live: Home Circuit game.


Communication with the kart is done directly over local-WLAN via an access point that the game sets up using [[LDN_services|lp2p:app]], making it the first title on retail to use lp2p. The underlying device management uses [[RCD]]. The RCD implementation is in the main-codebin itself, without symbols - however there are strings for this.
Communication with the kart is done directly over local-WLAN via an access point that the game sets up using [[LDN services|lp2p:app]], making it the first title on retail to use lp2p. The underlying device management uses [[RCD]]. The RCD implementation is in the main-codebin itself, without symbols - however there are strings for this.


This is also the first known title on retail which uses stack cookies. This is used by main-codebin, the ssp functionality in sdknso is still not used other than being called from an initialization func. This is implemented in the main-codebin as follows:
This is also the first known title on retail which uses stack cookies. This is used by main-codebin, the ssp functionality in sdknso is still not used other than being called from an initialization func. This is implemented in the main-codebin as follows:
* The global u64 __stack_chk_guard is loaded then saved immediately before {first saved register} on stack, during func entry. During func exit, the global u64 is compared with the cookie on stack, it will call __stack_chk_fail on mismatch. __stack_chk_fail just executes an undefined instruction to trigger a crash.
* The global u64 __stack_chk_guard is loaded then saved immediately before {first saved register} on stack, during func entry. During func exit, the global u64 is compared with the cookie on stack, it will call __stack_chk_fail on mismatch. __stack_chk_fail just executes an undefined instruction to trigger a crash.
* There is no initialization func for __stack_chk_guard, it's just a hard-coded constant: 0xDEADBEEFDEADBEEF. Since it's constant, this renders the stack cookie useless.
* There is no initialization func for __stack_chk_guard, it's just a hard-coded constant: 0xDEADBEEFDEADBEEF. Since it's constant, this renders the stack cookie useless.


RomFs contains only two files:
RomFs contains only two files:
* "data.zip"
* "data.zip"
* "update.pua": This is the firmware update data for the Kart. This is a tar archive. The extracted archive contains "update.pui" and "pui.hash". The latter is a binary 0x100-byte file. The former is another tar archive, the content of that archive is the following:
* "update.pua": This is the firmware update data for the Kart. This is a tar archive. The extracted archive contains "update.pui" and "pui.hash". The latter is a binary 0x100-byte file. The former is another tar archive, the content of that archive is the following:
Line 47: Line 49:


The only changes in the OSS for 1.0.0_1 -> 1.1.0_3 are the following (note that there are more versions between these):
The only changes in the OSS for 1.0.0_1 -> 1.1.0_3 are the following (note that there are more versions between these):
* The following archives were updated: linux-kernel, PsdDriver, rtl8188eu, uboot.
* The following archives were updated: linux-kernel, PsdDriver, rtl8188eu, uboot.
* In the PsdDriver source, the line-ending at the start of various source files was updated.
* In the PsdDriver source, the line-ending at the start of various source files was updated.
Line 58: Line 61:


{| class="wikitable" border="1"
{| class="wikitable" border="1"
!Component
!Manufacturer
!Part
!Notes
|-
|-
! Component
|SoC
! Manufacturer
|Realtek
! Part
|RTD1195 (PB)
! Notes
|Contains hardware H.264 encoder
|-
|-
| SoC
|DDR3 RAM
| Realtek
|Winbond
| RTD1195 (PB)
|W631GG6MB-15
| Contains hardware H.264 encoder
|128 MiB
|-
|-
| DDR3 RAM
|NAND Flash
| Winbond
|Winbond
| W631GG6MB-15
|W29N01HVSINA
| 128 MiB
|128 MiB
|-
|-
| NAND Flash
|WNIC
| Winbond
|Realtek
| W29N01HVSINA
|RTL8188E
| 128 MiB
|Connected to SoC via USB
|-
|-
| WNIC
|MCU
| Realtek
|STMicroelectronics
| RTL8188E
|STM32F
| Connected to SoC via USB
|Responsible for real-time steering and throttle control loops, six-axis IMU acquisition, GPIO, battery status
|-
|-
| MCU
|IMU
| STMicroelectronics
|STMicroelectronics
| STM32F
|LSM6DSL
| Responsible for real-time steering and throttle control loops, six-axis IMU acquisition, GPIO, battery status
|Unconfirmed. Mounted "upside-down" on the PCB: +X=right +Y=back +Z=down. Sensitivities: 4.375 m°/s, 0.244 mg.
|-
|-
| IMU
|Li-ion pouch cell
| STMicroelectronics
|Nintendo branded; OEM unknown
| LSM6DSL
|HAC-038
| Unconfirmed. Mounted "upside-down" on the PCB: +X=right +Y=back +Z=down. Sensitivities: 4.375 m°/s, 0.244 mg.
|3.7V nominal; 1750 mAh capacity
|-
|-
| Li-ion pouch cell
|Camera
| Nintendo branded; OEM unknown
|Unknown
| HAC-038
|Unknown
| 3.7V nominal; 1750 mAh capacity
|Possibly connected via MIPI CSI
|-
| Camera
| Unknown
| Unknown
| Possibly connected via MIPI CSI
|}
|}


Line 115: Line 117:


{| class="wikitable" border="1"
{| class="wikitable" border="1"
!Offset
!Size
!Description
|-
|-
! Offset
|0x0
! Size
|0x10
! Description
|Pairing seed. LP2P PSK is SHA256(seed)
|-
|-
| 0x0 || 0x10 || Pairing seed. LP2P PSK is SHA256(seed)
|0x10
|0x20
|Pairing SSID. Remaining space is filled with zeros.
|-
|-
| 0x10 || 0x20 || Pairing SSID. Remaining space is filled with zeros.
|0x30
|0x2
|Pairing channel. Encoded little-endian. Usually 0.
|-
|-
| 0x30 || 0x2 || Pairing channel. Encoded little-endian. Usually 0.
|0x32
|-
|0xC
| 0x32 || 0xC || Padding bytes; zero.
|Padding bytes; zero.
|}
|}


Line 133: Line 142:


{| class="wikitable" border="1"
{| class="wikitable" border="1"
|+ Summary of ports
|+Summary of ports
!Protocol
!Endpoint
!Port
!Description
![[RCD]] service ID
|-
|-
! Protocol
! colspan="5" |Management
! Endpoint
! Port
! Description
! [[RCD]] service ID
|-
|-
!colspan="5"| Management
| rowspan="5" |TCP
| rowspan="2" |Switch
|5201
|[[RCD#Handshake protocol|RCD handshake]] service (pairing only)
|0x0001
|-
|-
|rowspan="5"| TCP
|5202
|rowspan="2"| Switch
|RCD handshake service (non-pairing only)
| 5201
|0x0001
| [[RCD#Handshake protocol|RCD handshake]] service (pairing only)
| 0x0001
|-
|-
| 5202
| rowspan="3" |Kart
| RCD handshake service (non-pairing only)
|5103
| 0x0001
|"Fuji Control" RCD service
|0x0100
|-
|-
|rowspan="3"| Kart
|5106
| 5103
|"Fuji Pairing" RCD service (pairing only)
| "Fuji Control" RCD service
|0x0102
| 0x0100
|-
|-
| 5106
|5107
| "Fuji Pairing" RCD service (pairing only)
|"Fuji Update" RCD service
| 0x0102
|0x0103
|-
|-
| 5107
|UDP
| "Fuji Update" RCD service
|Kart
| 0x0103
|5004
|Time synchronization
|N/A
|-
|-
| UDP
! colspan="5" |Video
| Kart
| 5004
| Time synchronization
| N/A
|-
|-
!colspan="5"| Video
|UDP
| rowspan="2" |Switch
|5016+kartid
|"LSP" video streaming
|N/A
|-
|-
| UDP
|TCP
|rowspan="2"| Switch
|5032+kartid
| 5016+kartid
|"LSP" control channel
| "LSP" video streaming
|N/A (non-RCD)
| N/A
|-
|-
| TCP
! colspan="5" |Control
| 5032+kartid
| "LSP" control channel
| N/A (non-RCD)
|-
|-
!colspan="5"| Control
| rowspan="2" |UDP
|Kart
|5102
|Teleoperation (throttle, steering, tail light control)
|N/A
|-
|-
|rowspan="2"| UDP
|Switch
| Kart
|5116+kartid
| 5102
|Telemetry
| Teleoperation (throttle, steering, tail light control)
|N/A
| N/A
|-
| Switch
| 5116+kartid
| Telemetry
| N/A
|}
|}


Line 215: Line 223:
Accepts a 0x40-byte payload:
Accepts a 0x40-byte payload:
{| class="wikitable" border="1"
{| class="wikitable" border="1"
!Offset
!Size
!Description
|-
|-
! Offset
|0x00
! Size
|0x20
! Description
|SSID, zero-terminated. ''Home Circuit'' copies this straight from GroupInfo, so uninitialized bytes may follow.
|-
| 0x00
| 0x20
| SSID, zero-terminated. ''Home Circuit'' copies this straight from GroupInfo, so uninitialized bytes may follow.
|-
|-
| 0x20
|0x20
| 0x20
|0x20
| LP2P PSK
|LP2P PSK
|}
|}


Line 240: Line 247:


{| class="wikitable" border="1"
{| class="wikitable" border="1"
!Offset
!Size
!Description
|-
|-
! Offset
|0x0
! Size
|0x1
! Description
|boot_major_version
|-
| 0x0
| 0x1
| boot_major_version
|-
|-
| 0x1
|0x1
| 0x1
|0x1
| boot_minor_version
|boot_minor_version
|-
|-
| 0x2
|0x2
| 0x1
|0x1
| system_major_version
|system_major_version
|-
|-
| 0x3
|0x3
| 0x1
|0x1
| system_minor_version
|system_minor_version
|-
|-
| 0x4
|0x4
| 0x29
|0x29
| Zero-terminated ASCII string which is a 160-bit value in hexadecimal. Appears to be some kind of SHA1.
|Zero-terminated ASCII string which is a 160-bit value in hexadecimal. Appears to be some kind of SHA1.
|}
|}


Line 271: Line 277:


{| class="wikitable" border="1"
{| class="wikitable" border="1"
!Offset
!Size
!Description
|-
|-
! Offset
|0x0
! Size
|0x80
! Description
|Parameter name, zero-padded.
|-
|-
| 0x0
|0x80
| 0x80
|0x2
| Parameter name, zero-padded.
|Value length
|-
|-
| 0x80
|0x82
| 0x2
|0xE
| Value length
|Zero padding to align to 0x10-byte boundary
|-
|-
| 0x82
|0x90
| 0xE
|Varies
| Zero padding to align to 0x10-byte boundary
|Value to set
|-
| 0x90
| Varies
| Value to set
|}
|}


Line 296: Line 301:


{| class="wikitable" border="1"
{| class="wikitable" border="1"
!Offset
!Size
!Description
|-
|-
! Offset
|0x0
! Size
|0x2
! Description
|Fixed 0x0001; unknown purpose.
|-
|-
| 0x0
|0x2
| 0x2
|0x2
| Fixed 0x0001; unknown purpose.
|Telemetry (UDP) port on Switch
|-
|-
| 0x2
|0x4
| 0x2
|0x2
| Telemetry (UDP) port on Switch
|"LSP" control (TCP) port on Switch
|-
|-
| 0x4
|0x6
| 0x2
|0x2
| "LSP" control (TCP) port on Switch
|"LSP" video (UDP) port on Switch
|-
|-
| 0x6
|0x8
| 0x2
|0x8
| "LSP" video (UDP) port on Switch
|Current network time, per nn::time::StandardNetworkSystemClock::GetCurrentTime. (A Unix timestamp with one-second precision.)
|-
| 0x8
| 0x8
| Current network time, per nn::time::StandardNetworkSystemClock::GetCurrentTime. (A Unix timestamp with one-second precision.)
|}
|}


Line 327: Line 331:


{| class="wikitable" border="1"
{| class="wikitable" border="1"
!Offset
!Size
!Description
|-
|0x00
|0x80
|Parameter name, zero-padded.
|}
One of the known parameters is "product_code" which includes the kart's character as 1 byte, and includes a string with the value of the kart's serial number on the bottom of the kart (white-label barcode).
{| class="wikitable" border="1"
!Offset
!Size
!Description
|-
|0x00
|0x80
|Fixed 0x010000; unknown purpose
|-
|-
! Offset
|0x3
! Size
|0x1
! Description
|Fixed 0x03; unknown purpose
|-
|-
| 0x00
|0x7
| 0x80
|0x1
| Parameter name, zero-padded.
|Fixed 0x24; unknown purpose
|-
|0xC
|0x1
|Fixed 0x01; unknown purpose
|-
|0x11
|0x1
|Fixed 0x14; unknown purpose
|-
|0x20
|0x2
|Fixed 0x0100; padding
|-
|0x22
|0x1
|Character ID; 0x01 = Mario, 0x02 = Luigi, unknown if there are other "hidden" characters
|-
|0x23
|0x2
|Fixed 0x0001; padding
|-
|0x24
|0xF
|Kart Serial Number;
'''X''': Nintendo Switch Platform
 
'''Q''': Hardware Identifier for the Mario Kart Live: Home Circuit kart.
 
'''J'''/'''W''': Factory Location; Either '''W''': US, or '''J''': Japan
 
'''10'''/'''14'''/'''40'''/'''70:''' Factory Number
 
'''Last 9 Numbers''': Unit Number
|}
|}
One known parameter is "product_code" which appears to include a string with the value of the white-label barcode on the bottom of the kart. Perhaps this is how ''Home Circuit'' distinguishes between Mario and Luigi.


==== Command 0x04: SetState ====
==== Command 0x04: SetState ====
Line 344: Line 398:


{| class="wikitable" border="1"
{| class="wikitable" border="1"
!Offset
!Size
!Description
|-
|-
! Offset
|0x0
! Size
|0x1
! Description
|Drive mode. 0x01 enables driving controls (and video), 0x00 puts the kart to "sleep."
|-
| 0x0
| 0x1
| Drive mode. 0x01 enables driving controls (and video), 0x00 puts the kart to "sleep."
|-
|-
| 0x1
|0x1
| 0xF
|0xF
| Zero padding.
|Zero padding.
|}
|}


Line 374: Line 427:
=== Teleoperation ===
=== Teleoperation ===


The kart is operated over UDP port 5102. This port is only open when the kart is in [[#Command 0x04: SetState|drive state]]. It expects packets of the form:
The kart is operated over UDP port 5102. This port is only open when the kart is in [[User:Joel Osler/sandbox#Command 0x04: SetState|drive state]]. It expects packets of the form:


{| class="wikitable" border="1"
{| class="wikitable" border="1"
!Offset
!Size
!Description
|-
|-
! Offset
|0x0
! Size
|0x1
! Description
|Throttle; a signed integer. -128 is full-speed reverse, +127 is full-speed forward.
|-
| 0x0
| 0x1
| Throttle; a signed integer. -128 is full-speed reverse, +127 is full-speed forward.
|-
|-
| 0x1
|0x1
| 0x1
|0x1
| Steering; a signed integer. -128 is full left, +127 is full right.
|Steering; a signed integer. -128 is full left, +127 is full right.
|-
|-
| 0x2
|0x2
| 0x1
|0x1
| Brake light control. 0x00 turns the brake light off, 0x01 turns it on. This is independent of throttle.
|Brake light control. 0x00 turns the brake light off, 0x01 turns it on. This is independent of throttle.
|-
|-
| 0x3
|0x3
| 0x1
|0x1
| Pad byte, apparently always zero.
|Pad byte, apparently always zero.
|-
|-
| 0x4
|0x4
| 0x4?
|0x4?
| Little-endian packet counter. Incremented by 1 for each sent packet. Unknown total size.
|Little-endian packet counter. Incremented by 1 for each sent packet. Unknown total size.
|-
|-
| 0x8?
|0x8?
| 0x18?
|0x18?
| Zero padding to bring the total packet size to 0x20.
|Zero padding to bring the total packet size to 0x20.
|}
|}


Line 422: Line 474:


{| class="wikitable" border="1"
{| class="wikitable" border="1"
!Offset
!Size
!Description
|-
|-
! Offset
|0x0
! Size
|0x3
! Description
|Zero padding
|-
| 0x0
| 0x3
| Zero padding
|-
|-
| 0x3
|0x3
| 0x1
|0x1
| How many bars to show in the HUD for battery status (0x00-0x04)
|How many bars to show in the HUD for battery status (0x00-0x04)
|-
|-
| 0x4
|0x4
| 0x1C
|0x1C
| Zero padding, to bring total packet length to 0x20.
|Zero padding, to bring total packet length to 0x20.
|}
|}


Line 449: Line 500:


{| class="wikitable" border="1"
{| class="wikitable" border="1"
!Offset
!Size
! colspan="2" |Description
|-
|-
! Offset
|0x0
! Size
|0x2
!colspan="2"| Description
| colspan="2" |Total IMU samples, as an unsigned integer. This number includes the samples in this packet. It overflows.
|-
| 0x0
| 0x2
|colspan="2"| Total IMU samples, as an unsigned integer. This number includes the samples in this packet. It overflows.
|-
|-
| 0x2
|0x2
| 0x1
|0x1
|colspan="2"| Flags bitfield: PCCCCNNN. P=High-precision, C=Unknown, possibly clipping indicators. N=Number of IMU samples.
| colspan="2" |Flags bitfield: PCCCCNNN. P=High-precision, C=Unknown, possibly clipping indicators. N=Number of IMU samples.
|-
|-
| 0x3
|0x3
| 0x1
|0x1
|colspan="2"| Pad byte; zero.
| colspan="2" |Pad byte; zero.
|-
|-
| 0x4
|0x4
| 0x4
|0x4
|colspan="2"| Microsecond-precision timer, as an unsigned integer.
| colspan="2" |Microsecond-precision timer, as an unsigned integer.
|-
|-
| 0x8
|0x8
| 0x10
|0x10
|colspan="2"| Orientation: s32 qi, qj, qk, qr; gives the coefficients of a quaternion. Units appear to be increments of 2^-30, but quaternions should always be normalized before use.
| colspan="2" |Orientation: s32 qi, qj, qk, qr; gives the coefficients of a quaternion. Units appear to be increments of 2^-30, but quaternions should always be normalized before use.
|-
|-
| 0x18
|0x18
| 0xC
|0xC
| Integrator A position
|Integrator A position
|rowspan="2"| s32 x, y, z; gives an estimate of total displacement since this integrator was reset. Units: 24.59 * 2^-19 m(???)
| rowspan="2" |s32 x, y, z; gives an estimate of total displacement since this integrator was reset. Units: 24.59 * 2^-19 m(???)
|-
|-
| 0x24
|0x24
| 0xC
|0xC
| Integrator B position
|Integrator B position
|-
|-
| 0x30
|0x30
| 0xC
|0xC
| Integrator A velocity
|Integrator A velocity
|rowspan="2"| s32 x, y, z; gives an estimate of total change in velocity since this integrator was reset. Units: ~2^-19 m/s
| rowspan="2" |s32 x, y, z; gives an estimate of total change in velocity since this integrator was reset. Units: ~2^-19 m/s
|-
|-
| 0x3C
|0x3C
| 0xC
|0xC
| Integrator B velocity
|Integrator B velocity
|-
|-
|rowspan="2"| 0x48
| rowspan="2" |0x48
|rowspan="2"| 0xC*(NNN+1)
| rowspan="2" |0xC*(NNN+1)
|rowspan="2"| 1-8 raw IMU samples
| rowspan="2" |1-8 raw IMU samples
| s16 accel_x, accel_y, accel_z; // in units of 0.000244 Gs. 1G is added to accel_z to compensate for gravity.
|s16 accel_x, accel_y, accel_z; // in units of 0.000244 Gs. 1G is added to accel_z to compensate for gravity.
|-
|-
| s16 ang_vel_x, ang_vel_y, ang_vel_z; // in units of 0.004375 degrees per second.
|s16 ang_vel_x, ang_vel_y, ang_vel_z; // in units of 0.004375 degrees per second.
|}
|}


Line 523: Line 573:


ExeFs:
ExeFs:
* Nothing changed besides the usual NPDM update.
* Nothing changed besides the usual NPDM update.


RomFs:
RomFs:
* Only update.pua was updated, the following was changed in the extracted update.pui (pui.hash in update.pua was also updated):
* Only update.pua was updated, the following was changed in the extracted update.pui (pui.hash in update.pua was also updated):
** "config.txt": system_minor_version was changed from "3" to "4".
** "config.txt": system_minor_version was changed from "3" to "4".