/dev/usb/hid (v4)
/dev/usb/hid is used as an alternative interface to talk to USB HID devices such as the Rock Band instruments, USB keyboard or HID compatible game controller. There are at least 2 different versions which use different IOCTLs; only the earlier version (version 4) is documented here. It is found in IOS37, 60 and possibly some others.
List of IOCTLs
All memory should be allocated from MEM2 and 32-byte aligned. This structure is used for most commands:
// all multi-byte values are big endian
typedef struct {
u8 padding[16]; // anything you want can go here
s32 device_no;
union {
struct {
u8 bmRequestType;
u8 bmRequest;
u16 wValue;
u16 wIndex;
u16 wLength;
} control;
struct {
u32 endpoint;
u32 dLength;
} interrupt;
struct {
u8 bIndex;
} string;
};
void *data; // virtual pointer, not physical!
} req_args; // 32 bytes
Request number | Name | Input | Output | Notes |
---|---|---|---|---|
0 | GetDeviceChange | none | 0x600 bytes of device descriptors | Returns when a HID device is inserted/removed (or immediately when called for the first time). The return value will be -1 if forced to return by calling the Shutdown IOCTL. |
1 | SetSuspend | 8 bytes | none | Not actually implemented in IOS, returns 0 regardless of arguments |
2 | ControlMessage | 32 bytes of req_args | none | Read/Write a USB control message. device_no, bmRequestType, bmRequest, wValue, wIndex and wLength must be set (and data if wLength!=0). |
3 | InterruptMessage(IN) | 32 bytes of req_args | none | Read an interrupt message from an endpoint. device_no, endpoint, dLength and data must be set. |
4 | InterruptMessage(OUT) | 32 bytes of req_args | none | Send an interrupt message to an endpoint. device_no, endpoint, dLength and data must be set. |
5 | GetUSString | 32 bytes of req_args | none | Retrieves a descriptor string using language 0x0409(US). device_no, bIndex and data must be set. The returned string will be converted from unicode to char, data should be 255 bytes long to allow for the maximum length string. The actual length will be the return value. |
6 | GetVersion | none | 32 bytes (unused) | Gets the version of the /dev/usb/hid module (should return 0x40001). |
7 | Shutdown | none | none | Forces any pending GetDeviceChange requests to return immediately. |
8 | CancelInterrupt | 8 bytes | none | Cancels any pending interrupt transfers for an endpoint.
The first 4 bytes are the device ID. The 5th byte is the endpoint. |
Detailed Description
Unlike the regular IOS USB interface, all IPC commands are done via the /dev/usb/hid fd. There is no IOCTL to get a list of currently available devices, but there is an IOCTL that returns when a HID device is inserted or removed (it will also return immediately the first time it is called after IOS is loaded). It can also be forced to return immediately if the shutdown IOCTL is used, to ensure no dangling requests are left when your program exits.
GetDeviceChange
void *device_desc = memalign(32, 0x600); // should be from MEM2
ios_ioctl_async(fd, /*GetDeviceChange*/0, NULL, 0, device_desc, 0x600, cb, device_desc); // cb will be called when there is a device change, with the device_desc buffer as a parameter
The device_desc buffer will contain a set of descriptors for each device. At the beginning of each device's section will be two 4 bytes values, containing the total size of this device's data and the device's id number (used for device_no in req_args). The size of each individual descriptor within the data is padded to 4 bytes, the total size includes this padding. A size value of 0xFFFFFFFF indicates there are no more devices. For example here is what is returned when a Rock Band music keyboard is attached:
00000000 00 00 00 44 00 00 00 00 12 01 01 10 00 00 00 08 00000010 1B AD 33 30 00 05 00 00 00 01 00 00 09 02 00 20 00000020 01 01 00 80 32 00 00 00 09 04 00 00 02 03 00 00 00000030 00 00 00 00 07 05 02 03 00 40 01 00 07 05 81 03 00000040 00 40 0A 00 FF FF FF FF 00 00 00 00 00 00 00 00 ... 0x5B0 bytes unused
Breaking this down into 4-bytes words, it can be parsed as follows:
u32 *desc = (u32*)device_desc; desc[0x00] = 0x44; // size of this device's data desc[0x01] = 0; // this device's id (used to issue commands to this device) // DEVICE DESCRIPTOR desc[0x02] = 0x12010110; // bLength=18, bDescriptorType=01, bcdUSB=1.10 desc[0x03] = 0x00000008; // bDeviceClass=0, bDeviceSubClass=0, bDeviceProtocol=0, bMaxPacketSize=8 desc[0x04] = 0x1BAD3330; // VID=0x1BAD, PID=0x3330 desc[0x05] = 0x00050000; // bcdDevice=5, iManufacturer=0, iProduct=0 desc[0x06] = 0x00010000; // iSerialNumber=0, bNumConfigurations=1, 2 bytes padding // CONFIGURATION DESCRIPTOR desc[0x07] = 0x09020020; // bLength=9, bDescriptorType=2, wTotalLength=32 desc[0x08] = 0x01010080; // bNumInterfaces=1, bConfigurationValue=1, iConfiguration=0, bmAttributes=0x80 desc[0x09] = 0x32000000; // bMaxPower=100ma (?), 3 bytes padding // INTERFACE DESCRIPTOR desc[0x0A] = 0x09040000; // bLength=9, bDescriptorType=4, bInterfaceNumber=0, bAlternateSetting=0 desc[0x0B] = 0x02030000; // bNumEndpoints=2, bInterfaceClass=3(HID), bInterfaceSubClass=0, bInterfaceProtocol=0 desc[0x0C] = 0x00000000; // iInterface, 3 bytes padding // ENDPOINT DESCRIPTOR (OUT) desc[0x0D] = 0x07050203; // bLength=7, bDescriptorType=5, bEndpointAddress=0x02, bmAttributes=0x03(Interrupt) desc[0x0E] = 0x00400100; // wMaxPacketSize=64, bInterval=1, 1 byte padding // ENDPOINT DESCRIPTOR (IN) desc[0x0F] = 0x07058103; // bLength=7, bDescriptorType=5, bEndpointAddress=0x81, bmAttributes=0x03(Interrupt) desc[0x10] = 0x00400A00; // wMaxPacketSize=64, bInterval=10, 1 byte padding // total size = 0x11*sizeof(u32) = 0x44. End of first device's data. desc[0x11] = 0xFFFFFFFF; // no more devices
SetSuspend
This does nothing; supposedly it suspends or resumes a device, but it's not implemented in the IOS code.
ControlMessage
Issues a control message to a device. Takes a filled out req_args struct as a parameter; device_no specifies the target device while control.bmRequestType, control.bmRequest, control.wValue, control.wIndex and control.wLength should be filled according to the message being sent. If there is extra input/output information associated with the message it should be pointed to by data (control.wLength should specify the length as per the USB standard).
InterruptMessage(IN)
Fetches an interrupt message from the device. Takes a filled out req_args struct as a parameter; device_no specifies the target device, interrupt.endpoint specifies which interface endpoint should be used for the transfer, interrupt.dLength specifies the length of the transfer and data points to a destination buffer. The specified endpoint should be an IN endpoint.
InterruptMessage(OUT)
Sends an interrupt message to the device. Same as above, but data points to a source buffer and the specified endpoint should be an OUT endpoint.
GetUSString
Requests the specified string descriptor from the device and converts it from unicode. Takes a filled out req_args struct as a parameter; device_no specifies the target device, string.bIndex is the index of the string descriptor and data is the destination buffer. Data should be at least 255 bytes long to allow for the longest possible string. Any characters that cannot be represented correctly by a single byte will be translated to '?'. The return value specifies the length of the returned string.
GetVersion
Retrieves the version of the /dev/usb/hid interface; currently this should return 0x40001. The output buffer is not used.
Shutdown
Cancels a pending GetDeviceChange request, making it return immediately (the returned value will be -1). This is used to "clean up" the IOS state.
CancelInterrupt
Similar to Shutdown, but forces a pending InterruptMessage IOCTL to return immediately.