In memory of Ben “bushing” Byer, who passed away on Monday, February 8th, 2016.

/dev/usb/oh0: Difference between revisions

From WiiBrew
< /dev | usb
Jump to navigation Jump to search
Leoetlino (talk | contribs)
Leoetlino (talk | contribs)
tueidj: Thanks. Do you know in what IOS version it is implemented? It doesn't seem to be in IOS36.
 
(34 intermediate revisions by 2 users not shown)
Line 1: Line 1:
/dev/usb/oh0 is used to interact with the Wii's external USB bus. This interface is very similar to [[:/dev/usb/oh1]] and has two parts: the "root" or host device, and individual devices on which USB requests can be submitted.
/dev/usb/oh0 is used to interact with the Wii's external USB bus. This interface is very similar to [[:/dev/usb/oh1]] and has two parts: the "root" or host device, and individual devices on which USB requests can be submitted.


== /dev/usb/oh0 (IOS57 and newer) ==
== /dev/usb/oh0 (IOS57, 58 and 59) ==


In recent versions of IOS (after IOS 57), the OHCI0 module appears to implement a different interface and seems to only register this new interface of /dev/usb/oh0.
In IOS57, 58 and 59, the OHCI0 module appears to implement a different interface and seems to only register this new interface of /dev/usb/oh0.


Only IOS_OPEN, IOS_CLOSE and IOS_IOCTL are valid commands. The other commands immediately return IPC_EINVAL (-4).
Only IOS_OPEN, IOS_CLOSE and IOS_IOCTL are valid commands. The other commands immediately return IPC_EINVAL (-4).
Line 49: Line 49:


== /dev/usb/oh0 ==
== /dev/usb/oh0 ==
The OH1 module appears to be able to register itself as /dev/usb/oh0 and implements a similar set of requests.
The OH1 module appears to be able to register itself as /dev/usb/oh0 and implements a similar set (subset?) of requests.
 
This list of requests is based on IOS36.
 
