Changes

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".
2

edits