Skip to content

Instantly share code, notes, and snippets.

@JohnnyonFlame
Last active January 14, 2022 22:05
Show Gist options
  • Save JohnnyonFlame/725957ad80bbcd1f5c650e7ac536f16e to your computer and use it in GitHub Desktop.
Save JohnnyonFlame/725957ad80bbcd1f5c650e7ac536f16e to your computer and use it in GitHub Desktop.
PADEMU IOP Driver for the XBOX 360 wireless receiver.
#include "types.h"
#include "loadcore.h"
#include "stdio.h"
#include "sifrpc.h"
#include "sysclib.h"
#include "usbd.h"
#include "usbd_macro.h"
#include "thbase.h"
#include "thsemap.h"
#include "ds34usb.h"
#include "sys_utils.h"
//#define DPRINTF(x...) printf(x)
#define DPRINTF(x...)
#define REQ_USB_OUT (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)
#define REQ_USB_IN (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE)
static u8 usb_buf[MAX_BUFFER_SIZE + 20] __attribute((aligned(4))) = {0};
static u8 usb_buf_out[MAX_BUFFER_SIZE + 20] __attribute((aligned(4))) = {0};
int usb_probe(int devId);
int usb_connect(int devId);
int usb_disconnect(int devId);
static void usb_config_set(int result, int count, void *arg);
UsbDriver usb_driver = {NULL, NULL, "ds34usb", usb_probe, usb_connect, usb_disconnect};
xpad_device xpad = {};
void ds34usb_set_mode(int mode, int lock, int port)
{
//TODO:: Figure this out
}
static void usb_cmd_cb(int resultCode, int bytes, void *arg)
{
SignalSema(xpad.cmd_sema);
}
void ds34usb_set_rumble(u8 lrum, u8 rrum, int port)
{
if (port >= MAX_PADS)
return;
xpad.gamepads[port].lrum = lrum;
xpad.gamepads[port].rrum = rrum;
}
void ds34usb_reset()
{
usb_disconnect(xpad.devId);
}
int ds34usb_get_status(int port)
{
int ret;
if (port >= MAX_PADS)
return 0;
WaitSema(xpad.gamepads[port].sema);
if (xpad.gamepads[port].paired)
ret = DS34USB_STATE_RUNNING;
else
ret = DS34USB_STATE_DISCONNECTED;
SignalSema(xpad.gamepads[port].sema);
return ret;
}
int usb_probe(int devId)
{
UsbDeviceDescriptor *device = NULL;
DPRINTF("XPAD: probe: devId=%i\n", devId);
device = (UsbDeviceDescriptor *)UsbGetDeviceStaticDescriptor(devId, NULL, USB_DT_DEVICE);
if (device == NULL) {
DPRINTF("XPAD: Error - Couldn't get device descriptor\n");
return 0;
}
if (device->idVendor == XPAD_VID && device->idProduct == XPAD_PID)
return 1;
return 0;
}
int usb_connect(int devId)
{
UsbDeviceDescriptor *device;
UsbConfigDescriptor *config;
UsbEndpointDescriptor *endpoint;
DPRINTF("XPAD: connect: devId=%i\n", devId);
if (xpad.devId >= 0) {
DPRINTF("XPAD: Device already present. Ignoring.\n");
return 0;
}
WaitSema(xpad.sema);
xpad.devId = devId;
device = (UsbDeviceDescriptor *)UsbGetDeviceStaticDescriptor(devId, NULL, USB_DT_DEVICE);
config = (UsbConfigDescriptor *)UsbGetDeviceStaticDescriptor(devId, device, USB_DT_CONFIG);
xpad.ctrlEndp = UsbOpenEndpoint(devId, NULL);
endpoint = (UsbEndpointDescriptor *)UsbGetDeviceStaticDescriptor(devId, NULL, USB_DT_ENDPOINT);
int i, j;
for (i = 0; i < MAX_PADS; i++) {
for (j = 0; j < 2; j++) {
if (endpoint->bmAttributes == USB_ENDPOINT_XFER_INT) {
if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN && xpad.gamepads[i].inEndp < 0) {
xpad.gamepads[i].inEndp = UsbOpenEndpoint(devId, endpoint);
DPRINTF("XPAD: register outEndp endpoint id =%i addr=%02X packetSize=%i\n",
xpad.gamepads[i].inEndp, endpoint->bEndpointAddress,
(unsigned short int)endpoint->wMaxPacketSizeHB << 8 | endpoint->wMaxPacketSizeLB);
} else if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT && xpad.gamepads[i].outEndp < 0) {
xpad.gamepads[i].outEndp = UsbOpenEndpoint(devId, endpoint);
DPRINTF("XPAD: register inEndp id =%i addr=%02X packetSize=%i\n",
xpad.gamepads[i].outEndp, endpoint->bEndpointAddress,
(unsigned short int)endpoint->wMaxPacketSizeHB << 8 | endpoint->wMaxPacketSizeLB);
}
}
endpoint = (UsbEndpointDescriptor *)((char*)endpoint + endpoint->bLength);
}
for (j = 0; j < 2; j++) //skip headset endpoints
endpoint = (UsbEndpointDescriptor *)((char*)endpoint + endpoint->bLength);
}
xpad.status = DS34USB_STATE_CONNECTED;
UsbSetDeviceConfiguration(xpad.ctrlEndp, config->bConfigurationValue, usb_config_set, 0);
SignalSema(xpad.sema);
return 1;
}
int usb_disconnect(int devId)
{
DPRINTF("XPAD: disconnect: devId=%i\n", devId);
if (xpad.devId != devId && xpad.devId < 0)
return 0;
PollSema(xpad.sema);
xpad.status = DS34USB_STATE_DISCONNECTED;
int i;
for (i = 0; i < MAX_PADS; i++) {
if (xpad.gamepads[i].inEndp >= 0)
UsbCloseEndpoint(xpad.gamepads[i].inEndp);
if (xpad.gamepads[i].outEndp >= 0)
UsbCloseEndpoint(xpad.gamepads[i].outEndp);
xpad.gamepads[i].inEndp = -1;
xpad.gamepads[i].outEndp = -1;
xpad.gamepads[i].paired = 0;
}
if (xpad.ctrlEndp >= 0)
UsbCloseEndpoint(xpad.ctrlEndp);
xpad.ctrlEndp = -1;
xpad.devId = -1;
SignalSema(xpad.sema);
return 0;
}
static void usb_data_cb(int resultCode, int bytes, void *arg)
{
int pad = (int)arg;
WaitSema(xpad.gamepads[pad].sema);
if (bytes >= 2) {
if (bytes == 2) {
if (usb_buf[0] & 0x08) {
DPRINTF("XPAD: Controller %d %s.\n", pad, (usb_buf[1] & 0x80) ? "connected" : "disconnected");
xpad.gamepads[pad].paired = (usb_buf[1] & 0x80) != 0;
//TODO:: Improve usb output buffers, maybe one per gamepad port?
//Set controller LED on!
//TODO:: Delegate this to the proper function
if (usb_buf[1] & 0x80) {
PollSema(xpad.cmd_sema);
usb_buf_out[0] = 0x00;
usb_buf_out[1] = 0x00;
usb_buf_out[2] = 0x08;
usb_buf_out[3] = 0x40 + (pad + 2);
usb_buf_out[4] = 0x00;
usb_buf_out[5] = 0x00;
usb_buf_out[6] = 0x00;
usb_buf_out[7] = 0x00;
usb_buf_out[8] = 0x00;
usb_buf_out[9] = 0x00;
usb_buf_out[10] = 0x00;
usb_buf_out[11] = 0x00;
UsbInterruptTransfer(xpad.gamepads[pad].outEndp, usb_buf_out, 12, usb_cmd_cb, 0);
}
}
} else if (usb_buf[1] == 0x01) { //Input packet- process it.
struct Xbox360Msg *msg = (struct Xbox360Msg*)&usb_buf[4];
if (msg->type == 0x0 && msg->length == 0x13) {
xpad.gamepads[pad].data[0] = ~(
msg->back |
msg->thumb_l << 1 |
msg->thumb_r << 2 |
msg->start << 3 |
msg->dpad_up << 4 |
msg->dpad_right << 5 |
msg->dpad_down << 6 |
msg->dpad_left << 7);
xpad.gamepads[pad].data[1] = ~(
(msg->lt > 32) |
(msg->rt > 32) << 1 |
msg->lb << 2 |
msg->rb << 3 |
msg->y << 4 |
msg->b << 5 |
msg->a << 6 |
msg->x << 7);
xpad.gamepads[pad].data[2] = (128 + (msg->x2 / 256)); //rx
xpad.gamepads[pad].data[3] = (127 - (msg->y2 / 256)); //ry
xpad.gamepads[pad].data[4] = (128 + (msg->x1 / 256)); //lx
xpad.gamepads[pad].data[5] = (127 - (msg->y1 / 256)); //ly
xpad.gamepads[pad].data[6] = msg->dpad_right * 255; //right
xpad.gamepads[pad].data[7] = msg->dpad_left * 255; //left
xpad.gamepads[pad].data[8] = msg->dpad_up * 255; //up
xpad.gamepads[pad].data[9] = msg->dpad_down * 255; //down
xpad.gamepads[pad].data[10] = msg->y * 255; //triangle
xpad.gamepads[pad].data[11] = msg->b * 255; //circle
xpad.gamepads[pad].data[12] = msg->a * 255; //cross
xpad.gamepads[pad].data[13] = msg->x * 255; //square
xpad.gamepads[pad].data[14] = msg->lt * 255; //L1
xpad.gamepads[pad].data[15] = msg->rt * 255; //R1
xpad.gamepads[pad].data[16] = msg->lt; //L2
xpad.gamepads[pad].data[17] = msg->rt; //R2
}
}
}
UsbInterruptTransfer(xpad.gamepads[pad].inEndp, usb_buf, MAX_BUFFER_SIZE, usb_data_cb, (void *)pad);
SignalSema(xpad.gamepads[pad].sema);
}
static void usb_config_set(int result, int count, void *arg)
{
WaitSema(xpad.sema);
/* TODO:: Setup interrupts */
int i;
for (i = 0; i < MAX_PADS; i++) {
WaitSema(xpad.gamepads[i].sema);
UsbInterruptTransfer(xpad.gamepads[i].inEndp, usb_buf, MAX_BUFFER_SIZE, usb_data_cb, (void *)i);
SignalSema(xpad.gamepads[i].sema);
}
xpad.status = DS34USB_STATE_CONFIGURED;
SignalSema(xpad.sema);
}
void ds34usb_get_data(char *dst, int size, int port)
{
int ret;
if (port >= MAX_PADS)
return;
WaitSema(xpad.gamepads[port].sema);
mips_memcpy(dst, xpad.gamepads[port].data, size);
PollSema(xpad.cmd_sema);
usb_buf_out[0] = 0x00;
usb_buf_out[1] = 0x01;
usb_buf_out[2] = 0x0f;
usb_buf_out[3] = 0xc0;
usb_buf_out[4] = 0x00;
usb_buf_out[5] = xpad.gamepads[port].lrum;
usb_buf_out[6] = xpad.gamepads[port].rrum;
usb_buf_out[7] = 0x00;
usb_buf_out[8] = 0x00;
usb_buf_out[9] = 0x00;
usb_buf_out[10] = 0x00;
usb_buf_out[11] = 0x00;
UsbInterruptTransfer(xpad.gamepads[port].outEndp, usb_buf_out, 12, usb_cmd_cb, 0);
SignalSema(xpad.gamepads[port].sema);
}
int ds34usb_init(u8 pads, u8 options)
{
xpad.devId = -1;
xpad.sema = CreateMutex(IOP_MUTEX_UNLOCKED);
xpad.cmd_sema = CreateMutex(IOP_MUTEX_UNLOCKED);
if (xpad.cmd_sema < 0 || xpad.sema < 0) {
DPRINTF("XPAD: Failed to allocate I/O semaphore.\n");
return 0;
}
xpad.ctrlEndp = 0;
xpad.status = DS34USB_STATE_DISCONNECTED;
u8 pad;
for (pad = 0; pad < MAX_PADS; pad++) {
xpad.gamepads[pad].sema = CreateMutex(IOP_MUTEX_UNLOCKED);
if (xpad.gamepads[pad].sema < 0) {
DPRINTF("XPAD: Failed to allocate I/O semaphore.\n");
return 0;
}
xpad.gamepads[pad].inEndp = -1;
xpad.gamepads[pad].outEndp = -1;
xpad.gamepads[pad].enabled = 0;
xpad.gamepads[pad].led = 0;
xpad.gamepads[pad].paired = 0;
xpad.gamepads[pad].data[0] = 0xFF;
xpad.gamepads[pad].data[1] = 0xFF;
mips_memset(&xpad.gamepads[pad].data[2], 0x7F, 4);
mips_memset(&xpad.gamepads[pad].data[6], 0x00, 12);
}
if (UsbRegisterDriver(&usb_driver) != USB_RC_OK) {
DPRINTF("XPAD: Error registering USB devices\n");
return 0;
}
return 1;
}
#ifndef _DS34USB_H_
#define _DS34USB_H_
#include "irx.h"
#define XPAD_VID 0x045E
#define XPAD_PID 0x0719
#define MAX_BUFFER_SIZE 64 // Size of general purpose data buffer
#define MAX_PADS 1
typedef struct _iface_xpad
{
int sema;
int inEndp;
int outEndp;
u8 enabled;
u8 paired;
u8 lrum;
u8 rrum;
u8 led;
u8 data[18];
u8 type;
} xpad_iface;
typedef struct _usb_xpad
{
int devId;
int sema;
int cmd_sema;
int ctrlEndp;
u8 status;
xpad_iface gamepads[MAX_PADS];
} xpad_device;
enum eDS34USBStatus {
DS34USB_STATE_DISCONNECTED = 0x00,
DS34USB_STATE_AUTHORIZED = 0x01,
DS34USB_STATE_CONFIGURED = 0x02,
DS34USB_STATE_CONNECTED = 0x04,
DS34USB_STATE_RUNNING = 0x08,
};
enum eHID {
// {{{
/* HID event flag */
HID_FLAG_STATUS_REPORTED = 0x01,
HID_FLAG_BUTTONS_CHANGED = 0x02,
HID_FLAG_EXTENSION = 0x04,
HID_FLAG_COMMAND_SUCCESS = 0x08,
/* USB HID Transaction Header (THdr) */
HID_USB_GET_REPORT_FEATURE = 0x03,
HID_USB_SET_REPORT_OUTPUT = 0x02,
HID_USB_DATA_INPUT = 0x01,
/* Defines of various parameters for PS3 Game controller reports */
//PS3_F4_REPORT_ID = 0xF4,
//PS3_F4_REPORT_LEN = 0x04,
//
//PS3_01_REPORT_ID = 0x01,
//PS3_01_REPORT_LEN = 0x30,
//
//PS4_02_REPORT_ID = 0x02,
//PS4_11_REPORT_ID = 0x11,
//PS4_11_REPORT_LEN = 0x4D,
// }}}
};
struct Xbox360Msg
{
// -------------------------
u8 type :8; // always 0
u8 length :8; // always 0x14
// data[2] ------------------
u8 dpad_up :1;
u8 dpad_down :1;
u8 dpad_left :1;
u8 dpad_right :1;
u8 start :1;
u8 back :1;
u8 thumb_l :1;
u8 thumb_r :1;
// data[3] ------------------
u8 lb :1;
u8 rb :1;
u8 guide :1;
u8 dummy1 :1; // always 0
u8 a :1; // green
u8 b :1; // red
u8 x :1; // blue
u8 y :1; // yellow
// data[4] ------------------
u8 lt :8;
u8 rt :8;
// data[6] ------------------
s16 x1 :16;
s16 y1 :16;
// data[10] -----------------
s16 x2 :16;
s16 y2 :16;
// data[14]; ----------------
unsigned int dummy2 :32; // always 0
unsigned int dummy3 :16; // always 0
} __attribute__((__packed__));
void mips_memcpy(void *, const void *, unsigned);
void mips_memset(void *, int, unsigned);
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment