So, I'm dabbling with some Wii homebrew. The goal is to probe any JoyBus devices via the Serial Interface (SI). To start off, I simply want to send the ID command (0x00) and read the response to get the device ID. I'm using the latest DevkitPro and DevkitPPC. Here is the source code:
Attached below are the .dol and .elf files as well (in the ZIP file). I believe this is the (mostly) correct way of doing things. Clear the SI I/O buffer, write the command to the buffer, then initiate a transfer via SICOMCSR. I only had Dolphin's source code + Yet Another Gamecube Doc to go off of, and I haven't touched the GC/Wii Serial Interface in years (not since 2014 I guess).
Anyway, the latest master builds of Dolphin run the code as expected. Every second, it prints out something like...
This is normally what I'd want/expect to see. 0x09000000 represents a standard GC controller attached to a given controller port, and 0x8 is Dolphin's default response an unconnected device. I can change the attached device in Dolphin to something like the GC Keyboard or Steering Wheel or Dancing Mat, and it updates the ID accordingly.
Unfortunately, running this on real hardware (Nintendo Wii RVL-001) gives different results. I loaded up the .dol through the Homebrew menu and got results like this (Standard 1st-party GC Controller attached to Port 1):
So, my question is, does anyone have any idea what's going on? The zeroes for the rest of the IDs are not concerning, but the first one is totally unexpected.
Code:
#include <stdio.h>
#include <stdlib.h>
#include <gccore.h>
#include <wiiuse/wpad.h>
static void *xfb = NULL;
static GXRModeObj *rmode = NULL;
vu32* const si_reg = (u32*)0xCD006400;
vu32* const si_buf = (u32*)0xCD006480;
//Generates 32-bit value that can be written to SICOMCSR to start a transfer on a given channel
u32 generate_com_csr(u32 channel, u32 in_len, u32 out_len)
{
u32 com_csr = 0;
in_len &= 0x7F;
out_len &= 0x7F;
channel &= 0x3;
//Channel
com_csr |= (channel << 25);
//Channel Enable
com_csr |= (1 << 24);
//Output Length
com_csr |= (out_len << 16);
//Input Length
com_csr |= (in_len << 8);
//Command Enable
com_csr |= (1 << 7);
//Channel 2?
com_csr |= (channel << 1);
//Callback Enable
com_csr |= (0 << 6);
//TSTART
com_csr |= 1;
return com_csr;
}
//Sends the JOYBUS command 0x00 to the given channel
//Used to find out the device ID (written to SI buffer)
void ping_ID(u32 channel)
{
//Clear SI buffer
for(int x = 0; x < 0x80; x++) { si_buf[x] = 0; }
//Setup JOYBUS command 0x00
si_buf[0] = 0x00;
//Write to SICOMCSR to start transfer, wait for any pending transfers first
while(si_reg[13] & 0x1) { }
si_reg[13] = generate_com_csr(channel, 4, 2);
//Wait for next SICOMCSR transfer to finish
while(si_reg[13] & 0x1) { }
}
//---------------------------------------------------------------------------------
int main(int argc, char **argv) {
//---------------------------------------------------------------------------------
// Initialise the video system
VIDEO_Init();
// This function initialises the attached controllers
WPAD_Init();
// Obtain the preferred video mode from the system
// This will correspond to the settings in the Wii menu
rmode = VIDEO_GetPreferredMode(NULL);
// Allocate memory for the display in the uncached region
xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
// Initialise the console, required for printf
console_init(xfb,20,20,rmode->fbWidth,rmode->xfbHeight,rmode->fbWidth*VI_DISPLAY_PIX_SZ);
// Set up the video registers with the chosen mode
VIDEO_Configure(rmode);
// Tell the video hardware where our display memory is
VIDEO_SetNextFramebuffer(xfb);
// Make the display visible
VIDEO_SetBlack(FALSE);
// Flush the video register changes to the hardware
VIDEO_Flush();
// Wait for Video setup to complete
VIDEO_WaitVSync();
if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync();
u32 loop = 0;
u32 vcount = 0;
while(1) {
vcount++;
if(vcount == 60)
{
ping_ID(0);
u32 id_1 = si_buf[0];
ping_ID(1);
u32 id_2 = si_buf[0];
ping_ID(2);
u32 id_3 = si_buf[0];
ping_ID(3);
u32 id_4 = si_buf[0];
printf("%x\n", loop++);
printf("ID 1 -> 0x%x\n", id_1);
printf("ID 2 -> 0x%x\n", id_2);
printf("ID 3 -> 0x%x\n", id_3);
printf("ID 4 -> 0x%x\n\n", id_4);
vcount = 0;
}
// Call WPAD_ScanPads each loop, this reads the latest controller states
WPAD_ScanPads();
// WPAD_ButtonsDown tells us which buttons were pressed in this loop
// this is a "one shot" state which will not fire again until the button has been released
u32 pressed = WPAD_ButtonsDown(0);
// We return to the launcher application via exit
if ( pressed & WPAD_BUTTON_HOME ) exit(0);
// Wait for the next frame
VIDEO_WaitVSync();
}
return 0;
}
Attached below are the .dol and .elf files as well (in the ZIP file). I believe this is the (mostly) correct way of doing things. Clear the SI I/O buffer, write the command to the buffer, then initiate a transfer via SICOMCSR. I only had Dolphin's source code + Yet Another Gamecube Doc to go off of, and I haven't touched the GC/Wii Serial Interface in years (not since 2014 I guess).
Anyway, the latest master builds of Dolphin run the code as expected. Every second, it prints out something like...
Code:
ID 1 -> 0x09000000
ID 2 -> 0x8
ID 3 -> 0x8
ID 4 -> 0x8
This is normally what I'd want/expect to see. 0x09000000 represents a standard GC controller attached to a given controller port, and 0x8 is Dolphin's default response an unconnected device. I can change the attached device in Dolphin to something like the GC Keyboard or Steering Wheel or Dancing Mat, and it updates the ID accordingly.
Unfortunately, running this on real hardware (Nintendo Wii RVL-001) gives different results. I loaded up the .dol through the Homebrew menu and got results like this (Standard 1st-party GC Controller attached to Port 1):
Code:
ID 1 -> 0x20000300
ID 2 -> 0x0
ID 3 -> 0x0
ID 4 -> 0x0
So, my question is, does anyone have any idea what's going on? The zeroes for the rest of the IDs are not concerning, but the first one is totally unexpected.