{| class="wikitable"
{| class="wikitable"
|-
|-
Line 57: Line 59:


|-
|-
| 12 (ioctlv) || USBV0_IOCTL_GETDEVLIST || 2 || 2 || Both
| 12 (ioctlv) || USBV0_IOCTLV_GETDEVLIST || 2 || 2 || Both
|
|
Returns a list of connected devices matching an interface class.
<source lang="c">
struct Descriptor
{
  u32 unknown;
  u16 vid;
  u16 pid;
};
// sizeof(Descriptor) = 8
</source>
* in 0: u8 - number of descriptors to return
* in 0: u8 - number of descriptors to return
* in 1: u8 - interface class
* in 1: u8 - interface class
Line 65: Line 79:


|-
|-
| 15 (ioctl) || ? || - || 4 bytes || Both
| 15 (ioctl) || ? (USB_GetRhDesca) || - || 4 bytes || Both
| Mentioned by comex in #dolphin-dev.
| Unknown; something to do with the root hub (Desc = description?). Appears to return 02 00 03 02 regardless of the number of plugged in devices. {{check}}


|-
|-
| 20 (ioctlv) || ? || 1 || 1 || Both
| 20 (ioctlv) || USBV0_IOCTLV_GETRHPORTSTATUS|| 1 || 1 || OH0
| Unknown.
| Supposedly used for getting the root hub's port status. (internal name: GetRhPortStatus) {{check}}
* in 0: u8 - ???
* in 0: u8 - ?
* io 0: u16 - ???
* io 0: u32 - Presumably the port status


|-
|-
| 25 (ioctlv) || ? || 2 || ? || Both
| 25 (ioctlv) || USBV0_IOCTLV_SETRHPORTSTATUS || 2 || 0 || OH0
| Unknown. Returns IPC_EINVAL if in_vectors[0] is greater than the least significant byte of (heap + 0x40). Possibly has to do with suspend/resume. {{check}}
| Supposedly used for setting the root hub's port status. (internal name: SetRhPortStatus) {{check}}
* in 0: u8 - ???
* in 0: u8 - ?
* in 1: u16 - ???
* in 1: u32 - Presumably the port status


|-
|-
| 27 (ioctlv) || USBV0_IOCTL_DEVINSERTHOOK || 2 || 0 || OH0
| 27 (ioctlv) || USBV0_IOCTLV_DEVINSERTHOOK || 2 || 0 || OH0
|
|
Returns when a device with the requested VID/PID is plugged in, or immediately if the device is already inserted.
Returns when a device with the requested VID/PID is plugged in, or immediately if the device is already inserted.
Line 88: Line 102:


|-
|-
| 30 (ioctlv) || USBV0_IOCTL_DEVINSERTHOOKID (RegisterInsertionNotifyWithId) || 3 || 1 || OH0
| 28 (ioctlv) || USBV0_IOCTLV_DEVICECLASSCHANGE || 1 || 0 || OH0
|
|
Returns when a device with the requested VID/PID is plugged in, or immediately if the device is already inserted.
Has to do with device insertion hooks. Triggered on device class change (?) {{check}}
 
* in 0: u8 - Device class (can be zero)


It is similar to USBV0_IOCTL_DEVINSERTHOOK, but it has an additional in vector + io vector, possibly used for passing an "ID" (according the official name). Its purpose is unknown.
|-
| 30 (ioctlv) || USBV0_IOCTLV_DEVINSERTHOOKID (RegisterInsertionNotifyWithId) || 3 || 1 || OH0
|
Returns when a device with the requested VID/PID is plugged in. Similar to ioctlv 27, but with additional parameters.


* in 0: u16 - VID
* in 0: u16 - VID
* in 1: u16 - PID
* in 1: u16 - PID
* in 2: u8 - Unknown (00); some sort of ID?
* in 2: u8 - If falsy, return immediately if the device is already plugged in
* io 0: u8* of size 4 - Unknown; some sort of ID?
* io 0: u32 - Overwritten with an ID to use with ioctl 31 for cancelling this insertion hook


|-
|-
| 31 (?) || ? (IUSB_CancelInsertionNotify) || ? || ? || OH0
| 31 (ioctl) || USBV0_IOCTL_CANCEL_INSERT_HOOK || 4 bytes || - || OH0
| ?
| Internal names: IUSB_CancelInsertionNotify / __ohciCancelNotifyInsertion
 
Cancels a device insertion hook (the hook request will be replied to with return value -7022). The input buffer is the ID given in the unique output vector of ioctlv 30.
 
Returns IPC_EINVAL if input buffer was nullptr. Otherwise, IPC_SUCCESS is returned if there was no hook;
if there was a pending hook, the return value of <code>IOS_ResourceReply(ioctlv_30_request, -7022)</code> is used.
 
|}
|}


== /dev/usb/oh0/%x/%x ==
== /dev/usb/oh0/%x/%x ==
These devices represent an individual USB device, based on its vendor ID and product ID (in hexadecimal format without leading zeroes). It is unclear how devices with the same VID/PID can be used at the same time.
These devices represent an individual USB device, based on its vendor ID and product ID (in hexadecimal format without leading zeroes). It is unclear how devices with the same VID/PID can be used at the same time.
Only one device handle can be opened at the same time. Attempting to open more than one results in IPC_EEXIST (-2).
This list is based on IOS36.


{| class="wikitable"
{| class="wikitable"
Line 113: Line 142:


|-
|-
| 0 (ioctlv) || USBV0_IOCTL_CTRLMSG || 6 || 1 || Both
| 0 (ioctlv) || USBV0_IOCTLV_CTRLMSG || 6 || 1 || Both
|
|
Submits a control transfer.
Submits a control transfer.
Line 125: Line 154:


|-
|-
| 1 (ioctlv) || USBV0_IOCTL_BLKMSG || 2 || 1 || Both
| 1 (ioctlv) || USBV0_IOCTLV_BLKMSG || 2 || 1 || Both
|
|
Submits a bulk transfer.
Submits a bulk transfer.
Line 131: Line 160:
* in 1: u16 - Length
* in 1: u16 - Length
* io 0: array of length in[1] - Payload data
* io 0: array of length in[1] - Payload data
If io_vectors[0].size and the length don't match, returns IPC_EINVAL.


|-
|-
| 2 (ioctlv) || USBV0_IOCTL_INTRMSG || 2 || 1 || Both
| 2 (ioctlv) || USBV0_IOCTLV_INTRMSG || 2 || 1 || Both
|
|
Submits an interrupt transfer.
Submits an interrupt transfer.
Line 139: Line 170:
* in 1: u16 - Length
* in 1: u16 - Length
* io 0: array of length in[1] - Payload data
* io 0: array of length in[1] - Payload data
If io_vectors[0].size and the length don't match, returns IPC_EINVAL.


|-
|-
| 9 (ioctlv) || USBV0_IOCTL_ISOMSG || 3 || 2 || OH0
| 5 (ioctl) || USBV0_IOCTL_SUSPENDDEV || - || - || Both
|
Used to suspend a device.
 
|-
| 6 (ioctl) || USBV0_IOCTL_RESUMEDEV || - || - || Both
|
Used to resume a device.
 
|-
| 9 (ioctlv) || USBV0_IOCTLV_ISOMSG || 3 || 2 || OH0
|
|
Submits an isochronous transfer.
Submits an isochronous transfer.
Line 151: Line 194:


|-
|-
| 10 (ioctlv) || ? || ? || ? || OH0
| 10 (ioctlv) || USBV0_IOCTLV_LBLKMSG || 2 || 1 || OH0
|
Submits a bulk transfer. This is the same as ioctlv 1, except that this takes a u32 for the length instead of a u16.
 
* in 0: u8 - Endpoint
* in 1: u32 - Length
* io 0: array of length in_vectors[1] - Payload data
 
If io_vectors[0].size and the length don't match, returns IPC_EINVAL.
 
|-
| 26 (ioctl) || USBV0_IOCTL_DEVREMOVALHOOK || - || - || Both
|
|
???
Returns when the device is unplugged or reset (in which cases the return value is IPC_SUCCESS / IPC_OK), or when the child device (/dev/usb/oh0/%x/%x) is closed (IPC_ENOENT).
Handled by the same function as for bulk and interrupt transfers.
 
Only one hook is allowed at the same time. Further ioctls to set up another hook before it is triggered result in IPC_EEXIST.


|-
|-
| 26 (ioctlv) || USBV0_IOCTL_DEVREMOVALHOOK || 0 || 0 || Both
| 29 (ioctl) || USBV0_IOCTL_RESET_DEVICE || - || - || OH0
|
|
Returns when the device is unplugged.
Triggers the associated removal hook before resetting the USB device.
 
|-
| 32 (ioctlv) || ? || ? || ? || OH0
| Unknown. {{check}}
 


|-
|-
| 29 (ioctl) || ? || - || - || OH0
| 33 (ioctlv) || USBV0_IOCTLV_SETISOALT || ? || ? || OH0
|
|
Unknown. Used by Wheel of Fortune on shutdown. This probably resets the device.
Select an alternative interface configuration (for devices with isochronous endpoints, the active interface determines if usb bandwidth is reserved for iso transfers).
This is an alternative method to using a control message to achieve the same effect.
 
This ioctlv does not seem to be present in all IOS versions (not in IOS36 at least).
|}
|}



Latest revision as of 23:13, 27 December 2016

/dev/usb/oh0 is used to interact with the Wii's external USB bus. This interface is very similar to /dev/usb/oh1 and has two parts: the "root" or host device, and individual devices on which USB requests can be submitted.

/dev/usb/oh0 (IOS57, 58 and 59)

In IOS57, 58 and 59, the OHCI0 module appears to implement a different interface and seems to only register this new interface of /dev/usb/oh0.

Only IOS_OPEN, IOS_CLOSE and IOS_IOCTL are valid commands. The other commands immediately return IPC_EINVAL (-4).

Additionally, /dev/usb/oh0 can only be opened from UID 0x11. Attempts to open OH0 with other UIDs will result in IPC_EACCES (-1).

Ioctl Name Input Output Notes
0 USBV2_IOCTL_GET_VERSION - 0x20 bytes Writes the version 0x20001 to the output buffer. This is very similar to the IOS58 version of /dev/usb/hid, which writes 0x50001 to the output buffer instead. It is also reminiscent of /dev/usb/ehc which does the same thing (but with an ioctlv).
1 ? ? ? ?
2 ? ? ? ?
3 ? 0x20 bytes ? ?
4 ? 0x20 bytes ? ?
5 ? 0x20 bytes ? ?
16 ? 0x20 bytes ? ?
17 ? 0x20 bytes ? ?
18 ? 0x20 bytes ? Handled by the same function as for ioctl 20.
19 ? 0x20 bytes ? ?
20 ? 0x20 bytes ? Handled by the same function as for ioctl 18.

/dev/usb/oh0

The OH1 module appears to be able to register itself as /dev/usb/oh0 and implements a similar set (subset?) of requests.

This list of requests is based on IOS36.

Request Name Input Output OH0/OH1 Notes
12 (ioctlv) USBV0_IOCTLV_GETDEVLIST 2 2 Both

Returns a list of connected devices matching an interface class.

struct Descriptor 
{
  u32 unknown;
  u16 vid;
  u16 pid;
};
// sizeof(Descriptor) = 8
  • in 0: u8 - number of descriptors to return
  • in 1: u8 - interface class
  • io 0: u8 - number of devices
  • io 1: u32* of size num_descriptors * 8 - device list
15 (ioctl) ? (USB_GetRhDesca) - 4 bytes Both Unknown; something to do with the root hub (Desc = description?). Appears to return 02 00 03 02 regardless of the number of plugged in devices. [check]
20 (ioctlv) USBV0_IOCTLV_GETRHPORTSTATUS 1 1 OH0 Supposedly used for getting the root hub's port status. (internal name: GetRhPortStatus) [check]
  • in 0: u8 - ?
  • io 0: u32 - Presumably the port status
25 (ioctlv) USBV0_IOCTLV_SETRHPORTSTATUS 2 0 OH0 Supposedly used for setting the root hub's port status. (internal name: SetRhPortStatus) [check]
  • in 0: u8 - ?
  • in 1: u32 - Presumably the port status
27 (ioctlv) USBV0_IOCTLV_DEVINSERTHOOK 2 0 OH0

Returns when a device with the requested VID/PID is plugged in, or immediately if the device is already inserted.

  • in 0: u16 - VID
  • in 1: u16 - PID
28 (ioctlv) USBV0_IOCTLV_DEVICECLASSCHANGE 1 0 OH0

Has to do with device insertion hooks. Triggered on device class change (?) [check]

  • in 0: u8 - Device class (can be zero)
30 (ioctlv) USBV0_IOCTLV_DEVINSERTHOOKID (RegisterInsertionNotifyWithId) 3 1 OH0

Returns when a device with the requested VID/PID is plugged in. Similar to ioctlv 27, but with additional parameters.

  • in 0: u16 - VID
  • in 1: u16 - PID
  • in 2: u8 - If falsy, return immediately if the device is already plugged in
  • io 0: u32 - Overwritten with an ID to use with ioctl 31 for cancelling this insertion hook
31 (ioctl) USBV0_IOCTL_CANCEL_INSERT_HOOK 4 bytes - OH0 Internal names: IUSB_CancelInsertionNotify / __ohciCancelNotifyInsertion

Cancels a device insertion hook (the hook request will be replied to with return value -7022). The input buffer is the ID given in the unique output vector of ioctlv 30.

Returns IPC_EINVAL if input buffer was nullptr. Otherwise, IPC_SUCCESS is returned if there was no hook; if there was a pending hook, the return value of IOS_ResourceReply(ioctlv_30_request, -7022) is used.

