Last active
January 14, 2022 22:05
-
-
Save JohnnyonFlame/725957ad80bbcd1f5c650e7ac536f16e to your computer and use it in GitHub Desktop.
PADEMU IOP Driver for the XBOX 360 wireless receiver.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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