|
|
|
@ -37,6 +37,7 @@
|
|
|
|
|
#include "XidGamepad.h"
|
|
|
|
|
#include "USBDevice.h"
|
|
|
|
|
#include "Common/Input/InputConfig.h"
|
|
|
|
|
#include "OHCI.h"
|
|
|
|
|
|
|
|
|
|
#define LOG_STR_GAMEPAD "Gamepad:"
|
|
|
|
|
|
|
|
|
@ -93,9 +94,10 @@ struct USBXIDState {
|
|
|
|
|
|
|
|
|
|
const XIDDesc* xid_desc; // xid-specific descriptor
|
|
|
|
|
|
|
|
|
|
bool in_dirty; // indicates a change in the button's state
|
|
|
|
|
XIDGamepadReport in_state; // Get_Report struct
|
|
|
|
|
XIDGamepadOutputReport out_state; // Ser_Report struct
|
|
|
|
|
XIDGamepadReport in_state_capabilities; // Get_Capabilities struct (in)
|
|
|
|
|
XIDGamepadOutputReport out_state; // Set_Report struct
|
|
|
|
|
XIDGamepadOutputReport out_state_capabilities; // Get_Capabilities struct (out)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
USBDescIface::USBDescIface()
|
|
|
|
@ -166,32 +168,35 @@ XIDDesc::XIDDesc()
|
|
|
|
|
{
|
|
|
|
|
bLength = 0x10;
|
|
|
|
|
bDescriptorType = USB_DT_XID;
|
|
|
|
|
bcdXid = 1;
|
|
|
|
|
bcdXid = 0x100;
|
|
|
|
|
bType = 1;
|
|
|
|
|
bSubType = 1;
|
|
|
|
|
bMaxInputReportSize = 0x20;
|
|
|
|
|
bMaxOutputReportSize = 0x6;
|
|
|
|
|
wAlternateProductIds[0] = -1;
|
|
|
|
|
wAlternateProductIds[1] = -1;
|
|
|
|
|
wAlternateProductIds[2] = -1;
|
|
|
|
|
wAlternateProductIds[3] = -1;
|
|
|
|
|
bMaxInputReportSize = 20;
|
|
|
|
|
bMaxOutputReportSize = 6;
|
|
|
|
|
wAlternateProductIds[0] = 0xFFFF;
|
|
|
|
|
wAlternateProductIds[1] = 0xFFFF;
|
|
|
|
|
wAlternateProductIds[2] = 0xFFFF;
|
|
|
|
|
wAlternateProductIds[3] = 0xFFFF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const XIDDesc desc_xid_xbox_gamepad;
|
|
|
|
|
|
|
|
|
|
int XidGamepad::Init(int port)
|
|
|
|
|
{
|
|
|
|
|
if (port > 4 || port < 1) { return -1; };
|
|
|
|
|
if (port > 4 || port < 1) { return -1; }
|
|
|
|
|
|
|
|
|
|
XboxDeviceState* dev = ClassInitFn();
|
|
|
|
|
int rc = UsbXidClaimPort(dev, port);
|
|
|
|
|
if (rc != 0) {
|
|
|
|
|
m_UsbDev->m_HostController->m_bFrameTime = false;
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
m_UsbDev->USB_EpInit(dev);
|
|
|
|
|
m_UsbDev->USB_DeviceInit(dev);
|
|
|
|
|
m_UsbDev->USB_DeviceAttach(dev);
|
|
|
|
|
|
|
|
|
|
m_UsbDev->m_HostController->m_bFrameTime = false;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -245,6 +250,10 @@ int XidGamepad::UsbXidClaimPort(XboxDeviceState* dev, int port)
|
|
|
|
|
EmuWarning("Port requested %d.2 not found (in use?)", port);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (m_UsbDev->m_HostController->m_bFrameTime) {}
|
|
|
|
|
m_UsbDev->m_HostController->m_bFrameTime = true;
|
|
|
|
|
|
|
|
|
|
m_Port = port;
|
|
|
|
|
it = m_UsbDev->m_FreePorts.begin() + i;
|
|
|
|
|
dev->Port = *it;
|
|
|
|
@ -273,7 +282,18 @@ int XidGamepad::UsbXid_Initfn(XboxDeviceState* dev)
|
|
|
|
|
m_XidState->intr = m_UsbDev->USB_GetEP(dev, USB_TOKEN_IN, 2);
|
|
|
|
|
|
|
|
|
|
m_XidState->in_state.bLength = sizeof(m_XidState->in_state);
|
|
|
|
|
m_XidState->in_state.bReportId = 0;
|
|
|
|
|
m_XidState->out_state.length = sizeof(m_XidState->out_state);
|
|
|
|
|
m_XidState->out_state.report_id = 0;
|
|
|
|
|
|
|
|
|
|
std::memset(&m_XidState->in_state_capabilities, 0xFF, sizeof(m_XidState->in_state_capabilities));
|
|
|
|
|
m_XidState->in_state_capabilities.bLength = sizeof(m_XidState->in_state_capabilities);
|
|
|
|
|
m_XidState->in_state_capabilities.bReportId = 0;
|
|
|
|
|
|
|
|
|
|
std::memset(&m_XidState->out_state_capabilities, 0xFF, sizeof(m_XidState->out_state_capabilities));
|
|
|
|
|
m_XidState->out_state_capabilities.length = sizeof(m_XidState->out_state_capabilities);
|
|
|
|
|
m_XidState->out_state_capabilities.report_id = 0;
|
|
|
|
|
|
|
|
|
|
m_XidState->xid_desc = &desc_xid_xbox_gamepad;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
@ -317,20 +337,35 @@ void XidGamepad::UsbXid_HandleControl(XboxDeviceState* dev, USBPacket* p,
|
|
|
|
|
// The wValue field specifies the Report Type in the high byte and the Report ID in the low byte. Set Report ID
|
|
|
|
|
// to 0 (zero) if Report IDs are not used. 01 = input, 02 = output, 03 = feature, 04-FF = reserved"
|
|
|
|
|
DbgPrintf("%s GET_REPORT 0x%X\n", LOG_STR_GAMEPAD, value);
|
|
|
|
|
if (value == 0x100) {
|
|
|
|
|
assert(m_XidState->in_state.bLength <= length);
|
|
|
|
|
// m_XidState->in_state.bReportId++; /* FIXME: I'm not sure if bReportId is just a counter */
|
|
|
|
|
// JayFoxRox's analysis: "This 0x0100 case is for input.
|
|
|
|
|
// This is the case where the Xbox wants to read input data from the controller.
|
|
|
|
|
// Confirmed with a real Duke controller :
|
|
|
|
|
// If the buffer provided by the Xbox is too small, the controller will cut the transfer when the buffer is full (actual_length is patched).
|
|
|
|
|
// If the buffer is too large the controller will STALL instead.
|
|
|
|
|
// If the buffer has the correct length the full input data is transferred."
|
|
|
|
|
if (value == 0x0100) {
|
|
|
|
|
if (length <= m_XidState->in_state.bLength) {
|
|
|
|
|
SDL2Devices* controller = g_InputDeviceManager->FindDeviceFromXboxPort(m_Port);
|
|
|
|
|
if (controller != nullptr) {
|
|
|
|
|
controller->ReadButtonState(&m_XidState->in_state.wButtons, m_XidState->in_state.bAnalogButtons,
|
|
|
|
|
&m_XidState->in_state.sThumbLX, &m_XidState->in_state.sThumbLY, &m_XidState->in_state.sThumbRX,
|
|
|
|
|
&m_XidState->in_state.sThumbRY);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// ergo720: this shouldn't really happen. If it does, it either means that m_Port is wrong or there's a bug
|
|
|
|
|
// in the InputDeviceManager
|
|
|
|
|
p->Status = USB_RET_STALL;
|
|
|
|
|
assert(0);
|
|
|
|
|
}
|
|
|
|
|
std::memcpy(data, &m_XidState->in_state, m_XidState->in_state.bLength);
|
|
|
|
|
p->ActualLength = m_XidState->in_state.bLength;
|
|
|
|
|
p->ActualLength = length;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
assert(0);
|
|
|
|
|
p->Status = USB_RET_STALL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
p->Status = USB_RET_STALL;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
@ -341,18 +376,28 @@ void XidGamepad::UsbXid_HandleControl(XboxDeviceState* dev, USBPacket* p,
|
|
|
|
|
// request is the same as for the Get_Report request, however the data direction is reversed and the Report
|
|
|
|
|
// Data is sent from host to device."
|
|
|
|
|
DbgPrintf("%s SET_REPORT 0x%X\n", LOG_STR_GAMEPAD, value);
|
|
|
|
|
if (value == 0x200) {
|
|
|
|
|
// JayFoxRox's analysis: "The 0x0200 case below is for output.
|
|
|
|
|
// This is the case where the Xbox wants to write rumble data to the controller.
|
|
|
|
|
// To my knowledge :
|
|
|
|
|
// If the buffer provided by the Xbox is too small the transfer will STALL.
|
|
|
|
|
// If the buffer is too large the transfer will STALL.
|
|
|
|
|
// If the buffer has the correct length the full output data is transferred."
|
|
|
|
|
if (value == 0x0200) {
|
|
|
|
|
if (length == m_XidState->out_state.length) {
|
|
|
|
|
// Read length, then the entire packet
|
|
|
|
|
std::memcpy(&m_XidState->out_state, data, sizeof(m_XidState->out_state));
|
|
|
|
|
/* FIXME: This should also be a STALL */
|
|
|
|
|
assert(m_XidState->out_state.length == sizeof(m_XidState->out_state));
|
|
|
|
|
assert(m_XidState->out_state.length <= length);
|
|
|
|
|
//FIXME: Check actuator endianess
|
|
|
|
|
DbgPrintf("Set rumble power to left: 0x%X and right: 0x%X\n",
|
|
|
|
|
m_XidState->out_state.left_actuator_strength,
|
|
|
|
|
m_XidState->out_state.right_actuator_strength);
|
|
|
|
|
p->ActualLength = m_XidState->out_state.length;
|
|
|
|
|
|
|
|
|
|
p->ActualLength = length;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
p->Status = USB_RET_STALL;
|
|
|
|
|
}
|
|
|
|
|
UpdateForceFeedback();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
p->Status = USB_RET_STALL;
|
|
|
|
|
assert(0);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
@ -367,6 +412,7 @@ void XidGamepad::UsbXid_HandleControl(XboxDeviceState* dev, USBPacket* p,
|
|
|
|
|
p->ActualLength = m_XidState->xid_desc->bLength;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
p->Status = USB_RET_STALL;
|
|
|
|
|
assert(0);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
@ -374,9 +420,24 @@ void XidGamepad::UsbXid_HandleControl(XboxDeviceState* dev, USBPacket* p,
|
|
|
|
|
|
|
|
|
|
case VendorInterfaceRequest | XID_GET_CAPABILITIES: {
|
|
|
|
|
DbgPrintf("%s XID_GET_CAPABILITIES 0x%x\n", LOG_STR_GAMEPAD, value);
|
|
|
|
|
/* FIXME: ! */
|
|
|
|
|
if (value == 0x0100) {
|
|
|
|
|
if (length > m_XidState->in_state_capabilities.bLength) {
|
|
|
|
|
length = m_XidState->in_state_capabilities.bLength;
|
|
|
|
|
}
|
|
|
|
|
std::memcpy(data, &m_XidState->in_state_capabilities, length);
|
|
|
|
|
p->ActualLength = length;
|
|
|
|
|
}
|
|
|
|
|
else if (value == 0x0200) {
|
|
|
|
|
if (length > m_XidState->out_state_capabilities.length) {
|
|
|
|
|
length = m_XidState->out_state_capabilities.length;
|
|
|
|
|
}
|
|
|
|
|
std::memcpy(data, &m_XidState->out_state_capabilities, length);
|
|
|
|
|
p->ActualLength = length;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
p->Status = USB_RET_STALL;
|
|
|
|
|
//assert(false);
|
|
|
|
|
assert(0);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -411,14 +472,24 @@ void XidGamepad::UsbXid_HandleData(XboxDeviceState* dev, USBPacket* p)
|
|
|
|
|
switch (p->Pid) {
|
|
|
|
|
case USB_TOKEN_IN: {
|
|
|
|
|
if (p->Endpoint->Num == 2) {
|
|
|
|
|
if (m_XidState->in_dirty) {
|
|
|
|
|
SDL2Devices* controller = g_InputDeviceManager->FindDeviceFromXboxPort(m_Port);
|
|
|
|
|
if (controller != nullptr) {
|
|
|
|
|
bool ret;
|
|
|
|
|
ret = controller->ReadButtonState(&m_XidState->in_state.wButtons, m_XidState->in_state.bAnalogButtons,
|
|
|
|
|
&m_XidState->in_state.sThumbLX, &m_XidState->in_state.sThumbLY, &m_XidState->in_state.sThumbRX,
|
|
|
|
|
&m_XidState->in_state.sThumbRY);
|
|
|
|
|
if (ret) {
|
|
|
|
|
m_UsbDev->USB_PacketCopy(p, &m_XidState->in_state, m_XidState->in_state.bLength);
|
|
|
|
|
m_XidState->in_dirty = false;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
p->Status = USB_RET_NAK;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
p->Status = USB_RET_STALL;
|
|
|
|
|
assert(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
assert(0);
|
|
|
|
|
}
|
|
|
|
@ -426,7 +497,13 @@ void XidGamepad::UsbXid_HandleData(XboxDeviceState* dev, USBPacket* p)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case USB_TOKEN_OUT: {
|
|
|
|
|
p->Status = USB_RET_STALL;
|
|
|
|
|
if (p->Endpoint->Num == 2) {
|
|
|
|
|
m_UsbDev->USB_PacketCopy(p, &m_XidState->out_state, m_XidState->out_state.length);
|
|
|
|
|
UpdateForceFeedback();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
assert(0);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -444,3 +521,16 @@ void XidGamepad::XidCleanUp()
|
|
|
|
|
m_pPeripheralFuncStruct = nullptr;
|
|
|
|
|
m_XidState = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void XidGamepad::UpdateForceFeedback()
|
|
|
|
|
{
|
|
|
|
|
// JayFoxRox's remarks: "Xbox -> XID packets were not tested
|
|
|
|
|
// The handling out output packets / force feedback was not checked."
|
|
|
|
|
// For the above reason we don't implement vibration support for now since the current
|
|
|
|
|
// implementation is untested and could potentially contain errors
|
|
|
|
|
|
|
|
|
|
/* FIXME: Check actuator endianess */
|
|
|
|
|
DbgPrintf("Set rumble power to left: 0x%X and right: 0x%X\n",
|
|
|
|
|
m_XidState->out_state.left_actuator_strength,
|
|
|
|
|
m_XidState->out_state.right_actuator_strength);
|
|
|
|
|
}
|
|
|
|
|