Porting SNES9X RX 360 Controller support to GX (#887)

Title is self-explanatory.
This commit is contained in:
fancymelonhub 2019-12-30 14:45:20 -05:00 committed by dborth
parent 0fd4dc968b
commit e104bc6f9b
6 changed files with 438 additions and 39 deletions

View File

@ -37,6 +37,7 @@
#ifdef HW_RVL #ifdef HW_RVL
#include "utils/retrode.h" #include "utils/retrode.h"
#include "utils/xbox360.h"
#endif #endif
#define ANALOG_SENSITIVITY 30 #define ANALOG_SENSITIVITY 30
@ -335,6 +336,7 @@ UpdatePads()
#ifdef HW_RVL #ifdef HW_RVL
WiiDRC_ScanPads(); WiiDRC_ScanPads();
Retrode_ScanPads(); Retrode_ScanPads();
XBOX360_ScanPads();
WPAD_ScanPads(); WPAD_ScanPads();
#endif #endif
@ -536,6 +538,7 @@ static void decodepad (int chan)
u32 wiidrcp = userInput[chan].wiidrcdata.btns_h; u32 wiidrcp = userInput[chan].wiidrcdata.btns_h;
jp |= Retrode_ButtonsHeld(chan); jp |= Retrode_ButtonsHeld(chan);
jp |= XBOX360_ButtonsHeld(chan);
#endif #endif
/*** /***
@ -896,3 +899,10 @@ void SetDefaultButtonMap ()
SetControllers(); SetControllers();
} }
char* GetUSBControllerInfo()
{
static char info[50];
snprintf(info, 50, "Retrode: %s, XBOX360: %s", Retrode_Status(), XBOX360_Status());
return info;
}

View File

@ -35,5 +35,6 @@ void SetDefaultButtonMap ();
bool MenuRequested(); bool MenuRequested();
void SetupPads(); void SetupPads();
void UpdatePads(); void UpdatePads();
char* GetUSBControllerInfo();
#endif #endif

View File

@ -868,10 +868,12 @@ static void WindowCredits(void * ptr)
txt[i] = new GuiText("under the terms of the GNU General Public License (GPL) Version 2."); txt[i] = new GuiText("under the terms of the GNU General Public License (GPL) Version 2.");
txt[i]->SetPosition(0,y); i++; y+=20; txt[i]->SetPosition(0,y); i++; y+=20;
char iosVersion[20]; //char iosVersion[20];
char iosVersion[70]; // added 50 for Retrode info
#ifdef HW_RVL #ifdef HW_RVL
sprintf(iosVersion, "IOS: %ld", IOS_GetVersion()); //sprintf(iosVersion, "IOS: %ld", IOS_GetVersion());
snprintf(iosVersion, 70, "IOS: %ld / %s", IOS_GetVersion(), GetUSBControllerInfo());
#endif #endif
txt[i] = new GuiText(iosVersion, 18, (GXColor){0, 0, 0, 255}); txt[i] = new GuiText(iosVersion, 18, (GXColor){0, 0, 0, 255});

View File

@ -1,10 +1,11 @@
#ifdef HW_RVL #ifdef HW_RVL
#include <gccore.h> #include <gccore.h>
static bool retrodeSetup = false; static bool setup = false;
static s32 deviceIdRetrode = 0; static bool replugRequired = false;
static u8 endpointRetrode = 0; static s32 deviceId = 0;
static u8 bMaxPacketSizeRetrode = 0; static u8 endpoint = 0;
static u8 bMaxPacketSize = 0;
static u32 jpRetrode[4]; static u32 jpRetrode[4];
@ -29,8 +30,23 @@ static u8 getEndpoint(usb_devdesc devdesc)
return devdesc.configurations->interfaces->endpoints->bEndpointAddress; return devdesc.configurations->interfaces->endpoints->bEndpointAddress;
} }
static void openRetrode() static int removal_cb(int result, void *usrdata)
{ {
s32 fd = (s32) usrdata;
if (fd == deviceId)
{
deviceId = 0;
}
return 1;
}
static void open()
{
if (deviceId != 0)
{
return;
}
usb_device_entry dev_entry[8]; usb_device_entry dev_entry[8];
u8 dev_count; u8 dev_count;
if (USB_GetDeviceList(dev_entry, 8, USB_CLASS_HID, &dev_count) < 0) if (USB_GetDeviceList(dev_entry, 8, USB_CLASS_HID, &dev_count) < 0)
@ -39,7 +55,8 @@ static void openRetrode()
} }
// Retrode has two entries in USB_GetDeviceList(), one for gamepads and one for SNES mouse // Retrode has two entries in USB_GetDeviceList(), one for gamepads and one for SNES mouse
for (int i = 0; i < dev_count; ++i) int i;
for (i = 0; i < dev_count; ++i)
{ {
s32 fd; s32 fd;
if (USB_OpenDevice(dev_entry[i].device_id, dev_entry[i].vid, dev_entry[i].pid, &fd) < 0) if (USB_OpenDevice(dev_entry[i].device_id, dev_entry[i].vid, dev_entry[i].pid, &fd) < 0)
@ -48,33 +65,43 @@ static void openRetrode()
} }
usb_devdesc devdesc; usb_devdesc devdesc;
if (USB_GetDescriptors(fd, &devdesc) < 0 || !isRetrodeGamepad(devdesc)) if (USB_GetDescriptors(fd, &devdesc) < 0)
{ {
// You have to replug the Retrode controller!
replugRequired = true;
USB_CloseDevice(&fd); USB_CloseDevice(&fd);
continue; break;
}
if (isRetrodeGamepad(devdesc))
{
deviceId = fd;
replugRequired = false;
endpoint = getEndpoint(devdesc);
bMaxPacketSize = devdesc.bMaxPacketSize0;
USB_DeviceRemovalNotifyAsync(fd, &removal_cb, (void*) fd);
break;
}
else
{
USB_CloseDevice(&fd);
} }
deviceIdRetrode = fd;
endpointRetrode = getEndpoint(devdesc);
bMaxPacketSizeRetrode = devdesc.bMaxPacketSize0;
} }
setup = true;
} }
void Retrode_ScanPads() void Retrode_ScanPads()
{ {
if(!retrodeSetup) if (deviceId == 0)
{
retrodeSetup = true;
openRetrode();
}
if (deviceIdRetrode == 0)
{ {
return; return;
} }
uint8_t ATTRIBUTE_ALIGN(32) buf[bMaxPacketSizeRetrode]; uint8_t ATTRIBUTE_ALIGN(32) buf[bMaxPacketSize];
if (USB_ReadIntrMsg(deviceIdRetrode, endpointRetrode, sizeof(buf), buf) != 5) // Retrode gamepad endpoint returns 5 bytes with gamepad events
if (USB_ReadIntrMsg(deviceId, endpoint, sizeof(buf), buf) != 5)
{ {
return; return;
} }
@ -87,36 +114,61 @@ void Retrode_ScanPads()
// 3 = left Genesis/MD // 3 = left Genesis/MD
// 4 = right Genesis/MD // 4 = right Genesis/MD
// Retrode gamepad endpoint returns 5 bytes with gamepad events // Button layout
u32 jp12 = 0; // A=3,10
jp12 |= ((buf[2] & 0x9C) == 0x9C) ? PAD_BUTTON_UP : 0; // B=3,01
jp12 |= ((buf[2] & 0x64) == 0x64) ? PAD_BUTTON_DOWN : 0; // X=3,20
jp12 |= ((buf[1] & 0x9C) == 0x9C) ? PAD_BUTTON_LEFT : 0; // Y=3,02
jp12 |= ((buf[1] & 0x64) == 0x64) ? PAD_BUTTON_RIGHT : 0; // L=3,40
// R=3,80
// Up=2,9C
// Down=2,64
// Left=1,9C
// Right=1,64
// Start=3,08
// Select=3,04
jp12 |= (buf[3] & 0x10) ? PAD_BUTTON_A : 0; u32 jp = 0;
jp12 |= (buf[3] & 0x01) ? PAD_BUTTON_B : 0; jp |= ((buf[2] & 0x9C) == 0x9C) ? PAD_BUTTON_UP : 0;
jp12 |= (buf[3] & 0x20) ? PAD_BUTTON_X : 0; jp |= ((buf[2] & 0x64) == 0x64) ? PAD_BUTTON_DOWN : 0;
jp12 |= (buf[3] & 0x02) ? PAD_BUTTON_Y : 0; jp |= ((buf[1] & 0x9C) == 0x9C) ? PAD_BUTTON_LEFT : 0;
jp |= ((buf[1] & 0x64) == 0x64) ? PAD_BUTTON_RIGHT : 0;
jp12 |= (buf[3] & 0x40) ? PAD_TRIGGER_L : 0; jp |= (buf[3] & 0x10) ? PAD_BUTTON_A : 0;
jp12 |= (buf[3] & 0x80) ? PAD_TRIGGER_R : 0; jp |= (buf[3] & 0x01) ? PAD_BUTTON_B : 0;
jp |= (buf[3] & 0x20) ? PAD_BUTTON_X : 0;
jp |= (buf[3] & 0x02) ? PAD_BUTTON_Y : 0;
jp12 |= (buf[3] & 0x08) ? PAD_BUTTON_START : 0; jp |= (buf[3] & 0x40) ? PAD_TRIGGER_L : 0;
jp12 |= (buf[3] & 0x04) ? PAD_TRIGGER_Z : 0; // SNES select button maps to Z jp |= (buf[3] & 0x80) ? PAD_TRIGGER_R : 0;
jp |= (buf[3] & 0x08) ? PAD_BUTTON_START : 0;
jp |= (buf[3] & 0x04) ? PAD_TRIGGER_Z : 0; // SNES select button maps to Z
// Required, otherwise if the returned port isn't the one we are looking for, jp will be set to zero, // Required, otherwise if the returned port isn't the one we are looking for, jp will be set to zero,
// and held buttons are not possible w/o saving the state. // and held buttons are not possible w/o saving the state.
jpRetrode[buf[0] - 1] = jp12; jpRetrode[buf[0] - 1] = jp;
} }
u32 Retrode_ButtonsHeld(int chan) u32 Retrode_ButtonsHeld(int chan)
{ {
if (deviceIdRetrode == 0) if(!setup)
{
open();
}
if (deviceId == 0)
{ {
return 0; return 0;
} }
return jpRetrode[chan]; return jpRetrode[chan];
} }
#endif char* Retrode_Status()
{
open();
if (replugRequired)
return "please replug";
return deviceId ? "connected" : "not found";
}
#endif

316
source/utils/xbox360.c Normal file
View File

@ -0,0 +1,316 @@
#ifdef HW_RVL
#include <gccore.h>
#include <ogc/usb.h>
#define USB_CLASS_XBOX360 0xFF
static bool setup = false;
static bool replugRequired = false;
static s32 deviceId = 0;
static u8 endpoint_in = 0x81;
static u8 endpoint_out = 0x01; // some controllers require 0x02 for LED & rumble (updated below)
static u8 bMaxPacketSize = 20;
static u8 bConfigurationValue = 1;
static u8 ATTRIBUTE_ALIGN(32) buf[20];
static bool isReading = false;
static u32 jp = 0;
static u8 player = 0;
static u32 xboxButtonCount = 0;
static bool nextPlayer = false;
static u8 getEndpoint(usb_devdesc devdesc)
{
if (devdesc.configurations == NULL || devdesc.configurations->interfaces == NULL ||
devdesc.configurations->interfaces->endpoints == NULL)
{
return -1;
}
return devdesc.configurations->interfaces->endpoints->bEndpointAddress;
}
static bool isXBOX360(usb_devdesc devdesc)
{
return (devdesc.idVendor == 0x045e && devdesc.idProduct == 0x028e && getEndpoint(devdesc) == endpoint_in);
}
static int read(s32 device_id, u8 endpoint, u8 bMaxPacketSize0);
static void start_reading(s32 device_id, u8 endpoint, u8 bMaxPacketSize0)
{
if (isReading)
{
// already reading
return;
}
isReading = true;
read(deviceId, endpoint_in, bMaxPacketSize0);
}
static void stop_reading()
{
isReading = false;
}
static int read_cb(int res, void *usrdata)
{
if (!isReading)
{
// stop reading
return 1;
}
// NOTE: The four startup messages have res = 3 and can be ignored
// Button layout
// A=3,10
// B=3,20
// X=3,40
// Y=3,80
// Up=2,01
// Right=2,08
// Left=2,04
// Down=2,02
// L=3,01
// R=3,02
// L2=4,FF ; analog trigger
// R3=5,FF ; analog trigger
// L3=2,40 ; left hat button
// L3=2,80 ; right hat button
// XBOX=3,04
// Start=2,10
// Back=2,20
// LStickX=6/7,FF ; <low byte>,<high byte>
// LStickY=8/9,FF
// RStickX=10/11,FF
// RStickY=12/13,FF
if (res == 20)
{
jp = 0;
jp |= ((buf[2] & 0x01) == 0x01) ? PAD_BUTTON_UP : 0;
jp |= ((buf[2] & 0x02) == 0x02) ? PAD_BUTTON_DOWN : 0;
jp |= ((buf[2] & 0x04) == 0x04) ? PAD_BUTTON_LEFT : 0;
jp |= ((buf[2] & 0x08) == 0x08) ? PAD_BUTTON_RIGHT : 0;
jp |= ((buf[3] & 0x10) == 0x10) ? PAD_BUTTON_B : 0; // XBOX360 A button maps to B
jp |= ((buf[3] & 0x20) == 0x20) ? PAD_BUTTON_A : 0; // XBOX360 B button maps to A
jp |= ((buf[3] & 0x40) == 0x40) ? PAD_BUTTON_Y : 0; // XBOX360 X button maps to Y
jp |= ((buf[3] & 0x80) == 0x80) ? PAD_BUTTON_X : 0; // XBOX360 Y button maps to X
jp |= ((buf[3] & 0x01) == 0x01) ? PAD_TRIGGER_L : 0;
jp |= ((buf[3] & 0x02) == 0x02) ? PAD_TRIGGER_R : 0;
jp |= ((buf[2] & 0x10) == 0x10) ? PAD_BUTTON_START : 0;
jp |= ((buf[2] & 0x20) == 0x20) ? PAD_TRIGGER_Z : 0; // XBOX360 back button maps to Z
// triggers
jp |= (buf[4] > 128) ? PAD_TRIGGER_L : 0;
jp |= (buf[5] > 128) ? PAD_TRIGGER_R : 0;
// left stick
int16_t lx = (buf[7] << 8) | buf[6]; // [-32768, 32767]
int16_t ly = (buf[9] << 8) | buf[8]; // [-32768, 32767]
jp |= (ly > 16384) ? PAD_BUTTON_UP : 0;
jp |= (ly < -16384) ? PAD_BUTTON_DOWN : 0;
jp |= (lx < -16384) ? PAD_BUTTON_LEFT : 0;
jp |= (lx > 16384) ? PAD_BUTTON_RIGHT : 0;
// right stick
int16_t rx = (buf[11] << 8) | buf[10]; // [-32768, 32767]
int16_t ry = (buf[13] << 8) | buf[12]; // [-32768, 32767]
jp |= (ry > 16384) ? PAD_BUTTON_X : 0; // XBOX360 Y button maps to X
jp |= (ry < -16384) ? PAD_BUTTON_B : 0; // XBOX360 A button maps to B
jp |= (rx < -16384) ? PAD_BUTTON_Y : 0; // XBOX360 X button maps to Y
jp |= (rx > 16384) ? PAD_BUTTON_A : 0; // XBOX360 B button maps to A
// XBOX button to switch to next player
if ((buf[3] & 0x04) == 0x04)
{
xboxButtonCount++;
// count = 2 means you have to push the button 1x to switch players
// count = 10 means you have to push the button 5x to switch players
if (xboxButtonCount >= 2)
{
nextPlayer = true;
xboxButtonCount = 0;
}
}
}
// read again
read(deviceId, endpoint_in, bMaxPacketSize);
return 1;
}
// never call directly
static int read(s32 device_id, u8 endpoint, u8 bMaxPacketSize0)
{
// need to use async, because USB_ReadIntrMsg() blocks until a button is pressed
return USB_ReadIntrMsgAsync(device_id, endpoint, sizeof(buf), buf, &read_cb, NULL);
}
static void turnOnLED()
{
uint8_t ATTRIBUTE_ALIGN(32) buf[] = { 0x01, 0x03, 0x06 + player };
USB_WriteIntrMsg(deviceId, endpoint_out, sizeof(buf), buf);
}
static void increasePlayer()
{
player++;
if (player > 3)
{
player = 0;
}
turnOnLED();
}
void rumble(s32 device_id, u8 left, u8 right)
{
uint8_t ATTRIBUTE_ALIGN(32) buf[] = { 0x00, 0x08, 0x00, left, right, 0x00, 0x00, 0x00 };
USB_WriteIntrMsg(deviceId, endpoint_out, sizeof(buf), buf);
}
static int removal_cb(int result, void *usrdata)
{
s32 fd = (s32) usrdata;
if (fd == deviceId)
{
stop_reading();
deviceId = 0;
}
return 1;
}
// adapted from RetroArch input/drivers_hid/wiiusb_hid.c#wiiusb_get_description()
void wiiusb_get_description(usb_device_entry *device, usb_devdesc *devdesc)
{
unsigned char c;
unsigned i, k;
for (c = 0; c < devdesc->bNumConfigurations; c++)
{
const usb_configurationdesc *config = &devdesc->configurations[c];
for (i = 0; i < (int)config->bNumInterfaces; i++)
{
const usb_interfacedesc *inter = &config->interfaces[i];
for (k = 0; k < (int)inter->bNumEndpoints; k++)
{
const usb_endpointdesc *epdesc = &inter->endpoints[k];
bool is_int = (epdesc->bmAttributes & 0x03) == USB_ENDPOINT_INTERRUPT;
bool is_out = (epdesc->bEndpointAddress & 0x80) == USB_ENDPOINT_OUT;
bool is_in = (epdesc->bEndpointAddress & 0x80) == USB_ENDPOINT_IN;
if (is_int)
{
if (is_in)
{
//endpoint_in = epdesc->bEndpointAddress;
//endpoint_in_max_size = epdesc->wMaxPacketSize;
}
if (is_out)
{
endpoint_out = epdesc->bEndpointAddress;
//endpoint_out_max_size = epdesc->wMaxPacketSize;
}
}
}
break;
}
}
}
static void open()
{
if (deviceId != 0)
{
return;
}
usb_device_entry dev_entry[8];
u8 dev_count;
if (USB_GetDeviceList(dev_entry, 8, USB_CLASS_XBOX360, &dev_count) < 0)
{
return;
}
int i;
for (i = 0; i < dev_count; ++i)
{
s32 fd;
if (USB_OpenDevice(dev_entry[i].device_id, dev_entry[i].vid, dev_entry[i].pid, &fd) < 0)
{
continue;
}
usb_devdesc devdesc;
if (USB_GetDescriptors(fd, &devdesc) < 0)
{
// You have to replug the XBOX360 controller!
replugRequired = true;
USB_CloseDevice(&fd);
break;
}
if (isXBOX360(devdesc) && USB_SetConfiguration(fd, bConfigurationValue) >= 0)
{
deviceId = fd;
replugRequired = false;
wiiusb_get_description(&dev_entry[i], &devdesc);
turnOnLED();
USB_DeviceRemovalNotifyAsync(fd, &removal_cb, (void*) fd);
break;
}
else
{
USB_CloseDevice(&fd);
}
}
setup = true;
}
void XBOX360_ScanPads()
{
if (deviceId == 0)
{
return;
}
start_reading(deviceId, endpoint_in, bMaxPacketSize);
}
u32 XBOX360_ButtonsHeld(int chan)
{
if(!setup)
{
open();
}
if (deviceId == 0)
{
return 0;
}
if (nextPlayer)
{
nextPlayer = false;
increasePlayer();
}
if (chan != player)
{
return 0;
}
return jp;
}
char* XBOX360_Status()
{
open();
if (replugRequired)
return "please replug";
return deviceId ? "connected" : "not found";
}
#endif

18
source/utils/xbox360.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef _XBOX360_H_
#define _XBOX360_H_
#include <gctypes.h>
#ifdef __cplusplus
extern "C" {
#endif
bool XBOX360_ScanPads();
u32 XBOX360_ButtonsHeld(int chan);
char* XBOX360_Status();
#ifdef __cplusplus
}
#endif
#endif