Difference between revisions of "/dev/di"

From WiiBrew
Jump to: navigation, search
m (0xE3 DVDLowStopMotor: Oops, forgot about replacing it in the command itself)
m (0xE4 DVDLowAudioBufferConfig: param2 -> bufSize)
 
Line 1,595: Line 1,595:
 
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.
 
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) | (param2 & 0xf) // E401000X, E400000X
+
  DICMDBUF0 = 0xE4000000 | ((enable & 1) << 16) | (bufSize & 0xf) // E401000X, E400000X
 
  DICMDBUF1 = 0
 
  DICMDBUF1 = 0
 
  DICR = TSTART
 
  DICR = TSTART

Latest revision as of 00:23, 8 October 2019

See also: Hardware/Drive Interface


/dev/di is the IOS driver used to control the disc drive. This documentation is based on the most recent version (dated Jun 3 2009 07:49:09 and included in IOS58 and IOS80). Names starting with "DVDLow" are based on function names found in Nintendo titles (which print an error message including the name if the Ioctl or Ioctlv fails). Other names are based on debug messages found in the driver itself.

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

Contents

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

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.

Vector
Name Direction Size Alignment
Command In 0x20 4
Ticket (optional) In 0x2a4 if present, not checked if absent (should be 0) 32
Shared certs (optional) In Arbitrary if present, not checked if absent (should be 0) 32
TMD Out 0x49e4 32
ES Error Out 0x20 (of which only the first 4 bytes are used) 4
Command
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; 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). This does allow determination of the inputs, though:

Vector
Name Direction Size Alignment
Command In 0x20 4
TMD size pointer In 4 32
Shared certs size pointer In 4 32
Ticket Out 0x2A4 32
TMD size pointer (again; aliases with previous pointer) Out 4 32
TMD Out TMD size, from before 32
Shared certs size pointer (again; aliases with the previous pointer) Out 4 32
Shared certs Out Shared certs size, from before 32
Partition data offset pointer Out 4 32
H3 hashes Out 0x18000 32

Presumably, the ioctlv would have validated size and alignment, and then filled in additional parameters on the command before passing it on to the shared logic that it is still accessible via the ioctlv.

Command
Offset Type Name
0 u8 Command (0x90)
4 off_t Partition position (>> 2)
8 nodiscopenparams* params (probably written by ioctlv, but unverifyable)

0x91 DVDLowNoDiscOpenPartition

Dummied out; always returns 0x80. However, still usable as an Ioctl (which is probably unintended).

Presumably, the ioctlv would have validated size and alignment, and then filled in additional parameters on the command before passing it on to the shared logic that it is still accessible via the ioctlv. However, no PPC-side code exists so the exact ioctlv arguments are unknown.

Command
Offset Type Name
0 u8 Command (0x91)
4 nodiscopenparams* params (probably written by ioctlv, but unverifyable)
8 u32 * ES error output (probably written by ioctlv, but unverifyable)

0x92 DVDLowGetNoDiscBufferSizes

Dummied out; 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). This does allow determination of the inputs, though:

Vector
Name Direction Size Alignment
Command In 0x20 4
TMD size pointer Out 4 32
Shared certs size pointer Out 4 32

Presumably, the ioctlv would have validated size and alignment, and then filled in additional parameters on the command before passing it on to the shared logic that it is still accessible via the ioctlv.

Command
Offset Type Name
0 u8 Command (0x92)
4 off_t Partition position (>> 2)
8 u32 * TMD Size out (probably written by ioctlv, but unverifyable)
12 u32 * Cert Chain Size Out (probably written by ioctlv, but unverifyable)

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.

Vector
Name Direction Size Alignment
Command In 0x20 4
Ticket (optional) In 0x2a4 if present, not checked if absent (should be 0) 32
TMD (required) In Arbitrary 32
Shared certs (optional) In Arbitrary 32
ES Error Out 0x20 (of which only the first 4 bytes are used) 4
Command
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.

Vector
Name Direction Size Alignment
Command In 0x20 4
Ticket View (optional) In 0x98 if present, not checked if absent (should be 0) 32
TMD (required) In Arbitrary 32
Shared certs (optional) In Arbitrary 32
ES Error Out 0x20 (of which only the first 4 bytes are used) 4
Command
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
  • 0x79 DVDLowWaitForCoverClose
  • 0x7A DVDLowGetCoverRegister
  • 0x83 DVDLowGetLength
  • 0x84 Get DIIMMBUF
  • 0x85 DVDLowUnmaskCoverInterrupt
  • 0x86 DVDLowClearCoverInterrupt
  • 0x87
  • 0x88 DVDLowGetCoverStatus
  • 0x89 Enable Cover Interrupt
  • 0x8B DVDLowOpenPartition ioctl
  • 0x8E DVDLowEnableDvdVideo
  • 0x95 DVDLowGetStatusRegister
  • 0x96 DVDLowGetControlRegister

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.

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
Command
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
Command
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.

Command
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.

Command
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.)

Command
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.

Command
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.

Command
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
Command
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.

Command
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
Command
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).

Command
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).

Command
Offset Type Name
0 u8 Command (0x83)

0x85 DVDLowUnmaskCoverInterrupt

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).

The output buffer is not used, and it may be null. Its size is not checked.

Command
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.

Command
Offset Type Name
0 u8 Command (0x86)

0x87

Dummied out; does nothing (and always returns 1). Possibly an ID reserved for a PPC-only command (DVDLowBreak?), as is also done with DVDLowSetSpinupFlag?

The output buffer is not used, and it may be null. Its size is not checked.

Command
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).

Command
Offset Type Name
0 u8 Command (0x88)

0x89 Enable Cover Interrupt

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).

The output buffer is not used, and it may be null. Its size is not checked.

Command
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 (syscall_check_di_reset returns true), then it immediately calls syscall_deassert_di_reset; otherwise, it calls syscall_assert_di_reset, waits 12µs, and then calls syscall_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.

Command
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.

Command
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.

Command
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:

Ranges
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 IOS30 only permitted reads in the first range[check]. 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."

Command
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.

Inbuf
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.

Command
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.

Command
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.

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

Command
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.

Command
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).

Command
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).

Command
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
Command
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
Command
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
Command
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
Command
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
Command
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
Command
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
Command
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
Command
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
Command
Offset Type Name
0 u8 Command (0xDC)

0xDD DVDLowSetMaximumRotation

Sets the maximum rotation speed[check]?

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
Command
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
Command
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
Command
Offset Type Name
0 u8 Command (0xE0)

0xE1 Play Audio Stream

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.

Command
Offset Type Name
0 u8 Command (0xE1)
7 u8 Mode
8 size_t Length
12 off_t Position

0xE2 Request Audio Status

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)
Command
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
Command
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.

Command
Offset Type Name
0 u8 Command (0xE4)
7 bool Enable DVD audio
11 u8 Streaming buffer size