2019-08-06 02:22:54 +02:00
|
|
|
#ifdef HW_RVL
|
|
|
|
#include <gccore.h>
|
|
|
|
|
2020-01-04 04:13:51 +01:00
|
|
|
#define RETRODE_VID 0x0403
|
|
|
|
#define RETRODE_PID 0x97C1
|
|
|
|
|
2019-12-30 20:45:20 +01:00
|
|
|
static bool setup = false;
|
|
|
|
static bool replugRequired = false;
|
|
|
|
static s32 deviceId = 0;
|
|
|
|
static u8 endpoint = 0;
|
|
|
|
static u8 bMaxPacketSize = 0;
|
2019-08-06 02:22:54 +02:00
|
|
|
|
|
|
|
static u32 jpRetrode[4];
|
|
|
|
|
2020-01-04 04:13:51 +01:00
|
|
|
static bool isRetrode(usb_device_entry dev)
|
|
|
|
{
|
|
|
|
return dev.vid == RETRODE_VID && dev.pid == RETRODE_PID;
|
|
|
|
}
|
|
|
|
|
2019-08-06 02:22:54 +02:00
|
|
|
static bool isRetrodeGamepad(usb_devdesc devdesc)
|
|
|
|
{
|
2020-01-04 04:13:51 +01:00
|
|
|
if (devdesc.idVendor != RETRODE_VID || devdesc.idProduct != RETRODE_PID ||
|
2019-08-06 02:22:54 +02:00
|
|
|
devdesc.configurations == NULL || devdesc.configurations->interfaces == NULL ||
|
|
|
|
devdesc.configurations->interfaces->endpoints == NULL)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return devdesc.configurations->interfaces->bInterfaceSubClass == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-12-30 20:45:20 +01:00
|
|
|
static int removal_cb(int result, void *usrdata)
|
2019-08-06 02:22:54 +02:00
|
|
|
{
|
2020-01-04 04:13:51 +01:00
|
|
|
s32 fd = (s32) usrdata;
|
|
|
|
if (fd == deviceId)
|
|
|
|
{
|
|
|
|
deviceId = 0;
|
|
|
|
}
|
|
|
|
return 1;
|
2019-12-30 20:45:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void open()
|
|
|
|
{
|
|
|
|
if (deviceId != 0)
|
2020-01-04 04:13:51 +01:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2019-12-30 20:45:20 +01:00
|
|
|
|
2019-08-06 02:22:54 +02:00
|
|
|
usb_device_entry dev_entry[8];
|
|
|
|
u8 dev_count;
|
|
|
|
if (USB_GetDeviceList(dev_entry, 8, USB_CLASS_HID, &dev_count) < 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Retrode has two entries in USB_GetDeviceList(), one for gamepads and one for SNES mouse
|
2020-01-04 04:13:51 +01:00
|
|
|
for (int i = 0; i < dev_count; ++i)
|
2019-08-06 02:22:54 +02:00
|
|
|
{
|
2020-01-04 04:13:51 +01:00
|
|
|
if (!isRetrode(dev_entry[i]))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2019-08-06 02:22:54 +02:00
|
|
|
s32 fd;
|
|
|
|
if (USB_OpenDevice(dev_entry[i].device_id, dev_entry[i].vid, dev_entry[i].pid, &fd) < 0)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
usb_devdesc devdesc;
|
2019-12-30 20:45:20 +01:00
|
|
|
if (USB_GetDescriptors(fd, &devdesc) < 0)
|
2019-08-06 02:22:54 +02:00
|
|
|
{
|
2019-12-30 20:45:20 +01:00
|
|
|
// You have to replug the Retrode controller!
|
|
|
|
replugRequired = true;
|
2019-08-06 02:22:54 +02:00
|
|
|
USB_CloseDevice(&fd);
|
2019-12-30 20:45:20 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isRetrodeGamepad(devdesc))
|
|
|
|
{
|
|
|
|
deviceId = fd;
|
2020-01-04 04:13:51 +01:00
|
|
|
replugRequired = false;
|
|
|
|
endpoint = getEndpoint(devdesc);
|
|
|
|
bMaxPacketSize = devdesc.bMaxPacketSize0;
|
|
|
|
USB_DeviceRemovalNotifyAsync(fd, &removal_cb, (void*) fd);
|
|
|
|
break;
|
2019-12-30 20:45:20 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-01-04 04:13:51 +01:00
|
|
|
USB_CloseDevice(&fd);
|
2019-08-06 02:22:54 +02:00
|
|
|
}
|
|
|
|
}
|
2019-12-30 20:45:20 +01:00
|
|
|
|
2020-01-04 04:13:51 +01:00
|
|
|
setup = true;
|
2019-08-06 02:22:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Retrode_ScanPads()
|
|
|
|
{
|
2019-12-30 20:45:20 +01:00
|
|
|
if (deviceId == 0)
|
2019-08-06 02:22:54 +02:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-12-30 20:45:20 +01:00
|
|
|
uint8_t ATTRIBUTE_ALIGN(32) buf[bMaxPacketSize];
|
2019-08-06 02:22:54 +02:00
|
|
|
|
2020-01-04 04:13:51 +01:00
|
|
|
// Retrode gamepad endpoint returns 5 bytes with gamepad events
|
2019-12-30 20:45:20 +01:00
|
|
|
if (USB_ReadIntrMsg(deviceId, endpoint, sizeof(buf), buf) != 5)
|
2019-08-06 02:22:54 +02:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// buf[0] contains the port returned
|
|
|
|
// you have to make 4 calls to get the status, even if you are only interested in one port
|
|
|
|
// because it is not sure which port is returned first
|
|
|
|
// 1 = left SNES
|
|
|
|
// 2 = right SNES
|
|
|
|
// 3 = left Genesis/MD
|
|
|
|
// 4 = right Genesis/MD
|
|
|
|
|
2019-12-30 20:45:20 +01:00
|
|
|
// Button layout
|
2020-01-04 04:13:51 +01:00
|
|
|
// A=3,10
|
|
|
|
// B=3,01
|
|
|
|
// X=3,20
|
|
|
|
// Y=3,02
|
|
|
|
// L=3,40
|
|
|
|
// R=3,80
|
|
|
|
// Up=2,9C
|
|
|
|
// Down=2,64
|
|
|
|
// Left=1,9C
|
|
|
|
// Right=1,64
|
|
|
|
// Start=3,08
|
|
|
|
// Select=3,04
|
2019-12-30 20:45:20 +01:00
|
|
|
|
|
|
|
u32 jp = 0;
|
2020-01-04 04:13:51 +01:00
|
|
|
jp |= ((buf[2] & 0x9C) == 0x9C) ? PAD_BUTTON_UP : 0;
|
2019-12-30 20:45:20 +01:00
|
|
|
jp |= ((buf[2] & 0x64) == 0x64) ? PAD_BUTTON_DOWN : 0;
|
|
|
|
jp |= ((buf[1] & 0x9C) == 0x9C) ? PAD_BUTTON_LEFT : 0;
|
|
|
|
jp |= ((buf[1] & 0x64) == 0x64) ? PAD_BUTTON_RIGHT : 0;
|
|
|
|
|
|
|
|
jp |= (buf[3] & 0x10) ? PAD_BUTTON_A : 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;
|
|
|
|
|
|
|
|
jp |= (buf[3] & 0x40) ? PAD_TRIGGER_L : 0;
|
|
|
|
jp |= (buf[3] & 0x80) ? PAD_TRIGGER_R : 0;
|
|
|
|
|
|
|
|
jp |= (buf[3] & 0x08) ? PAD_BUTTON_START : 0;
|
2020-01-04 04:13:51 +01:00
|
|
|
jp |= (buf[3] & 0x04) ? PAD_TRIGGER_Z : 0; // SNES select button maps to Z
|
2019-08-06 02:22:54 +02:00
|
|
|
|
|
|
|
// 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.
|
2019-12-30 20:45:20 +01:00
|
|
|
jpRetrode[buf[0] - 1] = jp;
|
2019-08-06 02:22:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
u32 Retrode_ButtonsHeld(int chan)
|
|
|
|
{
|
2020-01-04 04:13:51 +01:00
|
|
|
if(!setup)
|
|
|
|
{
|
|
|
|
open();
|
|
|
|
}
|
2019-12-30 20:45:20 +01:00
|
|
|
if (deviceId == 0)
|
2019-08-06 02:22:54 +02:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return jpRetrode[chan];
|
|
|
|
}
|
|
|
|
|
2019-12-30 20:45:20 +01:00
|
|
|
char* Retrode_Status()
|
|
|
|
{
|
2020-01-04 04:13:51 +01:00
|
|
|
open();
|
|
|
|
if (replugRequired)
|
|
|
|
return "please replug";
|
|
|
|
return deviceId ? "connected" : "not found";
|
2019-12-30 20:45:20 +01:00
|
|
|
}
|
|
|
|
|
2020-01-04 04:13:51 +01:00
|
|
|
#endif
|