/dev/usb/oh1

From WiiBrew
< /dev‎ | usb
Jump to navigation Jump to search

/dev/usb/oh1 is the device tree that handles requests to the internal USB bus. This bus is connected to the Bluetooth dongle. This service exports a high-level interface similar to that of other userspace USB interfaces like libusb.

Opening

The device name is of the form /dev/usb/oh1/VID/PID, where VID and PID are the Vendor ID and Product ID of the device that is to be opened, in hexadecimal, with no leading zeroes. There is no known method of addressing individual identical device with the same VID/PID.

If two devices have identical VID/PID, IOS_Open will choose one randomly [check] and a subsequent call to IOS_Open will open the other.

The VID/PID of the internal dongle is 57e/305.

ios_open("/dev/usb/oh1/57e/305"); //open the internal bluetooth dongle

Requests

Send Control Request

ioctlv(fd, '''0''', 6, 1, vectors);

Vectors:

I/IO Number Type Length Description
I 0 u8 1 bmRequestType
I 1 u8 1 bRequest
I 2 u16 2 wValue (little-endian)
I 3 u16 2 wIndex (little-endian)
I 4 u16 2 wLength (same as length of vector 6, but little-endian)
I 5 u8 1 unknown; set to zero.
IO 6 array wLength request data payload

For example, to reset the Bluetooth HCI (and therefore break the connection with the Wiimote), send a message with the parameters (0x20,0,0,0,0x0300,0) and data 03 0C 00. (0x0300 is the length, 3, in little-endian format). The operation returns the number of bytes read or written. Vectors are an array of the following structure:

 typedef struct _ioctlv
 {
     /** Pointer to _aligned_ data to be transfered. */
     void *data;
     /** Size of data in byte. */
     u32 len;
 } ioctlv;

Send Or Receive Bulk/Interrupt Message

ioctlv(fd, '''1''', 2, 1, vectors); //Bulk
ioctlv(fd, '''2''', 2, 1, vectors); //Interrupt

Vectors:

I/IO Number Type Length Description
I 0 u8 1 bEndpoint
I 1 u16 w wLength
IO 2 array wLength request data payload

The same request is used for both sending and receiving data. Which operation is used depends on the endpoint number (>=0x80 are input endpoints, <0x80 are output). The operation returns the number of bytes read or written.

Note that wLength is big-endian, unlike for control requests.

Example Code: Reset Bluetooth

Here is an example code which resets the internal bluetooth dongle and breaks the connection to the wiimote. To compile it, you need devkitPro/devkitPPC and libogc with WII support.

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <ogcsys.h>
#include <gccore.h>
#include <ogc/ipc.h>
#include <ogc/pad.h>

/** Enable interrupt flag. */
#define MSR_EE 0x00008000

/** Disable interrupts. */
#define _CPU_ISR_Disable( _isr_cookie ) \
  { register u32 _disable_mask = MSR_EE; \
    _isr_cookie = 0; \
    asm volatile ( \
	"mfmsr %0; andc %1,%0,%1; mtmsr %1" : \
	"=&r" ((_isr_cookie)), "=&r" ((_disable_mask)) : \
	"0" ((_isr_cookie)), "1" ((_disable_mask)) \
	); \
  }

/** Alignment required for USB structures (I don't know if this is 32 or less). */
#define USB_ALIGN __attribute__ ((aligned(32)))

static void *xfb = NULL;
static GXRModeObj *rmode = NULL;

/** Entry point for Twilight Hack. */
static void ( * Reload ) () = ( void ( * ) () ) 0x80001800;

/** Return buttons pressed on gamecube controller. */
u16 getButtons(void)
{
	PAD_ScanPads();

	return PAD_ButtonsDown(0);
}

char bluetoothResetData1[] USB_ALIGN = {
	/* bmRequestType */
	0x20
};

char bluetoothResetData2[] USB_ALIGN = {
	/* bmRequest */
	0x00
};

char bluetoothResetData3[] USB_ALIGN = {
	/* wValue */
	0x00, 0x00
};

char bluetoothResetData4[] USB_ALIGN = {
	/* wIndex */
	0x00, 0x00
};

char bluetoothResetData5[] USB_ALIGN = {
	/* wLength */
	0x03, 0x00
};

char bluetoothResetData6[] USB_ALIGN = {
	/* unknown; set to zero. */
	0x00
};

char bluetoothResetData7[] USB_ALIGN = {
	/* Mesage payload. */
	0x03, 0x0c, 0x00
};

/** Vectors of data transfered. */
ioctlv bluetoothReset[] USB_ALIGN = {
	{
		bluetoothResetData1,
		sizeof(bluetoothResetData1)
	},
	{
		bluetoothResetData2,
		sizeof(bluetoothResetData2)
	},
	{
		bluetoothResetData3,
		sizeof(bluetoothResetData3)
	},
	{
		bluetoothResetData4,
		sizeof(bluetoothResetData4)
	},
	{
		bluetoothResetData5,
		sizeof(bluetoothResetData5)
	},
	{
		bluetoothResetData6,
		sizeof(bluetoothResetData6)
	},
	{
		bluetoothResetData7,
		sizeof(bluetoothResetData7)
	}
};

/** Entry point of example program. */
int main(int argc, char **argv) {
	s32 fd;
	int rv;
	uint32_t level;

	VIDEO_Init();
	PAD_Init();
	
	switch(VIDEO_GetCurrentTvMode()) {
		case VI_NTSC:
			rmode = &TVNtsc480IntDf;
			break;
		case VI_PAL:
			rmode = &TVPal528IntDf;
			break;
		case VI_MPAL:
			rmode = &TVMpal480IntDf;
			break;
		default:
			rmode = &TVNtsc480IntDf;
			break;
	}

	xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
	console_init(xfb, 20, 20,
		rmode->fbWidth,
		rmode->xfbHeight,
		rmode->fbWidth * VI_DISPLAY_PIX_SZ);
	
	VIDEO_Configure(rmode);
	VIDEO_SetNextFramebuffer(xfb);
	VIDEO_SetBlack(FALSE);
	VIDEO_Flush();
	VIDEO_WaitVSync();
	if(rmode->viTVMode&VI_NON_INTERLACE) {
		VIDEO_WaitVSync();
	}

	printf("Wiimote Bluetooth Reset Example\n");

	printf("Open Bluetooth Dongle\n");
	fd = IOS_Open("/dev/usb/oh1/57e/305", 2 /* 2 = write, 1 = read */);
	printf("fd = %d\n", fd);

	printf("Closing connection to blutooth\n");
	rv = IOS_Ioctlv(fd, 0, 6, 1, bluetoothReset);
	printf("ret = %d\n", rv);

	IOS_Close(fd);

	printf("Press Z to load other application.\n");

	while (!(getButtons() & PAD_TRIGGER_Z)) {
	}

	/* Restart Twilight Hack. */
	_CPU_ISR_Disable(level);
	Reload();

	return 0;
}