Ported back from JayFoxRox's research improved xid requests implementation + other minor xid changes

This commit is contained in:
ergo720 2018-07-19 18:54:03 +02:00
parent c18e2f6a38
commit 06a5cbc5a9
6 changed files with 147 additions and 45 deletions

View File

@ -199,7 +199,7 @@ void SDL2Devices::UpdateAxisState(uint8_t xbox_button, int16_t state)
}
}
void SDL2Devices::ReadButtonState(uint16_t* wButtons, uint8_t* bAnalogButtons, int16_t* sThumbLX,
bool SDL2Devices::ReadButtonState(uint16_t* wButtons, uint8_t* bAnalogButtons, int16_t* sThumbLX,
int16_t* sThumbLY, int16_t* sThumbRX, int16_t* sThumbRY)
{
if (bStateDirty) {
@ -212,5 +212,7 @@ void SDL2Devices::ReadButtonState(uint16_t* wButtons, uint8_t* bAnalogButtons, i
bAnalogButtons[i] = m_State.bAnalogButtons[i].load();
}
bStateDirty = false;
return true;
}
return false;
}

View File

@ -78,7 +78,7 @@ class SDL2Devices
// update bBindingsChanged flag
void SetChangedBinding(bool bFlag) { m_bBindingsChanged = bFlag; }
// read the current button state of a device
void ReadButtonState(uint16_t* wButtons, uint8_t* bAnalogButtons, int16_t* sThumbLX,
bool ReadButtonState(uint16_t* wButtons, uint8_t* bAnalogButtons, int16_t* sThumbLX,
int16_t* sThumbLY, int16_t* sThumbRX, int16_t* sThumbRY);

View File

@ -177,12 +177,15 @@ int Hub::Init(int port)
XboxDeviceState* dev = ClassInitFn();
int rc = UsbHubClaimPort(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;
}
@ -226,6 +229,10 @@ int Hub::UsbHubClaimPort(XboxDeviceState* dev, int port)
else {
m_UsbDev = g_USB1;
}
while (m_UsbDev->m_HostController->m_bFrameTime) {}
m_UsbDev->m_HostController->m_bFrameTime = true;
for (auto usb_port : m_UsbDev->m_FreePorts) {
if (usb_port->Path == std::to_string(port)) {
break;

View File

@ -141,7 +141,7 @@ class OHCI
{
public:
// Indicates that the timer thread is accessing the OHCI object. Necessary because the input thread from the
// InputDeviceManager will access us when it needs to destroy a device
// InputDeviceManager will access us when it needs to create or destroy a device
std::atomic_bool m_bFrameTime;
// constructor

View File

@ -36,7 +36,8 @@
#include "XidGamepad.h"
#include "USBDevice.h"
#include "Common/Input/InputConfig.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; // Get_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 */
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);
// 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 = length;
}
else {
p->Status = USB_RET_STALL;
}
std::memcpy(data, &m_XidState->in_state, m_XidState->in_state.bLength);
p->ActualLength = m_XidState->in_state.bLength;
}
else {
assert(0);
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) {
// Read length, then the entire packet
std::memcpy(&m_XidState->out_state, data, sizeof(m_XidState->out_state));
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;
// 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));
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: ! */
p->Status = USB_RET_STALL;
//assert(false);
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(0);
}
break;
}
@ -411,12 +472,22 @@ 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) {
m_UsbDev->USB_PacketCopy(p, &m_XidState->in_state, m_XidState->in_state.bLength);
m_XidState->in_dirty = false;
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);
}
else {
p->Status = USB_RET_NAK;
}
}
else {
p->Status = USB_RET_NAK;
p->Status = USB_RET_STALL;
assert(0);
}
}
else {
@ -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);
}

View File

@ -75,7 +75,10 @@ class XidGamepad
void UsbXid_HandleReset();
void UsbXid_HandleControl(XboxDeviceState* dev, USBPacket* p,
int request, int value, int index, int length, uint8_t* data);
void UsbXid_HandleData(XboxDeviceState* dev, USBPacket* p);
void UsbXid_HandleData(XboxDeviceState* dev, USBPacket* p);
// this should update the vibration strenght of the real controller this gamepad represents.
// It doesn't do anything at the moment
void UpdateForceFeedback();
};
extern XidGamepad* g_XidControllerObjArray[4];