/dev/di
- See also: Hardware/Drive Interface
/dev/di is the IOS driver used to control the disc drive. This documentation is mostly based on the most recent version (dated Jun 3 2009 07:49:09 and included in IOS58 and IOS80). Names are based on function names found in Nintendo titles (which print an error message including the name if the Ioctl or Ioctlv fails). DVDLowRequestAudioStatus and DVDLowAudioStream are not found in Wii titles, but the names can be found in debug symbols included in various Gamecube games.
Input structure
The input to all /dev/di commands (other than enable DVD video) is the following struct, which must be sized 0x20 and aligned 4:
struct diCommand { u8 command; // Same as the ioctl number u8 padding1, padding2, padding3; u32 arg1; u32 arg2; u32 arg3; u32 arg4; u32 arg5; u32 arg6; u32 arg7; }
The command field should match the ioctl number (other than with enable DVD video), but if they do not match the ioctl number is the one that is used. The ioctl handler includes a warning stating the following:
(DiIoctl) Note: IOCTL command (0x##) doesn't match block command (0x##) (DiIoctl) Note: This is normal for DVD software before 6-24
This probably means IOCTL numbers were created on June 24th of some year.
Return values
"Internal command result" is used within the DI driver, and is converted to the actual return value by commandResultToIoctlReturn
(at 20200114
).
Return value | Internal command result | Meaning |
---|---|---|
-1 | N/A | Returned by the IPC handler for invalid pointers (not directly part of /dev/di) |
0 | N/A | Returned by enable dvd video (0x8e) on success. |
0x1 | 0x0000 | OK/Success |
0x2 | 0xa000 | Drive error (bit 2 (DEINT) of DISR set) |
0x4 | N/A | Returned after DVDLowWaitForCoverClose (0x79). Also could theoretically be or'd onto most other return values if a flag (20209000 ) is set, but this flag is never set.
|
0x8 | N/A | Not actually returned by any DI ioctls, but Nintendo's libraries will OR this value when breakRequested is true, and the bit is reserved for that purpose. |
0x10 | 0x8000 | Read timed out (took over 15000000µs, i.e. 15 seconds, to respond; this mainly happens when bad parameters result in DILENGTH not being written) |
0x20 | 0x7700 | Security error (e.g. buffer size too small, attempting to enable DVD video, etc) |
0x40 | 0x7800 | Verify error (when call to ES_DiVerify fails when opening a partition) |
0x80 | 0xb000 | Bad argument |
Version history
There are 14 known normal versions (along with 9 matching vWii versions) of the DI driver found in various IOS versions, based on the IOS versions present on NUS and those found on various game discs. These are generalized into 5 version families, based on observable behavior (this is not strictly chronological, presumably as Nintendo was working on multiple versions with the same features at the same time). It is quite likely that there are additional changes not noted here.
The DI driver includes a full set of ES IoctlV wrappers, although it only uses ES_DiVerify and ES_DiVerifyWithTicketView. It also includes instructions for all syscalls, even though most are not used. Both of those change across versions, even though those differences do not actually show up in practice.
Group | Version | 0xE0 on error |
DEADBEEF fatal error |
OoB 0x8D | 0x90 | 0x91 | 0x92 | 0x93 | 0x94 | 0x95 | 0x96 | IOSC | Syscall | ES | Live |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
A | Aug 10 2006 11:24:50 | Yes | No | No | No | No | No | No | No | No | No | No | 0x73 | 0x2f | No |
Aug 13 2006 17:33:44 | No | 0x74 | 0x31 | No | |||||||||||
Oct 5 2006 17:41:21 | Yes | Yes | |||||||||||||
Apr 3 2012 11:52:55 | vWii | ||||||||||||||
B | Jun 8 2007 18:17:09 | Yes | No | Yes | Yes | Yes | Yes | Yes | Yes | Yes | No | No | 0x76 | 0x3d | No |
Jun 8 2007 18:20:10 | No | ||||||||||||||
C | Jul 14 2008 19:25:32 | Yes | No | Yes | Partial | Partial | Partial | Yes | Yes | Yes | No | No | 0x76 | 0x3d | Yes |
Jul 14 2008 19:32:38 | Yes | ||||||||||||||
Jul 24 2008 20:08:45 | Yes | ||||||||||||||
Apr 3 2012 12:00:161 | vWii | ||||||||||||||
Apr 3 2012 12:00:162 | vWii | ||||||||||||||
Apr 3 2012 13:11:40 | vWii | ||||||||||||||
D | Jul 11 2008 14:34:27 | No | Yes | Yes | Partial | Partial | Partial | Yes | Yes | Yes | Yes | No | 0x76 | 0x40 | Yes |
Jul 24 2008 00:30:13 | Yes | ||||||||||||||
Dec 24 2008 13:51:06 | Yes | ||||||||||||||
Apr 3 2012 12:21:34 | vWii | ||||||||||||||
Apr 3 2012 12:31:01 | vWii | ||||||||||||||
E | Nov 24 2008 15:39:09 | No | Yes | Yes | Partial | Partial | Partial | Yes | Yes | Yes | Yes | Yes | 0x79 | 0x42 | No |
Apr 6 2009 17:10:07 | 0x44 | No | |||||||||||||
Jun 3 2009 07:49:09 | 0x45 | Yes | |||||||||||||
Feb 27 2012 14:39:56 | 0x45* | Yes | |||||||||||||
Apr 2 2012 14:03:54 | vWii | ||||||||||||||
Apr 3 2012 12:50:03 | vWii | ||||||||||||||
Apr 3 2012 13:00:48 | vWii |
vWii note
vWii IOS versions are also listed here. See WiiUBrew's title database for a list of vWii IOS versions. There is one vWii version of the DI module for each live regular version. Every vWii version has 2 differences from the corresponding regular version: they time out after 35000000µs instead of 15000000µs (perhaps due to Starlet/Starbuck clock rate differences?[check]), and they check for a disc ID of 401A
in the post-DVDLowReadDiskID read code (treating such a disc as an unencrypted "GAMECUBE or other" disc, instead of a disc that needs to have encryption information read).
Group A
In this version, 0x8D DVDLowUnencryptedRead only accepted the start and end being between 0 and 0x14000, and IOCtls 0x95 DVDLowGetStatusRegister and 0x96 DVDLowGetControlRegister and all IOCtlVs other than 0x8B DVDLowOpenPartition did not exist. 0x87 and 0x7F DVDLowSetSpinupFlag did exist as the current stubs.
This version will issue command 0xE0 to the drive if the error interrupt is set before a command runs (in addition to clearing the interrupt). It prints a message before it issues the command, but not if the issued command fails.
Furthermore, the IOSC syscalls seem to have weird IDs shifted up by 2; IOSC_Decrypt (used by AESdecryptHW) is 0x69 instead of 0x6b, IOSC_GenerateHash is 0x65 instead of 0x67, and IOSC_DeleteObject is 0x5a instead of 0x5c. Presumably, later versions added two syscalls before these[check] (and after 0x52, as that syscall has the same ID in later versions).
For the sake of organization, prelaunch versions are also listed here, although they have slight differences.
Aug 10 2006 11:24:50
Found in IOS4 v3 in the insert startup disc Wii's NAND. Not available on NUS.
0x8D can only be used with start and end offsets between 0x10000 and 0x14001 or bytes 0x40000 through 0x50003. The size alignment check means this doesn't actually allow reading more bytes past the end. This means that the PPC would only have access to the Wii Disc's partitions information and such, and not header bytes beyond 0x20 (the first 0x20 bytes are accessible through 0x70 DVDLowReadDiskID), i.e. the game title and encryption information cannot be accessed.
SHA-1 | 5032764e723e0db7e6d7f434219c9d50289a1cab | ||
---|---|---|---|
Thing | Virtual address | Physical address | Size |
Code (and entry point) | 20200000 | 13540000 | 0x6540 |
Data (ES vars) | 20207000 | 13547000 | 0x140 |
BSS (zero'd) | 20208000 | 13548000 | 0x2BE08 |
Stack | 2022bd40 | ? | 0x8000 |
Protected heap | 20208020 | ? | 0x4000 |
Open heap | 13400000 | ? | 0x18000 |
Aug 13 2006 17:33:44
Found in IOS9 v1 in the insert startup disc Wii's NAND. Not available on NUS.
A hash error in doBlockRead ("Data failed to verify against H0 Hash"/"H0 Hashes failed to verify"/"H1 Hashes failed to verify"/"H2 Hashes failed to verify") or openPartition ("Verifying H3 hashes against H4 hash failed") results in diFatalError being called, while the Aug 10 build simply returns a security error.
0x8D now is able to access data between 0 and 0x14000.
In this version only, 0x71 DVDLowRead can be used with non-secure discs, simply calling doRawDiskRead. In versions both before and after, attempting to do this returns a security error.
Added ES wrappers:
- ES_Sign (0x30, at 202045e0)
- ES_VerifySign (0x31, at 20204650)
SHA-1 | 9dce75d14e01f6efc8d56821c139490792b8b3f9 | ||
---|---|---|---|
Thing | Virtual address | Physical address | Size |
Code (and entry point) | 20200000 | 13580000 | 0x670C |
Data (ES vars) | 20207000 | 13587000 | 0x140 |
BSS (zero'd) | 20208000 | 13588000 | 0x2BE08 |
Stack | 2022bd40 | ? | 0x8000 |
Protected heap | 20208020 | ? | 0x4000 |
Open heap | 13400000 | ? | 0x18000 |
Oct 5 2006 17:41:21
Used in monolithic IOS versions (those prior to IOS28, obviously excluding stubbed IOS versions). As these versions have only a single ELF file for all modules, there is no single hash for the DI driver.
The main thread's priority is now 0x54 instead of 0x50. DVDLowReset now also skips the check for clearing the drive error (in addition to DVDLowRequestError).
openPartition now allocates and reads 0x2c0 bytes for the partition (instead of 0x2bc bytes), meaning the "Data size >> 2" field was not read before.
SHA-1 | Varies | ||
---|---|---|---|
Thing | Virtual address | Physical address | Size |
Code (and entry point) | 20200000 | 13580000 | 0x6704 |
Data (ES vars) | 20207000 | 13587000 | 0x140 |
BSS (zero'd) | 20208000 | 13588000 | 0x2BE08 |
Stack | 2022bd40 | ? | 0x8000 |
Protected heap | 20208020 | ? | 0x4000 |
Open heap | 13400000 | ? | 0x18000 |
Apr 3 2012 11:52:55
Wii U vWii variant of Oct 5 2006 17:41:21. Used in monolithic IOS versions (prior to IOS28). Has the normal vWii changes, and additionally, the string constant /dev/es
is located in a mutable location (at 20207020) instead of at a constant location with the other string constants (as in all other versions).
SHA-1 | Varies | ||
---|---|---|---|
Thing | Virtual address | Physical address | Size |
Code (and entry point) | 20200000 | 13580000 | 0x6718 |
Data (ES vars) | 20207000 | 13587000 | 0x140 |
BSS (zero'd) | 20208000 | 13588000 | 0x2BE08 |
Stack | 2022bd40 | ? | 0x8000 |
Protected heap | 20208020 | ? | 0x4000 |
Open heap | 13400000 | ? | 0x18000 |
Group B
Adds 0x95 DVDLowGetStatusRegister, and adds all of the IOCtlVs (which are also exposed as IOCtls): 0x90 DVDLowGetNoDiscOpenPartitionParams, 0x91 DVDLowNoDiscOpenPartition, 0x92 DVDLowGetNoDiscBufferSizes, 0x93 DVDLowOpenPartitionWithTmdAndTicket, and 0x94 DVDLowOpenPartitionWithTmdAndTicketView. It also allows all 3 ranges in 0x8D DVDLowUnencryptedRead.
The various allocation functions were tweaked; rather than having separate functions for different alignments, they just take an alignment parameter. They also no longer return a bool and modify a parameter, instead just returning a pointer.
Added a warning if the call to clearDriveErrorInterupt fails. In this version, it can theoretically fail (as it sends an actual 0xE0 command), but later versinos keep that message even after they stop sending a command.
Added 5 unused debug functions (starting at 20201a6c) that print out various messages, after the (also unused) functions that print info relating to stack usage. All of these functions continue to exist for the rest of the versions. Removed some other unused debug methods (dumpDiskInfo, a hex dump method, and a method that printed info about a partion, previously starting at 20202510, 20202540, and 202025f4). Removed printIOS_OpenError (20201f28), which was used if /dev/es failed to open. Removed initBytes (20202590) which filled memory with 0xDEADBEEFCAFEBABE.
The function that a hash of some data (located at 20202478 in this version and 20201778 before, and also is the only function that calls IOSC_GenerateHash) changed its fatal error messages for invalid input: "Hash array address is not 64 byte aligned" became "Address of array to be hashed is not 64 byte aligned" and "Hash array length must be >= 64" became "Number of bytes to be hashed must be >= 64". The function was also changed to copy the computed hash to a parameter (always returning true if computation was successful) instead of comparing the computed hash with the parameter (returning false if computation fails or there was a mismatch). New functions were added using this function that verify one (20202554) or multiple hashes (20202584).
doBlockRead prints "(doBlockRead) Data subblock %d failed to verify against H0 Hash" instead of "(doBlockRead) Data failed to verify against H0 Hash" if a hash fails. Note that the subsequent call to diFatalError still uses the old message. Additionally, the coutner for the loop changed direction since it can show up in that message (presumably a compiler optimisation no longer being possible, instead of an actual change).
Partition-related code was split into several functions (and fewer functions are now inlined), due to the addition of functions for the new IoctlVs. Actual behavior seems to be identical, apart from log messages using new function names.
Removed a variable that stored the H3 hashes offset that was never read (previously located at 20233df4). However, they didn't remove an even more useless H3 hashes size variable right after it (previously at 20233df8, now at 2022ddb4), for some reason.
ES_DiVerifyWrapper now supports ticket views in addition to tickets.
Added several ES wrappers, most unused:
- ES_GetStoredContent (0x32 and 0x33, at 20204f34)
- ES_GetStoredTmd (0x34 and 0x35, at 20205120)
- ES_GetSharedContents (0x36 and 0x37, at 20204fe4)
- ES_DeleteSharedContent (0x38, at 20205308)
- ES_DiGetTmd (0x39 and 0x3a, at 202055dc)
- ES_DiVerify_TicketView (0x3b, at 202057f0); this function actually is used.
- ES_SetupStreamKey (0x3c, at 20205b58)
- ES_DeleteStreamKey (0x3d, at 20205c18)
Also:
- ES_DiGetTmdView (0x1a, at 20205668) now uses 0x19 if the size is not specified.
- ES_DiGetTicketView (0x1b, at 20205578 and previously 20204104) now allows the first parameter to be null. This function still is not used.
Jun 8 2007 18:17:09
Used by early builds of certain IOS versions. No current version of IOS uses this build.
- IOS30 prior to v2816 (stubbing)
- IOS31 prior to v3088
- IOS33 prior to v2832 (v1040 only)
- IOS34 prior to v3087 (v1039 only)
- IOS35 prior to v3088 (v1040 only)
- IOS36 prior to v3090 (v1042 only)
- IOS37 prior to v3609 (v2070 only)
This version will issue command 0xE0 to the drive if the error interrupt is set before a command runs, and additionally prints a a second message if that fails in DiIoctl (which exists in all subsequent versions, even those that do not actually issue a command so it cannot ever fail).
SHA-1 | 260be947a08f57f6ef51086427fe222fd4040399 | ||
---|---|---|---|
Thing | Virtual address | Physical address | Size |
Code (and entry point) | 20200000 | 139B0000 | 0x80E0 |
Data (ES vars) | 20209000 | 139B9000 | 0x140 |
BSS (zero'd) | 2020A000 | 139BA000 | 0x2BDC4 |
Stack | 2022ddc4 | ? | 0x8000 |
Protected heap | 2020a020 | ? | 0x4000 |
Open heap | 13600000 | ? | 0x18000 |
Jun 8 2007 18:20:10
Only found in IOS28 version 1288 (which is the first build that split things into modules). This version is not present on NUS, but can be found on the update partition of some discs, such as LEGO Star Wars: The Complete Saga and Marble Saga: Kororinpa. The only difference between the build from the earlier build is that the open heap is at address 0x13800000 (0x9c << 0x15) instead of address 0x13600000 (0x9b << 0x15). This is a 1-byte difference at offset bfc in the file or at address 20200ad4. (There are also differences for the build dates).
SHA-1 | fb308a9a1d9341df9517db155f4383162325dcc0 | ||
---|---|---|---|
Thing | Virtual address | Physical address | Size |
Code (and entry point) | 20200000 | 139B0000 | 0x80E0 |
Data (ES vars) | 20209000 | 139B9000 | 0x140 |
BSS (zero'd) | 2020A000 | 139BA000 | 0x2BDC4 |
Stack | 2022ddc4 | ? | 0x8000 |
Protected heap | 2020A020 | ? | 0x4000 |
Open heap | 13800000 | ? | 0x18000 |
Group C
Removes IOCtlVs 0x90, 0x91, and 0x92 (but they are still accessible as IOCtls). (0x93 and 0x94 remain available as IOCtlVs.)
The thunk function for memcpy are now located between thunks for IOS_FlushDCache and IOSC_GenerateHash (at 20205b80) instead of request_di_interrupt and time_now (at 20205dc0) in group B.
Jul 14 2008 19:25:32
Replaces the Jun 8 build for IOS versions other than IOS37:
- IOS31 starting with v3088
- IOS33 starting with v2832
- IOS34 starting with v3087
- IOS35 starting with v3088
- IOS36 starting with v3090
SHA-1 | 57667279972205462da427535a75a913574f2798 | ||
---|---|---|---|
Thing | Virtual address | Physical address | Size |
Code (and entry point) | 20200000 | 139B0000 | 0x7D74 |
Data (ES vars) | 20208000 | 139B8000 | 0x140 |
BSS (zero'd) | 20209000 | 139B9000 | 0x2BDC4 |
Stack | 2022cdc4 | ? | 0x8000 |
Protected heap | 20209020 | ? | 0x4000 |
Open heap | 13600000 | ? | 0x18000 |
Jul 14 2008 19:32:38
Only found in IOS28 (which is the first build that split things into modules). The only difference between the build from 7 minutes earlier is that the open heap is at address 0x13800000 (0x9c << 0x15) instead of address 0x13600000 (0x9b << 0x15). This is a 1-byte difference at offset 924 in the file or at address 202007fc. (There are technically 2 other differences between the versions, for the build date strings.)
SHA-1 | 92b9a637383729b25fbcb663f2895f66c6d9c987 | ||
---|---|---|---|
Thing | Virtual address | Physical address | Size |
Code (and entry point) | 20200000 | 139B0000 | 0x7D74 |
Data (ES vars) | 20208000 | 139B8000 | 0x140 |
BSS (zero'd) | 20209000 | 139B9000 | 0x2BDC4 |
Stack | 2022cdc4 | ? | 0x8000 |
Protected heap | 20209020 | ? | 0x4000 |
Open heap | 13800000 | ? | 0x18000 |
Jul 24 2008 20:08:45
Only found in IOS38. Note that this also has a version string of $IOSVersion: DIP: 07/24/08 20:08:44 64M $
, probably just due to the two timestamps being determined at separate instants.
Identical to the Jul 14 2008 19:25:32 build apart from the priority of the main thread being set to 0x1b instead of 0x54 (all versions other than this and Jul 24 2008 00:30:13 use 0x54). This results in byte differences at address 20207c2c (file offset 7d54), as well as in some ELF header area (file offset 114), and the timestamps.
SHA-1 | b4cdc54a5912d64f9ef1e516931ab32d64677a9c | ||
---|---|---|---|
Thing | Virtual address | Physical address | Size |
Code (and entry point) | 20200000 | 139B0000 | 0x7D74 |
Data (ES vars) | 20208000 | 139B8000 | 0x140 |
BSS (zero'd) | 20209000 | 139B9000 | 0x2BDC4 |
Stack | 2022cdc4 | ? | 0x8000 |
Protected heap | 20209020 | ? | 0x4000 |
Open heap | 13600000 | ? | 0x18000 |
Apr 3 2012 12:00:16 1
Wii U vWii variant of Jul 14 2008 19:25:32, with the normal vWii changes. Used by IOS31, IOS33, IOS34, IOS35, and IOS36.
SHA-1 | e04f3abe93ca9b9a2518c2ddc3d273e43caed1f8 | ||
---|---|---|---|
Thing | Virtual address | Physical address | Size |
Code (and entry point) | 20200000 | 139B0000 | 0x7D90 |
Data (ES vars) | 20208000 | 139B8000 | 0x140 |
BSS (zero'd) | 20209000 | 139B9000 | 0x2BDC4 |
Stack | 2022cdc4 | ? | 0x8000 |
Protected heap | 20209020 | ? | 0x4000 |
Open heap | 13600000 | ? | 0x18000 |
Apr 3 2012 12:00:16 2
Wii U vWii variant of Jul 24 2008 20:08:45, with the normal vWii changes. Used by IOS38 exclusively. The only difference from the other build with the same timestamp is the main thread's priority (which was changed to 0x1b from 0x54). This difference appears in memory at address 20207c48 (offset 7d70) and in the ELF header at file offset 114. It's rather odd that the timestamp was not updated despite that change.
SHA-1 | b62ad5ea5a2e03d2fb73e93dad1d34c102ec357a | ||
---|---|---|---|
Thing | Virtual address | Physical address | Size |
Code (and entry point) | 20200000 | 139B0000 | 0x7D90 |
Data (ES vars) | 20208000 | 139B8000 | 0x140 |
BSS (zero'd) | 20209000 | 139B9000 | 0x2BDC4 |
Stack | 2022cdc4 | ? | 0x8000 |
Protected heap | 20209020 | ? | 0x4000 |
Open heap | 13600000 | ? | 0x18000 |
Apr 3 2012 13:11:40
Wii U vWii variant of Jul 14 2008 19:32:38, with the normal vWii changes. Used by IOS28 exclusively. The open heap is at 0x13800000 instead of 0x13600000, due to a 1-byte change at address 202007fc or offset 924.
MD5 | 1d1723825d53b5389ec80c89c8a3aa06701ae07d | ||
---|---|---|---|
Thing | Virtual address | Physical address | Size |
Code (and entry point) | 20200000 | 139B0000 | 0x7D90 |
Data (ES vars) | 20208000 | 139B8000 | 0x140 |
BSS (zero'd) | 20209000 | 139B9000 | 0x2BDC4 |
Stack | 2022cdc4 | ? | 0x8000 |
Protected heap | 20209020 | ? | 0x4000 |
Open heap | 0x13800000 | ? | 0x18000 |
Group D
Adds 0x96 DVDLowGetControlRegister. Note that although these versions are earlier than group C, they have more features.
diFatalError attempts to write 0xdeadbeef to 0xffff0000 before it calls CancelThread and enters an infinite loop. The message was also changed from "(diFatalError) Fatal error in DI driver: %s\nExiting\n" to "(diFatalError) *** DI FATAL ERROR: %s\nExiting\n". Something about this changed compiler or decompiler behavior, changing the way uses of that function affect code flow which makes some changes harder to spot and creates a lot of changes that aren't actually changes.
clearDriveErrorInterupt and doWaitForCoverClose were moved to be before handleDiCommand instead of after (group C has them at 2020146c/2020149c, and now they are at 20200b80/20200b98). Furthermore, clearDriveErrorInterupt no longer issues a 0xE0 command to the drive, and always returns success (however, the rest of the code still assumes it can fail, printing a warning in that case).
Improved error messages in doBlockRead. The debug messages for when a hash failed now also print the first parameter as a pointer (e.g. "(doBlockRead) Data subblock %d failed to verify against H0 Hash (%08x)"). The fatal error message for the first case was changed from "Data failed to verify against H0 Hash" to "Data subblock failed to verify against H0 Hash" (the other messages of the form "H0 Hashes failed to verify" were not changed). Additionally, if the call to doRawDiskRead fails, the message "(doBlockRead) doRawDiskRead failed, rc=%d\n" is printed (previously nothing was printed); the return value is still that of doRawDiskRead in that case. Lastly, when a hash fails, the parameter is memset with value 0xA5 prior to calling diFatalError.
The implementation of DVDLowRead no longer calls doReadHashEncryptedState if it hasn't been called before (before it checks if the disc is a secure disc). It never needed to anyways, as it is called after DVDLowReadDiskID, which *must* be called first. It was also moved to be before doNonConfirmingDiscRead and doReadHashEncryptedState (at 202029e8) instead of after them (at 20202950).
doReadHashEncryptedState only considers a disc as secure (and only enables hashing) if both disable hashing (byte 0x60 of the Wii Disc) and disable encryption (byte 0x61) are false (and also acts as if hashing were disabled if encryption is disabled). Previously, only the hashing byte controlled whether the disc was secure and hashing was enabled.
The 0x18000-byte H3 hashes buffer is cleared by commonOpenPartition with value 0xA5 if a non-encrypted disc is used (disc encryption at byte 0x61 on the Wii Disc is 0 and the partition's H3 offset is also 0; disabling encryption but having an H3 offset set will result in a fatal error in both this version and earlier versions).
Some more ES wrappers were added:
- 0x3E (at 2020525c)
- ES_GetV0TicketFromView (0x40, at 20205068)
Jul 11 2008 14:34:27
Used by several IOS builds:
- IOS37 starting with v2816
- IOS50 v4889 (v5120 is a stub)
- IOS51 v4633 (v4864 is a stub)
- IOS52 v5661 (v5888 is a stub)
- IOS53 (all versions)
- IOS55 (all versions)
Note that this also has a version string of $IOSVersion: DIP: 07/11/08 14:34:26 64M $
, probably just due to the two timestamps being determined at separate instants.
SHA-1 | bff35f53a0ed9f69b15a224552fcc73372308099 | ||
---|---|---|---|
Thing | Virtual address | Physical address | Size |
Code (and entry point) | 20200000 | 139B0000 | 0x8088 |
Data (ES vars) | 20209000 | 139B9000 | 0x140 |
BSS (zero'd) | 2020A000 | 139BA000 | 0x2BDC4 |
Stack | 2022ddc4 | ? | 0x8000 |
Protected heap | 2020a020 | ? | 0x4000 |
Open heap | 13600000 | ? | 0x18000 |
Jul 24 2008 00:30:13
Only found in IOS48.
Identical to the Jul 11 2008 14:34:27 build apart from the priority of the main thread being set to 0x1b instead of 0x54 (all versions other than this and Jul 24 2008 20:08:45 use 0x54). This results in byte differences at address 20207f40 (file offset 8068), as well as in some ELF header area (file offset 114), and the timestamps.
SHA-1 | 0fc5a88a327ee2b5c4ce3dc05faf8c7ef3bbcc1b | ||
---|---|---|---|
Thing | Virtual address | Physical address | Size |
Code (and entry point) | 20200000 | 139B0000 | 0x8088 |
Data (ES vars) | 20209000 | 139B9000 | 0x140 |
BSS (zero'd) | 2020A000 | 139BA000 | 0x2BDC4 |
Stack | 2022ddc4 | ? | 0x8000 |
Protected heap | 2020a020 | ? | 0x4000 |
Open heap | 13600000 | ? | 0x18000 |
Dec 24 2008 13:51:06
Used in all versions of IOS41, IOS43, IOS45, and IOS46.
Rebuild with no changes (other than the timestamps) of Jul 11 2008 14:34:27.
SHA-1 | d254a265d25d96723a566e6f877f5df05a645699 | ||
---|---|---|---|
Thing | Virtual address | Physical address | Size |
Code (and entry point) | 20200000 | 139B0000 | 0x8088 |
Data (ES vars) | 20209000 | 139B9000 | 0x140 |
BSS (zero'd) | 2020A000 | 139BA000 | 0x2BDC4 |
Stack | 2022ddc4 | ? | 0x8000 |
Protected heap | 2020a020 | ? | 0x4000 |
Open heap | 13600000 | ? | 0x18000 |
Apr 3 2012 12:21:34
Wii U vWii variant of Jul 14 2008 19:32:38, with the normal vWii changes. Used by IOS37, IOS53, and IOS55.
SHA-1 | e7a0824785268df455d56c1803620eff180d6556 | ||
---|---|---|---|
Thing | Virtual address | Physical address | Size |
Code (and entry point) | 20200000 | 139B0000 | 0x80A4 |
Data (ES vars) | 20209000 | 139B9000 | 0x140 |
BSS (zero'd) | 2020A000 | 139BA000 | 0x2BDC4 |
Stack | 2022ddc4 | ? | 0x8000 |
Protected heap | 2020a020 | ? | 0x4000 |
Open heap | 13600000 | ? | 0x18000 |
Apr 3 2012 12:31:01
Wii U vWii variant of Jul 14 2008 19:32:38, with the normal vWii changes. Used by IOS41, IOS43, IOS45, IOS46, and IOS48 (the original version was only used by IOS48, with the rest using Dec 24 2008 13:51:06). The main thread has priority 0x1b instead of 0x54, resulting in byte differences at address 20207f5c (offset 8084) and in the ELF header (offset 114), as well as the timestamps. Was the priority change for versions other than IOS48 intentional, with the other modules being updated to compensate[check]?
SHA-1 | 8835422c143de4b359fea4a0a56aef9386caa53d | ||
---|---|---|---|
Thing | Virtual address | Physical address | Size |
Code (and entry point) | 20200000 | 139B0000 | 0x80A4 |
Data (ES vars) | 20209000 | 139B9000 | 0x140 |
BSS (zero'd) | 2020A000 | 139BA000 | 0x2BDC4 |
Stack | 2022ddc4 | ? | 0x8000 |
Protected heap | 2020a020 | ? | 0x4000 |
Open heap | 13600000 | ? | 0x18000 |
Group E
The code that checks H0/H1/H2 hashes was moved into the kernel, using syscall 0x77 (IOSC_CheckDiHashes). H3 hashes are still present. It's not clear if the actual hashing behavior changed[check].
Wrappers for ES IoctlVs 0x41 (at 20205ba4 in 2008 and 20205c58 in 2009 and 2012) and 0x42 (at 20205b44 in 2008 and 20205bf8 in 2009 and 2012) were added.
Instructions for syscalls 0x77, 0x78, and 0x79 were added, though only 0x77 is used. Note that these are out of order; 0x77 is at the end of the list at 202042d0 while 0x78 and 0x79 are wedged between 0x5a and 0x5b at 202041e0 for some reason.
Nov 24 2008 15:39:09
Used in the first builds of a few IOS versions:
SHA-1 | 96b035dafcfaf826d1772abd07b8014aed15035f | ||
---|---|---|---|
Thing | Virtual address | Physical address | Size |
Code (and entry point) | 20200000 | 139B0000 | 0x7F00 |
Data (ES vars) | 20208000 | 139B8000 | 0x140 |
BSS (zero'd) | 20209000 | 139B9000 | 0x2BDC4 |
Stack | 2022cdc4 | ? | 0x8000 |
Protected heap | 20209020 | ? | 0x4000 |
Open heap | 13600000 | ? | 0x18000 |
Apr 6 2009 17:10:07
Used exclusively by IOS56 v5146, which is not found on NUS (but can be found on e.g. Guitar Hero 5).
No changes to the actual driver code from the Nov 24 2008 version, but some of the ES wrapper code changed. These also cause string constants to shift, which makes byte comparisons slightly annoying. The changes:
- ES_AddTicket (20204514, Ioctlv 0x01) no longer always uses a size of 0x2a4, but will instead use 0x2a4 plus a 32-bit size at offset 0x2a8 if the byte at offset 0x1bc is nonzero.
- ES_GetTicketFromView (20204fc0, Ioctlvs 0x43 and 0x44) was added
SHA-1 | 9a915fd77389a79c7fa516e4aac4e30e4e1174ad | ||
---|---|---|---|
Thing | Virtual address | Physical address | Size |
Code (and entry point) | 20200000 | 139B0000 | 0x7FB4 |
Data (ES vars) | 20208000 | 139B8000 | 0x140 |
BSS (zero'd) | 20209000 | 139B9000 | 0x2BDC4 |
Stack | 2022cdc4 | ? | 0x8000 |
Protected heap | 20209020 | ? | 0x4000 |
Open heap | 13600000 | ? | 0x18000 |
Jun 3 2009 07:49:09
Used in several IOS versions, and also updated versions of builds that used the Nov 24 2008 version.
- IOS56 starting with v5405
- IOS57 starting with v5661
- IOS58 in all versions
- IOS59 in all versions
- IOS61 starting with v5405
- IOS70 v6687 (v6912 is a stub)
- IOS80 in all versions
No changes to the actual driver code from the Nov 24 2008 version, but some of the ES wrapper code changed. These also cause string constants to shift, which makes byte comparisons slightly annoying. The changes:
- Ioctlv 0x45 (20205cb8) was added
SHA-1 | 4e04e88ec7250de84a1e788ae69fdad9351330a8 | ||
---|---|---|---|
Thing | Virtual address | Physical address | Size |
Code (and entry point) | 20200000 | 139B0000 | 0x7FF0 |
Data (ES vars) | 20208000 | 139B8000 | 0x140 |
BSS (zero'd) | 20209000 | 139B9000 | 0x2BDC4 |
Stack | 2022cdc4 | ? | 0x8000 |
Protected heap | 20209020 | ? | 0x4000 |
Open heap | 13600000 | ? | 0x18000 |
Feb 27 2012 14:39:56
Used exclusively by IOS62. The wrappers for ES IoctlVs 0x41 and 0x42 were changed slightly, both requiring that the second parameter is not greater than 0x14 instead of 0x13. This results in differences at addresses 20205c16 (0x42) and 20205c76 (0x41) (file offsets 5d30 and 5d9e), in addition to the timestamp change.
SHA-1 | d5dfeb42909a20453c0f574e9a8c41f50792bf8f | ||
---|---|---|---|
Thing | Virtual address | Physical address | Size |
Code (and entry point) | 20200000 | 139B0000 | 0x7FF0 |
Data (ES vars) | 20208000 | 139B8000 | 0x140 |
BSS (zero'd) | 20209000 | 139B9000 | 0x2BDC4 |
Stack | 2022cdc4 | ? | 0x8000 |
Protected heap | 20209020 | ? | 0x4000 |
Open heap | 13600000 | ? | 0x18000 |
Apr 2 2012 14:03:54
Wii U vWii variant of Jun 3 2009 07:49:09, with the normal vWii changes. Also has the changes to 0x41 and 0x42 from Feb 27 2012 14:39:56. Used by IOS56, IOS57, IOS58, and IOS80.
SHA-1 | 43cbc9d451df6296214347f8a33349b2dda843f0 | ||
---|---|---|---|
Thing | Virtual address | Physical address | Size |
Code (and entry point) | 20200000 | 139B0000 | 0x800C |
Data (ES vars) | 20209000 | 139B9000 | 0x140 |
BSS (zero'd) | 2020A000 | 139BA000 | 0x2BDC4 |
Stack | 2022ddc4 | ? | 0x8000 |
Protected heap | 2020a020 | ? | 0x4000 |
Open heap | 13600000 | ? | 0x18000 |
Apr 3 2012 12:50:03
A second Wii U vWii variant of Jun 3 2009 07:49:09, with the normal vWii changes (and no other changes other than the timestamp). Also has the changes to 0x41 and 0x42 from Feb 27 2012 14:39:56. Used exclusively by the three versions of IOS59.
SHA-1 | e961f817c53ad3d87af96635df99bc6ee70ed056 | ||
---|---|---|---|
Thing | Virtual address | Physical address | Size |
Code (and entry point) | 20200000 | 139B0000 | 0x800C |
Data (ES vars) | 20209000 | 139B9000 | 0x140 |
BSS (zero'd) | 2020A000 | 139BA000 | 0x2BDC4 |
Stack | 2022ddc4 | ? | 0x8000 |
Protected heap | 2020a020 | ? | 0x4000 |
Open heap | 13600000 | ? | 0x18000 |
Apr 3 2012 13:00:48
Wii U vWii variant of Feb 27 2012 14:39:56, with the normal vWii changes (and no other changes other than the timestamp, though to be clear it does have the changed 0x41 and 0x42). Used exclusively by the three versions of IOS62.
SHA-1 | 86bec5ad6815c2bf1f154690f2abd0e6141f0f8b | ||
---|---|---|---|
Thing | Virtual address | Physical address | Size |
Code (and entry point) | 20200000 | 139B0000 | 0x800C |
Data (ES vars) | 20209000 | 139B9000 | 0x140 |
BSS (zero'd) | 2020A000 | 139BA000 | 0x2BDC4 |
Stack | 2022ddc4 | ? | 0x8000 |
Protected heap | 2020a020 | ? | 0x4000 |
Open heap | 13600000 | ? | 0x18000 |
IoctlVs
An incorrect size or alignment, or an incorrect in count or out count, will result in a return value of 0x80. Commands not listed here will cause a hang.
IoctlVs and Ioctls both go to one shared function that actually handles them which takes a diCommand; IoctlV is used when there are multiple pointers being passed to simplify conversion from virtual to physical addresses (which happens on the PPC side in both Nintendo's and libogc's IoctlV function). The IoctlVs will modify the passed diCommand to have additional parameters, noted in yellow. This means that in some cases the Ioctl can be used directly.
0x8B DVDLowOpenPartition
Opens a partition, including verifying it through /dev/es (the resulting error code will be written to the specified location, even if it is 0). DVDLowReadDiskID needs to have been called beforehand.
Returns 0x80 if DVDLowReadDiskID has not been called, or a partition is already open, 0x20 if allocations fail or the partition does not pass some DI security checks (valid cert offset, valid tmd offset, presence of hashes, presense of tmd), and 0x40 for an ES error.
This command will clear the error interrupt if it is set.
Index | Name | Direction | Size | Alignment |
---|---|---|---|---|
0 | Command | In | 0x20 | 4 |
1 | Ticket (optional) | In | 0x2a4 if present, not checked if absent (should be 0) | 32 |
2 | Shared certs (optional) | In | Arbitrary if present, not checked if absent (should be 0) | 32 |
3 | TMD | Out | 0x49e4 | 32 |
4 | ES Error | Out | 0x20 (of which only the first 4 bytes are used) | 4 |
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x8B) |
4 | off_t | Partition offset |
8 | Ticket* | Ticket |
12 | size_t | Shared certs size |
16 | u8* | Shared certs |
20 | TMD* | tmd |
24 | u32* | ES error output |
0x90 DVDLowGetNoDiscOpenPartitionParams
Dummied out on all current IOS versions; always returns 0x80. However, still usable as an Ioctl (which is probably unintended). System menu 4.3U (among other titles) still has a PPC-side implementation (815504c4) but this is not used (the higher-level function that calls it is gone due to not being referenced). The only version that has an implementation of it is Jun 8 2007 18:17:09, which is no longer used by any IOS version.
Index | Name | Direction | Size | Alignment |
---|---|---|---|---|
0 | Command | In | 0x20 | 4 |
1 | TMD size pointer (not used) | In | 4 | 4 (32 on PPC side) |
2 | Shared certs size pointer (not used) | In | 4 | 4 (32 on PPC side) |
3 | Ticket | Out | 0x2A4 | 32 |
4 | TMD size pointer (again; aliases with previous pointer) | Out | 4 | 4 (32 on PPC side) |
5 | TMD | Out | TMD size, from before (i.e. *vector[4].data) | 32 |
6 | Shared certs size pointer (again; aliases with the previous pointer) | Out | 4 | 4 (32 on PPC side) |
7 | Shared certs | Out | Shared certs size, from before (i.e. *vector[6].data) | 32 |
8 | Partition data offset pointer | Out | 4 | 4 (32 on PPC side) |
9 | H3 hashes | Out | 0x18000 | 32 |
After validating sizes and alignments, the params field of the command is set to a stack-allocated structure:
struct nodiscopenparams { undefined4 unused1; ticket * ticket; // Set to vector[3].data undefined4 unused2; size_t tmdSize; // Set to *vector[4].data tmd * tmd; // Set to vector[5].data size_t sharedCertsSize; // Set to *vector[6].data byte * sharedCerts; // Set to vector[7].data off_t partitionDataOffset; // Set to partitionOffset + partition->dataOffset after working h3buffer * h3Hashes; // set to vector[9].data }
After executing at a lower level (see §0x90 DVDLowGetNoDiscOpenPartitionParams ioctl), tmdSize, sharedCertsSize, and partitionDataOffset are written back to vectors 4, 6, and 8 respectively. (If an error occurs, all three are instead set to 0.)
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x90) |
4 | off_t | Partition position (>> 2) |
8 | nodiscopenparams* | params |
0x91 DVDLowNoDiscOpenPartition
Dummied out on all current IOS versions; always returns 0x80. However, still usable as an Ioctl (which is probably unintended). No PPC-side code exists for this command in any known title (it was never called, even in an unreachable in practice way, so the function was optimized out). The only version that has an implementation of it is Jun 8 2007 18:17:09, which is no longer used by any IOS version.
Index | Name | Direction | Size | Alignment |
---|---|---|---|---|
0 | Command | In | 0x20 | 4 |
1 | Ticket | In | 0x2a4 | 32 |
2 | TMD | In | Not checked | 32 |
3 | Shared certs | In | Not checked | 32 |
4 | H3 hashes | In | 0x18000 | 32 |
5 | ES Error | Out | 4 (properly sized, unlike with regular open partition) | 4 |
After validating sizes and alignments, the params field of the command is set to a stack-allocated structure:
struct nodiscopenparams { undefined4 unused1; ticket * ticket; // Set to vector[1].data undefined4 unused2; size_t tmdSize; // Set to vector[2].size tmd * tmd; // Set to vector[2].data size_t sharedCertsSize; // Set to vector[3].size byte * sharedCerts; // Set to vector[3].data off_t partitionDataOffset; // Set by command h3buffer * h3Hashes; // set to vector[4].data }
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x91) |
4 | off_t | Partition data offset |
4 | nodiscopenparams* | params |
8 | u32 * | ES error output (vector[5].data) |
0x92 DVDLowGetNoDiscBufferSizes
Dummied out on all current IOS versions; always returns 0x80. However, still usable as an Ioctl (which is probably unintended). System menu 4.3U (among other titles) still has a PPC-side implementation (815502b8) but this is not used (the higher-level function that calls it is gone due to not being referenced). The only version that has an implementation of it is Jun 8 2007 18:17:09, which is no longer used by any IOS version.
Index | Name | Direction | Size | Alignment |
---|---|---|---|---|
0 | Command | In | 0x20 | 4 |
1 | TMD size pointer | Out | 4 | 4 (32 on ppc side) |
2 | Shared certs size pointer | Out | 4 | 4 (32 on ppc side) |
Both pointers must be non-zero. The ioctlv fills in additional parameters on the command.
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x92) |
4 | off_t | Partition position (>> 2) |
8 | u32 * | TMD Size out |
12 | u32 * | Cert Chain Size Out |
0x93 DVDLowOpenPartitionWithTmdAndTicket
Opens a partition, including verifying it through /dev/es (the resulting error code will be written to the specified location, even if it is 0). DVDLowReadDiskID needs to have been called beforehand. This function takes an already-read TMD and can take an already-read ticket, which means it can be faster since the ticket does not need to be read from the disc.
Returns 0x80 if DVDLowReadDiskID has not been called, or a partition is already open, 0x20 if allocations fail or the partition does not pass some DI security checks (valid cert offset, valid tmd offset, presence of hashes), and 0x40 for an ES error.
This command will clear the error interrupt if it is set.
Index | Name | Direction | Size | Alignment |
---|---|---|---|---|
0 | Command | In | 0x20 | 4 |
1 | Ticket (optional) | In | 0x2a4 if present, not checked if absent (should be 0) | 32 |
2 | TMD (required) | In | Arbitrary | 32 |
3 | Shared certs (optional) | In | Arbitrary | 32 |
4 | ES Error | Out | 0x20 (of which only the first 4 bytes are used) | 4 |
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x93) |
4 | off_t | Partition offset |
4 | runopenpartparam* | params |
8 | u32 * | ES error output |
After running, the first argument is a pointer to a (stack-allocated) structure used by this and 0x94:
struct runopenpartparam { off_t position; ticket * ticket; // Only used by 0x93 ticketview * ticketview; // Only used by 0x94 size_t tmdSize; tmd * tmd; size_t sharedCertsSize; u8 * sharedCerts; }
0x94 DVDLowOpenPartitionWithTmdAndTicketView
Opens a partition, including verifying it through /dev/es (the resulting error code will be written to the specified location, even if it is 0). DVDLowReadDiskID needs to have been called beforehand. This function takes an already-read TMD and can take an already-read ticket ticket view, which means it can be faster since the ticket does not need to be read from the disc.
Returns 0x80 if DVDLowReadDiskID has not been called, or a partition is already open, 0x20 if allocations fail or the partition does not pass some DI security checks (valid cert offset, valid tmd offset, presence of hashes), and 0x40 for an ES error.
This command will clear the error interrupt if it is set.
Index | Name | Direction | Size | Alignment |
---|---|---|---|---|
0 | Command | In | 0x20 | 4 |
1 | Ticket View (optional) | In | 0x98 if present, not checked if absent (should be 0) | 32 |
2 | TMD (required) | In | Arbitrary | 32 |
3 | Shared certs (optional) | In | Arbitrary | 32 |
4 | ES Error | Out | 0x20 (of which only the first 4 bytes are used) | 4 |
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x94) |
4 | off_t | Partition offset |
4 | runopenpartparam* | params (see above) |
8 | u32 * | ES error output |
Ioctls
Using a number not listed here results in a return value of 0x80. The input buffer must be sized 0x20 (except for command 0x8E). Unless otherwise noted, commands return 1 on success.
Ioctls are implemented in two functions: DiIoctl and handleDiCommand. handleDiCommand is actually also used by IoctlV as well, and other than for DVDLowOpenPartition, IoctlV commands are accidentally exposed as Ioctls as well. The following commands are implemented in DiIoctl:
Commands implemented in DiIoctl |
---|
|
Commands will clear the error interrupt by writing bit 2 of DISR if it is set after execution. Furthermore, the same check happens before execution, but this should generally not happen barring other code directly writing to the DI registers (a warning is logged in this case). These checks happen in handleDiCommand, so commands implemented in DiIoctl are not affected; additionally the second check is skipped for 0x8A DVDLowReset and 0xE0 DVDLowRequestError. In some versions, the second check will also issue a request error command to the drive (which, as a side effect, clears the error in the drive itself, which would break a second DVDLowRequestError — that explains why it is skipped).
If an output buffer size check fails, DIMAR and DILENGTH will not be written, and there is code that sets the return value to 0x20. However, the driver still attempts to start the transfer, which will fail due to not writing DILENGTH (which should have counted back down 0 after any previous successful transfer[check]); this will result in an eventual timeout and returning of 0x10[check].
0x12 DVDLowInquiry
Retrieves information about the drive verison; see yagcd §5.7.3.1 for more info.
The output buffer size must be ≥ 0x20, or DIMAR and DILENGTH will not be written. It must also be 32-bit aligned, or else the driver will hang.
Note that YAGCD is incorrect and there is actually one additional byte after the drive date, apparently indicating the version. This is not new to the Wii.
DICMDBUF0 = 0x12000000 DILENGTH = 0x20 DIMAR = outbuf DICMDBUF1 = 0 DICR = TSTART | DMA
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x12) |
0x70 DVDLowReadDiskID
Reads the current disc ID and initializes the drive. Many other commands will not work before this (either by explicitly checking, or due to the drive returning error 0x05xxxxxx).
This command cannot be used while the drive interface is resetting (if syscall 0x46 syscall_check_di_reset returns true); in which case it will return 0x80. 0x80 will also be returned if the output buffer is not 32-byte aligned.
The output buffer size must be ≥ 0x20, or DIMAR and DILENGTH will not be written.
DICMDBUF0 = 0xA8000040 DICMDBUF1 = 0 DICMDBUF2 = 0x20 DILENGTH = 0x20 DIMAR = dest DICR = TSTART | DMA
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x70) |
After this command has finished reading, the driver will also look at the 4 bytes at offset 0x18 in the output (i.e. outbuf[6] if outbuf is a u32 array) for the Wii magicword 0x5D1C9EA3 to determine if it is a Wii disc. If it is a Wii disc, and it has not already read it, it will read 0x44 (padded to 0x60) bytes starting at byte 0x20 (i.e. the game title, and the disable hashing and disable encryption flags). Thus, after the first call, DVDLowGetLength will return 0x60, and on later calls it will return 0x20.
0x71 DVDLowRead
Reads and decrypts disc data. This command can only be used if hashing and encryption are enabled for the disc. DVDLowOpenPartition needs to have been called before for the keys to be read.
The output buffer has no requirements on alignment, but will perform better if 32-byte aligned since it can avoid a copy from a buffer within the driver. Similarly, the offset and size can be any value, but ones that are sector-aligned (sizes that are multiples of 0x7C00 and offsets that are multiples of 0x1F00) avoid copies for the first and/or last sector that needs to be read and decrypted. Each individual sector is read using command 0xA8.
This command immediately returns 0x20 if the buffer is too small, and also returns 0x20 if something went wrong with decryption or hashing and 2 for a drive error.
If everything completed successfully, the last length value used by DVDLowGetLength is set to offset (almost certainly a mistake on Nintendo's end). Otherwise, it is set to 0.
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x71) |
4 | size_t | Size (bytes) |
8 | off_t | Offset (bytes >> 2) |
0x79 DVDLowWaitForCoverClose
Waits for a disc to be inserted; if there is already a disc inserted, it must be removed first. This command does not time out; if no disc is inserted, it will wait forever. (As such, I'm not entirely sure how it can be cancelled; I assume but have not checked that the Wii Fit Channel uses this when waiting for the Wii Fit disc to be inserted, but that can be canceled...)
Continuously waits for a DI interrupt, and when it receives one it checks for the cover interrupt (bit 2 of DICVR); if it is set it then checks if the cover is closed (bit 0 of DICVR) and if sufficient time has ellapsed. It also clears the TC and error interrupts should they occur.
The output buffer is not used, and it may be null. Its size is not checked.
On completion, returns 0x4.
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x79) |
0x7A DVDLowGetCoverRegister
Stores the current value of DICVR into outbuf, which must be at least 4 bytes in size (or else 0x20 is returned). Note that Nintendo titles also refer to this as DVDLowPrepareCoverRegister, but that function simply asynchronously reads it into game memory so that it can be accessed by a separate function later. (A similar name pattern is found for the other get-reg commands.)
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x7A) |
0x7E DVDLowNotifyReset
Resets internal flags, closes the open partition (if there is one), clears the transfer complete interrupt and drive error interrupt, enables the transfer complete interrupt and error interrupt, and disables the cover interrupt.
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x7E) |
0x7F DVDLowSetSpinupFlag
Prints the message "(handleDiCommand) DI_SET_SPINUP_FLAG_CMD should have been executed in the PPC shim layer only" and returns 0x80.
The PPC-side simply stores a boolean which is later used as the parameter to DVDLowReset. For some reason, Nintendo decided to give it an ioctl number as well, even though it didn't need one.
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x7F) |
0x80 DVDLowReadDvdPhysical
Probably related to DVD-Video[check].
The output buffer size must be ≥ 0x800, or DIMAR and DILENGTH will not be written. The output buffer also needs to be 32-byte aligned, or else the driver will hang.
DICMDBUF0 = 0xAD000000 | (position << 8) // AD00XX00 DICMDBUF1 = 0 DICMDBUF2 = 0 DILENGTH = 0x800 DIMAR = dest DICR = TSTART | DMA
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x80) |
7 | u8 | Position(?) |
0x81 DVDLowReadDvdCopyright
Probably related to DVD-Video[check].
DICMDBUF0 = 0xAD010000 | (position << 8) // AD01XX00 DICMDBUF1 = 0 DICMDBUF2 = 0 DICR = TSTART
The contents of DIIMMBUF (u32) are written to the output buffer. The output buffer size is not checked, but 4 would be a sane value.
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x81) |
7 | u8 | Position(?) |
0x82 DVDLowReadDvdDiscKey
Probably related to DVD-Video[check].
The output buffer size must be ≥ 0x800, or DIMAR and DILENGTH will not be written. The output buffer also needs to be 32-byte aligned, or else the driver will hang.
DICMDBUF0 = 0xAD020000 | (position << 8) // AD02XX00 DICMDBUF1 = 0 DICMDBUF2 = 0 DILENGTH = 0x800 DIMAR = dest DICR = TSTART | DMA
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x82) |
7 | u8 | Position(?) |
0x83 DVDLowGetLength
Stores the last DILENGTH value into outbuf, which must be at least 4 bytes in size (or else 0x20 is returned). Note that this doesn't directly read DILENGTH, but rather a separate value that is set when DILENGTH is set (and zero'd if a read error occurs on a command that uses DILENGTH; this means that this command cannot be used to get the amount of data still left to be transfered when the error happened).
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x83) |
0x84 Get DIIMMBUF
Stores the current value of DIIMMBUF into outbuf, which must be at least 4 bytes in size (or else 0x20 is returned).
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x83) |
0x85 DVDLowMaskCoverInterrupt
Disables the cover interrupt by clearing bit 1 of DICVR (leaving bit zero unchanged). Does not clear the cover interrupt if it is currently asserted (does not write bit 2). Actual code is DICVR = (DICVR & ~4 & ~2)
.
Titles have a DVDLowMaskCoverInterrupt function that is dummied out to always return 1; this function is used by DVDInit in the exact same place that gamecube titles write DICVR = 0
(which should be equivalent, as writes to bit 0 which indicates the cover status presumably do nothing[check]). However, since it is stubbed out, there is no way of being sure that 0x85 was actually used by that function.
The output buffer is not used, and it may be null. Its size is not checked.
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x85) |
0x86 DVDLowClearCoverInterrupt
Clears the cover interrupt by writing bit 2 of DICVR (leaving the other bits unchanged).
The output buffer is not used, and it may be null. Its size is not checked.
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x86) |
0x87 DVDLowUnmaskStatusInterrupts
Dummied out; does nothing (and always returns 1).
Titles have a DVDLowUnmaskStatusInterrupts function that is dummied out to always return 1; this function is used by DVDInit in the exact same place that gamecube titles write DISR = 0x2a
(which enables DEINTMASK, TCINTMASK, and BRKINTMASK, and does not clear any asserted interrupts). However, since it is stubbed out, there is no way of being sure that 0x87 was actually used by that function.
The output buffer is not used, and it may be null. Its size is not checked.
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x87) |
0x88 DVDLowGetCoverStatus
Checks the current cover status and stores the result into outbuf, which must be at least 4 bytes in size (or else 0x20 is returned).
The result is 0 right after a reset[check], 1 if a disc is not inserted (bit 1 of DICVR set), and 2 if a disc is inserted (bit 1 of DICVR not set).
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x88) |
0x89 DVDLowUnmaskCoverInterrupt
Enables the cover interrupt by setting bit 1 of DICVR (leaving bit zero unchanged). Does not clear the cover interrupt if it is currently asserted (does not write bit 2). Actual code is DICVR = ((DICVR & ~4) | 2)
.
Debug symbols list a function called DVDLowUnmaskCoverInterrupt, but no actual function remains as it was removed as unused (and even if it did still exist, it presumably would be dummied out to just return 1 as it is only 8 bytes). Therefore, there is no way to be certain that 0x89 actually was called DVDLowUnmaskCoverInterrupt, but it seems very likely based on DVDLowMaskCoverInterrupt.
The output buffer is not used, and it may be null. Its size is not checked.
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x89) |
0x8A DVDLowReset
Resets the drive, using syscalls 0x44, 0x45, and 0x46. If a reset is already in progress (check_di_reset returns true), then it immediately calls deassert_di_reset; otherwise, it calls assert_di_reset, waits 12µs, and then calls deassert_di_reset. Afterwards, registers are reset in the same way as DVDLowNotifyReset other than the cover interrupt. The cover interrupt is temporarilly disabled during this process, but is reenabled afterwards if it was enabled before.
Enable spinup is passed to syscall 0x4e, which activates the DI_SPIN GPIO if it is 0 and disables it otherwise.
The output buffer is not used, and it may be null. Its size is not checked.
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x8A) |
4 | u32 | Enable spinup |
0x8B DVDLowOpenPartition ioctl
Returns 0x20 and prints a warning that "OPEN_PARTITION done through Ioctlv, not Ioctl".
The output buffer is not used, and it may be null. Its size is not checked.
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x8B) |
0x8C DVDLowClosePartition
Closes the currently-open partition, removing information about its keys and such.
The output buffer is not used, and it may be null. Its size is not checked.
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x8C) |
0x8D DVDLowUnencryptedRead
Reads raw data from the disc. Only usable in the "System Area" of the disc. The start and end must lie within a single one of the following ranges or else 0x20 is returned:
Start (offset) | Start (bytes) | Length (offset) | Length (bytes) | End (offset) | End (bytes) |
---|---|---|---|---|---|
0 | 0 | 0x14000 | 0x50000 | 0x14000 | 0x50000 |
0x460A0000 | 0x118280000 | 8 | 0x20 | 0x460A0008 | 0x118280020 |
0x7ED40000 | 0x1FB500000 | 8 | 0x20 | 0x7ED40008 | 0x1FB500020 |
The output buffer must be 32-byte aligned and the length must also be a multiple of 32; otherwise, 0x80 is returned.
The output buffer size must be ≥ length, or DIMAR and DILENGTH will not be written.
DICMDBUF0 = 0xA8000000 DICMDBUF1 = position DICMDBUF2 = length DILENGTH = length DIMAR = dest DICR = TSTART|DMA
Versions of IOS prior to IOS28 only permitted reads in the first range. The prelaunch august 10 build further restricts it to only allow reads starting at offset 0x10000 (byte 0x40000), along with a 1-higher upper bound (likely due to use of ≤ instead of just less than) which does not matter in practice due the length alignment requirement. This means that the PPC would only have access to the Wii Disc's partitions information and such, and not header bytes beyond 0x20 (the first 0x20 bytes are accessible through 0x70 DVDLowReadDiskID), i.e. the game title and encryption information cannot be accessed.
Nintendo titles check on startup if they are running an IOS ≥ 30 (and ≤ 253) and if so, perform some DI checks; specifically, they attempt to read 0x20 bytes from 0x460a0000 (or from 0x7ed40000 if the byte at 0x8000319c is 0x81 — possibly related to dual-layer discs?). If this read attempt returns anything other than 2, the game will refuse to start with the message "An error has occurred. Press the Eject Button, remove the Game Disc, and turn off the power to the console. Please read the Wii Operations Manual for further instructions." If the drive error is anything other than 0x0052100 (OK/Logical block address out of range), the game will refuse to start with the message "Error #001, unauthorized device has been detected."
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x8D) |
4 | size_t | Length (bytes) |
8 | off_t | Position (>> 2) |
0xA8 Unchecked Unencrypted Read
DVDLowUnencryptedRead without any restrictions on the regions it can be used; internal use only. Attempting to use this from IOS_Ioctl will result in return 0x20. Otherwise idential to DVDLowUnencryptedRead, including the alignment and size checks.
0x8E DVDLowEnableDvdVideo
Can only be called by uid[check] 0. The restrictions on inbuf are different: it only needs to have a size of at least 1. If the value is 0, performs syscall 0x50 with true as the parameter; otherwise, false is the parameter. (Syscall 0x50 takes disable as a parameter, while this takes enable).
If the uid is incorrect or inbuf is size 0, returns 0x20.
Offset | Type | Name |
---|---|---|
0 | bool | Enable |
0x90 DVDLowGetNoDiscOpenPartitionParams ioctl
Prepares an argument for use with DVDLowNoDiscOpenPartition. There is some overlap between this structure and runopenpartparam; they may actually be the same structure. A disc needs to be inserted and DVDLowReadDiscID must have been called for this to actually be used.
No other alignment, size, or pointer target validation happens when using the ioctl. Presumably, this would have happened in the ioctlv.
Returns 0x80 if DVDLowReadDiscID has not been called or the tmdSize or sharedCertsSize is too small, and 0x20 if the partition's tmd offset or cert chain is 0, or if the disc has encryption enabled but is not encryped.
struct nodiscopenparams { undefined4 unused1; ticket * ticket; // Must be set; size 0x2a4 undefined4 unused2; size_t tmdSize; // Must be set tmd * tmd; // Pointer to a buffer of size tmdSize size_t sharedCertsSize; // Must be set byte * sharedCerts; // Pointer to a buffer of size sharedCertsSize off_t partitionDataOffset; // Set to partitionOffset + partition->dataOffset h3buffer * h3Hashes; // Must be set; 0x18000 }
The output buffer is not used, and it may be null. Its size is not checked.
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x90) |
4 | off_t | Partition position (>> 2) |
8 | nodiscopenparams* | params |
0x91 DVDLowNoDiscOpenPartition ioctl
Opens/checks a partition on a disc, using data that was read from the disc earlier and without reading anything new from the disc. (Presumably, this would have been used to speed up startup times, though it isn't usable as-is.)
The output buffer is not used, and it may be null. Its size is not checked.
Returns 0x80 if a partition is already open, 0x40 if /dev/es does not accept the output, and 0x20 if H3 hash verification failed. The resulting es error code will be written to the specified location, even if it is 0.
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x91) |
4 | nodiscopenparams* | params |
8 | u32 * | ES error output |
0x92 DVDLowGetNoDiscBufferSizes ioctl
Gets the values needed for tmdSize and sharedCertsSize. A disc needs to be inserted and DVDLowReadDiscID must have been called for this to actually be used.
No other alignment, size, or pointer target validation happens when using the ioctl. Presumably, this would have happened in the ioctlv.
The output buffer is not used, and it may be null. Its size is not checked.
Returns 0x80 if DVDLowReadDiscID has not been called, and 0x20 if the partition's tmd offset or cert chain is 0, or if the disc has encryption enabled but is not encryped.
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x92) |
4 | off_t | Partition position (>> 2) |
8 | u32 * | TMD Size out |
12 | u32 * | Cert Chain Size Out |
0x93 DVDLowOpenPartitionWithTmdAndTicket ioctl
Should behave identically to the ioctlv, as long as pointers are properly translated (when using ioctl, it does not know to translate random pointers in the inbuf, nor does it know to check them). Using the ioctl bypasses the size and alignment validation that happens in the ioctlv.
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x93) |
4 | runopenpartparam* | params |
8 | u32 * | ES error output |
0x94 DVDLowOpenPartitionWithTmdAndTicketView ioctl
Should behave identically to the ioctlv, as long as pointers are properly translated (when using ioctl, it does not know to translate random pointers in the inbuf, nor does it know to check them). Using the ioctl bypasses the size and alignment validation that happens in the ioctlv.
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x93) |
4 | runopenpartparam* | params |
8 | u32 * | ES error output |
0x95 DVDLowGetStatusRegister
Stores the current value of DISR into outbuf, which must be at least 4 bytes in size (or else 0x20 is returned).
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x95) |
0x96 DVDLowGetControlRegister
Stores the current value of DICR into outbuf, which must be at least 4 bytes in size (or else 0x20 is returned).
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0x96) |
0xA4 DVDLowReportKey
Original purpose unknown. Does not work on retail drives.
Nintendo titles send this after performing the off-disc DVDLowUnencryptedRead check to verify that they are on official hardware; specifically, the first parameter is set to 4 (internally, 0x40000 >> 16) and the second parameter is set to 0. If this call returns anything other than 2, the "An error has occurred" message will be shown. If the drive error is anything other than 0x0053100 (OK/Invalid Request Medium Format Corrupted) or 0x0052000 (OK/Invalid command operation code), the "Error #001" message will be shown.
The output buffer size must be ≥ 0x20, or DIMAR and DILENGTH will not be written. The output buffer also needs to be 32-byte aligned, or else the driver will hang.
DICMDBUF0 = 0xA4000000 | (param1 << 16) // A4XX0000 DICMDBUF1 = param2 & 0xFFFFFF DICMDBUF2 = 0 DILENGTH = 0x20 DIMAR = dest DICR = TSTART | DMA
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0xA4) |
7 | u8 | param1 |
8 | u32 | param2 |
0xAB DVDLowSeek
Seeks to the sector containing a specific position on the disc.
The output buffer is not used, and it may be null. Its size is not checked.
if (encryptionEnabled) { sectorNum = position / 0x1F00 start = partitionOffset + sectorNum * 0x2000 } else { start = partitionOffset + position } DICMDBUF0 = 0xab000000 DICMDBUF1 = start DICR = TSTART
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0xAB) |
4 | off_t | Position (>> 2) |
0xD0 DVDLowReadDvd
Probably related to DVD-Video[check].
The output buffer size must be ≥ 0x800 * length, or DIMAR and DILENGTH will not be written. The output buffer also needs to be 32-byte aligned, or else the driver will hang.
DICMDBUF0 = 0xD0000000 | ((flag1 & 1) << 7) | ((flag2 & 1) << 6) // D00000C0, D0000080, D0000040, D0000000 DICMDBUF1 = position & 0xFFFFFF DICMDBUF2 = length & 0xFFFFFF DILENGTH = 0x800 * length DIMAR = dest DICR = TSTART | DMA
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0xD0) |
7 | bool | Flag1 |
11 | bool | Flag2 |
12 | size_t | Length? |
16 | off_t | Position? |
0xD1 DVDLowReadDvdConfig
Probably related to DVD-Video[check].
The contents of DIIMMBUF (u32) are written to the output buffer. The output buffer size is not checked, but 4 would be a sane value.
DICMDBUF0 = 0xD1000000 | ((flag1 & 1) << 16) | param2 // D10000XX, D10100XX DICMDBUF1 = position & 0xFFFFFF DICMDBUF2 = 0 DICR = TSTART
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0xD1) |
7 | bool | Flag1 |
11 | u8 | Param2 |
12 | off_t | Position? |
0xD2 DVDLowStopLaser
Disables the laser.
The contents of DIIMMBUF (u32) are written to the output buffer. The output buffer size is not checked, but 4 would be a sane value.
DICMDBUF0 = 0xD2000000 DICR = TSTART
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0xD2) |
0xD9 DVDLowOffset
Unknown. The offset(?) provided is stored by the driver, but isn't read.
The contents of DIIMMBUF (u32) are written to the output buffer. The output buffer size is not checked, but 4 would be a sane value.
DICMDBUF0 = 0xD9000000 | ((flag & 1) << 16) // 0xD9000000, 0xD9010000 DICMDBUF1 = offset DICR = TSTART
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0xD9) |
7 | bool | flag |
8 | off_t | Offset? |
0xDA DVDLowReadDiskBca
Reads the last 0x40 bytes of the burst cutting area. Note that the actual BCA is 188 (0xBC) bytes long; the area returned by this command is mostly 0'd except for the last 12 bytes (except for New Super Mario Bros. Wii, which also has a 1 byte before those 12 bytes, which the game checks for as some kind of copy protection it seems).
The output buffer size must be ≥ 0x40, or DIMAR and DILENGTH will not be written. The output buffer also needs to be 32-byte aligned, or else the driver will hang.
DICMDBUF0 = 0xDA000000 DILENGTH = 0x40 DIMAR = dest DICR = TSTART | DMA
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0xDA) |
0xDB DVDLowRequestDiscStatus
Unknown[check].
The contents of DIIMMBUF (u32) are written to the output buffer. The output buffer size is not checked, but 4 would be a sane value.
DICMDBUF0 = 0xDB000000 DICR = TSTART
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0xDB) |
0xDC DVDLowRequestRetryNumber
Unknown[check].
The contents of DIIMMBUF (u32) are written to the output buffer. The output buffer size is not checked, but 4 would be a sane value.
DICMDBUF0 = 0xDC000000 DICR = TSTART
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0xDC) |
0xDD DVDLowSetMaximumRotation
Sets the maximum rotation speed, allegedly[check]? Does not actually seem to have any impact on the speed of the drive (based on the sound it makes; Gamecube games continue to make one sound and Wii games continue to make a faster sound, regardless of the value used).
Using a speed of 3 will result in the drive erroring with code 0x052400 (Invalid Field in command packet) and the ioctl returning 2.
Nintendo's wrapper code internally shifts its parameter down by 16 (so titles pass 0x20000 and the ioctl uses 2). It looks like titles also only ever use the speed of 2[check] (the higher-level DVDDownRotationAsync only uses speed 2, and the other calls that happen in the drive reset handler also only use 2 — though they also only happen when the value at 800030e6 is 0x8003, apparently tied to the device code returned from inquiry or'd with 0x8000, and my drive has a device code of 2).
The output buffer is not used, and it may be null. Its size is not checked.
DICMDBUF0 = 0xDD000000 | ((speed & 3) << 16) // DD000000, DD010000, DD020000, DD030000 DICR = TSTART
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0xDD) |
7 | u8 | Speed |
0xDF DVDLowSerMeasControl
Unknown[check]. Possibly "sensor measurement control" or "servo measurement control".
The output buffer size must be ≥ 0x20, or DIMAR and DILENGTH will not be written. The output buffer also needs to be 32-byte aligned, or else the driver will hang.
DICMDBUF0 = 0xDF000000 | ((flag1 & 1) << 17) | ((flag2 & 1) << 16) // DF030000, DF010000, DF020000, DF000000 DILENGTH = 0x20 DIMAR = dest DICR = TSTART | DMA
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0xDF) |
7 | bool | Flag1 |
11 | bool | Flag2 |
0xE0 DVDLowRequestError
Reads the current drive error. Does not clear the error interrupt.
The contents of DIIMMBUF (u32) are written to the output buffer. The output buffer size is not checked, but 4 would be a sane value.
See yagcd §5.7.3.5 for a partial list of error codes. Byte 4 is as described in yagcd. The 3rd byte an SCSI Sense Key, and bytes 2 and 1 are SCSI ASC/ASCQ.
DICMDBUF0 = 0xE0000000 DICR = TSTART
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0xE0) |
0xE1 DVDLowAudioStream
Plays an audio stream, according to yagcd §5.7.1 and this article. The audio stream is played through the Audio Interface. Audio is not decrypted, making this command useless for Wii games.
The output buffer is not used, and it may be null. Its size is not checked.
DICMDBUF0 = 0xE1000000 | ((mode & 3) << 16) // E1000000, E1010000, E1020000, E1030000 DICMDBUF1 = position DICMDBUF2 = length DICR = TSTART
Modes:
- 0 → Wait for the current stream to finish before starting the new one (if present)
- 1 → Immediately stop the current stream. The parameters are ignored (and will not be reflected by 0xE2).
- 2 → Invalid, fails with error 0x052401 (invalid audio command) and returns 2
- 3 → Invalid, fails with error 0x052401 (invalid audio command) and returns 2
If length and position are both 0, then audio streaming stops.
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0xE1) |
7 | u8 | Mode |
8 | size_t | Length |
12 | off_t | Position |
0xE2 DVDLowRequestAudioStatus
Requests information about an audio stream, according to yagcd §5.7.1 and this article.
The output buffer is not used, and it may be null. Its size is not checked. This is somewhat odd, as the response is put into into DIIMMBUF.
DICMDBUF0 = 0xE2000000 | ((request & 3) << 16) // E2000000, E2010000, E2020000, E2030000 DICMDBUF1 = 0 DICR = TSTART
Requests:
- 0 → Is stream playing (sets DIIMMBUF to 0 or 1)
- 1 → Current playback address (sets DIIMMBUF to current location, with sector-level granularity, and as a 32-bit offset; the result is always a multiple of 0x2000)
- 2 → Playback start address (sets DIIMBUF to the value previously configured from play audio stream)
- 3 → Playback length (sets DIIMBUF to the value previously configured from play audio stream)
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0xE2) |
7 | u8 | Request |
0xE3 DVDLowStopMotor
Stops the motor. The motor can be re-activated by performing a reset with spinup enabled.
If the eject flag is set, the disc will be ejected after the motor is stopped. The kill flag is odder; from libogc:
Warning, this will kill your drive untill the next reset. Will not respond to DI commands, will not take in or eject the disc. Your drive will be d - e - d, dead.
I deem this function to be harmless, as normal operation will resume after a reset. However, I am not liable for anyones drive exploding as a result from using this function.
The contents of DIIMMBUF (u32) are written to the output buffer. The output buffer size is not checked, but 4 would be a sane value.
DICMDBUF0 = 0xE3000000 | ((eject & 1) << 17) | ((kill & 1) << 20) // E3120000, E3020000, E3100000, E3000000 DICMDBUF1 = 0 DICR = TSTART
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0xE3) |
7 | bool | Eject |
11 | bool | Kill |
0xE4 DVDLowAudioBufferConfig
Audio buffer configuration.
The contents of DIIMMBUF (u32) are written to the output buffer. The output buffer size is not checked, but 4 would be a sane value.
DICMDBUF0 = 0xE4000000 | ((enable & 1) << 16) | (bufSize & 0xf) // E401000X, E400000X DICMDBUF1 = 0 DICR = TSTART
The system menu issues this for Gamecube games based off of the audio streaming and streaming buffer size fields in the Wii Disc header; byte 8 indicates whether or not it is enabled and byte 9 indicates the buffer size (defaulting to 10 if set to 0 while streaming is enabled).
This command can only be issued immediately after reading the disc ID; calls before that will return 0x05020401 (Disk ID not read) and calls after a read will error with 0x052402 (Configuration out of permitted period). Since DVDLowReadDiskID does an additional read for Wii discs, this command cannot be used on a Wii disc. Bypassing /dev/di and manually writing the DI registers does allow the other commands to be used.
Offset | Type | Name |
---|---|---|
0 | u8 | Command (0xE4) |
7 | bool | Enable DVD audio |
11 | u8 | Streaming buffer size |