/dev/usb/oh0/%x/%x

These devices represent an individual USB device, based on its vendor ID and product ID (in hexadecimal format without leading zeroes). It is unclear how devices with the same VID/PID can be used at the same time.

Only one device handle can be opened at the same time. Attempting to open more than one results in IPC_EEXIST (-2).

This list is based on IOS36.

Request Name Input Output OH0/OH1 Notes
0 (ioctlv) USBV0_IOCTLV_CTRLMSG 6 1 Both

Submits a control transfer.

  • in 0: u8 - bmRequestType
  • in 1: u8 - bmRequest
  • in 2: u16 - wValue (swapped)
  • in 3: u16 - wIndex (swapped)
  • in 4: u16 - wLength (swapped)
  • in 5: u8 - Unknown (00)
  • io 0: array of length wLength - Request data
1 (ioctlv) USBV0_IOCTLV_BLKMSG 2 1 Both

Submits a bulk transfer.

  • in 0: u8 - Endpoint
  • in 1: u16 - Length
  • io 0: array of length in[1] - Payload data

If io_vectors[0].size and the length don't match, returns IPC_EINVAL.

2 (ioctlv) USBV0_IOCTLV_INTRMSG 2 1 Both

Submits an interrupt transfer.

  • in 0: u8 - Endpoint
  • in 1: u16 - Length
  • io 0: array of length in[1] - Payload data

If io_vectors[0].size and the length don't match, returns IPC_EINVAL.

5 (ioctl) USBV0_IOCTL_SUSPENDDEV - - Both

Used to suspend a device.

6 (ioctl) USBV0_IOCTL_RESUMEDEV - - Both

Used to resume a device.

9 (ioctlv) USBV0_IOCTLV_ISOMSG 3 2 OH0

Submits an isochronous transfer.

  • in 0: u8 - Endpoint
  • in 1: u16 - Length
  • in 2: u8 - Number of isochronous packets
  • io 0: u16* of size in[2] - Sizes of the isochronous packets
  • io 1: array of length in[1] - Payload data
10 (ioctlv) USBV0_IOCTLV_LBLKMSG 2 1 OH0

Submits a bulk transfer. This is the same as ioctlv 1, except that this takes a u32 for the length instead of a u16.

  • in 0: u8 - Endpoint
  • in 1: u32 - Length
  • io 0: array of length in_vectors[1] - Payload data

If io_vectors[0].size and the length don't match, returns IPC_EINVAL.

26 (ioctl) USBV0_IOCTL_DEVREMOVALHOOK - - Both

Returns when the device is unplugged or reset (in which cases the return value is IPC_SUCCESS / IPC_OK), or when the child device (/dev/usb/oh0/%x/%x) is closed (IPC_ENOENT).

Only one hook is allowed at the same time. Further ioctls to set up another hook before it is triggered result in IPC_EEXIST.

29 (ioctl) USBV0_IOCTL_RESET_DEVICE - - OH0

Triggers the associated removal hook before resetting the USB device.

32 (ioctlv) ? ? ? OH0 Unknown. [check]


33 (ioctlv) USBV0_IOCTLV_SETISOALT ? ? OH0

Select an alternative interface configuration (for devices with isochronous endpoints, the active interface determines if usb bandwidth is reserved for iso transfers). This is an alternative method to using a control message to achieve the same effect.

This ioctlv does not seem to be present in all IOS versions (not in IOS36 at least).

Known Devices

  • /dev/usb/oh0/0b95/7720: ASIX AX88772 USB2.0 to Fast Ethernet Adapter (referenced in IOS eth driver)
  • /dev/usb/oh0/57e/308: Nintendo Wii Speak (microphone)
  • /dev/usb/oh0/46d/a03: Logitech microphone

Errors

-4 (IPC_EINVAL): might be caused by an invalid device fd

-7003: STALL (according to gc-linux)

-7004: STALL (according to gc-linux)

-7005: NAK (according to gc-linux)

-7008

-7022