From 06a5cbc5a914580c39e2358d40f32dacf2a589e0 Mon Sep 17 00:00:00 2001 From: ergo720 Date: Thu, 19 Jul 2018 18:54:03 +0200 Subject: [PATCH] Ported back from JayFoxRox's research improved xid requests implementation + other minor xid changes --- src/Common/Input/SDL2_Device.cpp | 4 +- src/Common/Input/SDL2_Device.h | 2 +- src/devices/usb/Hub.cpp | 7 ++ src/devices/usb/OHCI.h | 2 +- src/devices/usb/XidGamepad.cpp | 172 +++++++++++++++++++++++-------- src/devices/usb/XidGamepad.h | 5 +- 6 files changed, 147 insertions(+), 45 deletions(-) diff --git a/src/Common/Input/SDL2_Device.cpp b/src/Common/Input/SDL2_Device.cpp index 788762af5..975da59d9 100644 --- a/src/Common/Input/SDL2_Device.cpp +++ b/src/Common/Input/SDL2_Device.cpp @@ -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; } diff --git a/src/Common/Input/SDL2_Device.h b/src/Common/Input/SDL2_Device.h index e275b393e..ea7dc2f09 100644 --- a/src/Common/Input/SDL2_Device.h +++ b/src/Common/Input/SDL2_Device.h @@ -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); diff --git a/src/devices/usb/Hub.cpp b/src/devices/usb/Hub.cpp index 42ddda8a0..f985c3053 100644 --- a/src/devices/usb/Hub.cpp +++ b/src/devices/usb/Hub.cpp @@ -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; diff --git a/src/devices/usb/OHCI.h b/src/devices/usb/OHCI.h index 925f86bb1..ddf2b4804 100644 --- a/src/devices/usb/OHCI.h +++ b/src/devices/usb/OHCI.h @@ -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 diff --git a/src/devices/usb/XidGamepad.cpp b/src/devices/usb/XidGamepad.cpp index d93c8335f..077490a4c 100644 --- a/src/devices/usb/XidGamepad.cpp +++ b/src/devices/usb/XidGamepad.cpp @@ -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); +} diff --git a/src/devices/usb/XidGamepad.h b/src/devices/usb/XidGamepad.h index 35a202c82..bc73cf365 100644 --- a/src/devices/usb/XidGamepad.h +++ b/src/devices/usb/XidGamepad.h @@ -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];