Connected LLE USB implementation with the rest of the emulator (untested)

This commit is contained in:
ergo720 2018-07-18 23:32:18 +02:00
parent 4e7d4a7eda
commit c18e2f6a38
13 changed files with 368 additions and 34 deletions

View File

@ -35,8 +35,13 @@
// ******************************************************************
#include "InputConfig.h"
#include "..\devices\usb\XidGamepad.h"
#include "..\..\CxbxKrnl\EmuKrnl.h" // For EmuWarning
#include <thread>
InputDeviceManager* g_InputDeviceManager = nullptr;
InputDeviceManager::InputDeviceManager()
{
if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) < 0) {
@ -53,6 +58,7 @@ InputDeviceManager::~InputDeviceManager()
int InputDeviceManager::EnumSdl2Devices()
{
int NumOfJoysticks;
int NumInvalidJoysticks;
SDL2Devices* pDev;
SDL_GameController* pController;
std::vector<SDL2Devices*>::iterator it;
@ -60,16 +66,19 @@ int InputDeviceManager::EnumSdl2Devices()
NumOfJoysticks = SDL_NumJoysticks();
if (NumOfJoysticks < 0) {
EmuWarning("Failed to enumerate joysticks. The error was: %s", SDL_GetError());
return -1;
return 0;
}
SDL_GameControllerButtonBind;
NumInvalidJoysticks = 0;
for (int i = 0; i < NumOfJoysticks; i++) {
if (SDL_IsGameController(i)) {
pDev = new SDL2Devices();
pDev->m_Index = i;
m_Sdl2Devices.push_back(pDev);
}
// this joystick not supported at the moment
// this joystick is not supported at the moment
NumInvalidJoysticks++;
}
for (it = m_Sdl2Devices.begin(); it != m_Sdl2Devices.end();) {
@ -78,14 +87,154 @@ int InputDeviceManager::EnumSdl2Devices()
EmuWarning("Failed to open game controller %s. The error was %s\n", SDL_GameControllerNameForIndex((*it)->m_Index), SDL_GetError());
delete (*it);
it = m_Sdl2Devices.erase(it);
NumInvalidJoysticks++;
}
else {
printf("Found game controller %s\n", SDL_GameControllerName(pController));
(*it)->m_Gamepad = pController;
(*it)->m_jyID = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(pController));
(*it)->m_Attached = 1;
++it;
}
}
return NumOfJoysticks - NumInvalidJoysticks;
}
int InputDeviceManager::ConnectDeviceToXbox(int port, int type)
{
int ret = -1;
std::vector<SDL2Devices*>::iterator it;
if (port > 4 || port < 1) { return ret; };
for (it = m_Sdl2Devices.begin(); it != m_Sdl2Devices.end(); ++it) {
if ((*it)->m_Index == (port - 1)) {
--port;
break;
}
}
if (it == m_Sdl2Devices.end()) {
EmuWarning("Attempted to connect a device not yet enumerated.\n");
return ret;
}
switch (type)
{
case MS_CONTROLLER_DUKE: {
if (g_HubObjArray[port] == nullptr) {
g_HubObjArray[port] = new Hub;
ret = g_HubObjArray[port]->Init(port);
if (ret) {
delete g_HubObjArray[port];
g_HubObjArray[port] = nullptr;
break;
}
if (g_XidControllerObjArray[port] == nullptr) {
g_XidControllerObjArray[port] = new XidGamepad;
ret = g_XidControllerObjArray[port]->Init(port);
if (ret) {
g_HubObjArray[port]->HubDestroy();
delete g_HubObjArray[port];
g_HubObjArray[port] = nullptr;
delete g_XidControllerObjArray[port];
g_XidControllerObjArray[port] = nullptr;
}
}
else {
ret = -1;
g_HubObjArray[port]->HubDestroy();
delete g_HubObjArray[port];
g_HubObjArray[port] = nullptr;
EmuWarning("Xid controller already present at port %d.2\n", port + 1);
}
}
else {
EmuWarning("Hub already present at port %d\n", port + 1);
}
break;
}
case MS_CONTROLLER_S:
case LIGHT_GUN:
case STEERING_WHEEL:
case MEMORY_UNIT:
case IR_DONGLE:
case STEEL_BATTALION_CONTROLLER: {
printf("This device type is not yet supported\n");
break;
}
default:
EmuWarning("Attempted to attach an unknown device type\n");
}
if (!ret) {
(*it)->m_Type = type;
(*it)->m_Attached = 1;
}
return ret;
}
void InputDeviceManager::DisconnectDeviceFromXbox(int port)
{
std::vector<SDL2Devices*>::iterator it;
if (port < 1) { return; }
for (it = m_Sdl2Devices.begin(); it != m_Sdl2Devices.end(); ++it) {
if ((*it)->m_Index == (port - 1)) {
--port;
break;
}
}
if (it == m_Sdl2Devices.end()) {
// Not necessarily a bug. This could also be triggered by detaching an unsupported joystick
return;
}
if (port + 1 > 4) {
delete (*it);
m_Sdl2Devices.erase(it);
return;
}
switch ((*it)->m_Type)
{
case MS_CONTROLLER_DUKE: {
if (g_HubObjArray[port] != nullptr && g_XidControllerObjArray[port] != nullptr) {
g_HubObjArray[port]->HubDestroy();
delete g_HubObjArray[port];
g_HubObjArray[port] = nullptr;
delete g_XidControllerObjArray[port];
g_XidControllerObjArray[port] = nullptr;
delete (*it);
m_Sdl2Devices.erase(it);
// Here, we could also see if there are detached devices that have a matching type and bound buttons, so that it can immediately
// be used instead of remaining inactive (example: 5 controllers and 1st is detached -> 5th can be used if it has bindings)
}
else {
EmuWarning("Attempted to disconnect a device not attached to the Xbox.\n");
}
break;
}
case MS_CONTROLLER_S:
case LIGHT_GUN:
case STEERING_WHEEL:
case MEMORY_UNIT:
case IR_DONGLE:
case STEEL_BATTALION_CONTROLLER: {
printf("This device type is not yet supported\n");
break;
}
default:
EmuWarning("Attempted to detach an unknown device type\n");
}
}
void InputDeviceManager::StartInputThread()
@ -128,6 +277,29 @@ void InputDeviceManager::InputThread(InputDeviceManager* pVoid)
break;
}
case SDL_JOYDEVICEADDED: {
bool found = false;
for (auto dev : pVoid->m_Sdl2Devices) {
if (dev->m_Index == event.jdevice.which) {
// already enumerated, skipping
found = true;
break;
}
}
if (!found) {
// for now we only support a single controller at port 1, more will be added later
if (!pVoid->IsValidController(event.jdevice.which) && event.jdevice.which == 0) {
pVoid->ConnectDeviceToXbox(1, MS_CONTROLLER_DUKE);
}
}
break;
}
case SDL_JOYDEVICEREMOVED: {
pVoid->DisconnectDeviceFromXbox(event.jdevice.which + 1);
break;
}
case SDL_CONTROLLERBUTTONUP:
case SDL_CONTROLLERBUTTONDOWN: {
pVoid->UpdateButtonState(event.cbutton.which, event.cbutton.button, event.cbutton.state);
@ -139,6 +311,13 @@ void InputDeviceManager::InputThread(InputDeviceManager* pVoid)
break;
}
case SDL_CONTROLLERDEVICEADDED: {
}
case SDL_CONTROLLERDEVICEREMOVED: {
}
case SDL_QUIT: {
bContinue = false;
break;
@ -250,3 +429,51 @@ void InputDeviceManager::UpdateAxisState(SDL_JoystickID id, uint8_t axis_index,
ControllerObj->UpdateAxisState(xbox_button, state);
}
int InputDeviceManager::IsValidController(int index)
{
SDL2Devices* pDev;
SDL_GameController* pController;
if (SDL_IsGameController(index)) {
pDev = new SDL2Devices();
pDev->m_Index = index;
m_Sdl2Devices.push_back(pDev);
}
else {
// this joystick is not supported at the moment
return -1;
}
pController = SDL_GameControllerOpen(pDev->m_Index);
if (pController == nullptr) {
EmuWarning("Failed to open game controller %s. The error was %s\n", SDL_GameControllerNameForIndex(pDev->m_Index), SDL_GetError());
delete pDev;
m_Sdl2Devices.erase(m_Sdl2Devices.begin() + index);
return -1;
}
else if (pDev->m_Index > 3) {
printf("More than 4 controllers detected. Putting game controller %s in detached state\n",
SDL_GameControllerName(pController));
pDev->m_Attached = 0;
return -1;
}
else {
printf("Found game controller %s\n", SDL_GameControllerName(pController));
pDev->m_Gamepad = pController;
pDev->m_jyID = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(pController));
return 0;
}
}
SDL2Devices* InputDeviceManager::FindDeviceFromXboxPort(int port)
{
if (port > 4 || port < 1) { return nullptr; };
for (auto it = m_Sdl2Devices.begin(); it != m_Sdl2Devices.end(); ++it) {
if ((*it)->m_Index == (port - 1)) {
return *it;
}
}
return nullptr;
}

View File

@ -39,8 +39,6 @@
#include <vector>
#include <atomic>
#include <thread>
#include "..\..\CxbxKrnl\EmuKrnl.h" // For EmuWarning
#include "SDL.h"
#define GAMEPAD_A 0
@ -51,7 +49,7 @@
#define GAMEPAD_WHITE 5
#define GAMEPAD_LEFT_TRIGGER 6
#define GAMEPAD_RIGHT_TRIGGER 7
#define GAMEPAD_DPAD_UP 8
#define GAMEPAD_DPAD_DOWN 9
#define GAMEPAD_DPAD_LEFT 10
@ -75,13 +73,35 @@
#include "SDL2_Device.h"
/* enum indicating the device type to attach to the virtual xbox */
typedef enum {
DEVICE_INVALID = 0,
MS_CONTROLLER_DUKE,
MS_CONTROLLER_S,
LIGHT_GUN,
STEERING_WHEEL,
MEMORY_UNIT,
IR_DONGLE,
STEEL_BATTALION_CONTROLLER,
};
class InputDeviceManager
{
public:
InputDeviceManager();
~InputDeviceManager();
// enumerate all available sdl2 controllers
int EnumSdl2Devices();
// start input event processing thread
void StartInputThread();
// connect the enumerated device to the virtual xbox
int ConnectDeviceToXbox(int port, int type);
// disconnect a device from the emulated xbox
void DisconnectDeviceFromXbox(int port);
// find the device attached to the supplied xbox port
SDL2Devices* FindDeviceFromXboxPort(int port);
private:
@ -90,8 +110,6 @@ class InputDeviceManager
// assign the button binding to the devices
//void AssignBindings();
// start input event processing thread
void StartInputThread();
// input thread
static void InputThread(InputDeviceManager* pVoid);
// updates the button state of a joystick
@ -100,6 +118,10 @@ class InputDeviceManager
void UpdateHatState(SDL_JoystickID id, uint8_t hat_index, uint8_t state);
// updates the axis state of a joystick
void UpdateAxisState(SDL_JoystickID id, uint8_t axis_index, int16_t state);
// checks if the controller attached can be used by sdl
int IsValidController(int index);
};
extern InputDeviceManager* g_InputDeviceManager;
#endif

View File

@ -35,6 +35,7 @@
// ******************************************************************
#include "SDL2_Device.h"
#include <assert.h>
int SDL2Devices::GetBoundButton(int sdl_key)
@ -197,3 +198,19 @@ void SDL2Devices::UpdateAxisState(uint8_t xbox_button, int16_t state)
assert(0);
}
}
void SDL2Devices::ReadButtonState(uint16_t* wButtons, uint8_t* bAnalogButtons, int16_t* sThumbLX,
int16_t* sThumbLY, int16_t* sThumbRX, int16_t* sThumbRY)
{
if (bStateDirty) {
*wButtons = m_State.wButtons.load();
*sThumbLX = m_State.sThumbLX.load();
*sThumbLY = m_State.sThumbLY.load();
*sThumbRX = m_State.sThumbRX.load();
*sThumbRY = m_State.sThumbRY.load();
for (int i = 0; i < 8; ++i) {
bAnalogButtons[i] = m_State.bAnalogButtons[i].load();
}
bStateDirty = false;
}
}

View File

@ -60,6 +60,10 @@ class SDL2Devices
SDL_GameController* m_Gamepad;
// id of this controller
SDL_JoystickID m_jyID;
// attach/detach state of this controller
int m_Attached;
// indicates the xbox device emulated by this controller
int m_Type = DEVICE_INVALID;
// update analog button state
void UpdateAnalogButtonState(uint8_t xbox_button, uint8_t state);
@ -73,10 +77,13 @@ class SDL2Devices
int GetBoundButton(int sdl_key);
// 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,
int16_t* sThumbLY, int16_t* sThumbRX, int16_t* sThumbRY);
private:
// default bindings
// default bindings (hardcoded for now)
const int m_ButtonMap_Analog[8][2] = {
{ GAMEPAD_A, SDL_CONTROLLER_BUTTON_A },
{ GAMEPAD_B, SDL_CONTROLLER_BUTTON_B },

View File

@ -71,6 +71,7 @@ namespace xboxkrnl
#include "devices\LED.h" // For LED::Sequence
#include "EmuSha.h" // For the SHA1 functions
#include "Timer.h" // For Timer_Init
#include "..\Common\Input\InputConfig.h" // For the InputDeviceManager
/*! thread local storage */
Xbe::TLS *CxbxKrnl_TLS = NULL;
@ -1454,10 +1455,23 @@ __declspec(noreturn) void CxbxKrnlInit
EmuHLEIntercept(pXbeHeader);
SetupXboxDeviceTypes();
if (!bLLE_USB) {
SetupXboxDeviceTypes();
}
InitXboxHardware(HardwareModel::Revision1_5); // TODO : Make configurable
if (bLLE_USB) {
int ret;
g_InputDeviceManager = new InputDeviceManager;
ret = g_InputDeviceManager->EnumSdl2Devices();
g_InputDeviceManager->StartInputThread();
if (ret > 0) {
// Temporary: the device type and bindings should be read from emushared, for now always assume one xbox controller
g_InputDeviceManager->ConnectDeviceToXbox(1, MS_CONTROLLER_DUKE);
}
}
// Now the hardware devices exist, couple the EEPROM buffer to it's device
g_EEPROM->SetEEPROM((uint8_t*)EEPROM);

View File

@ -33,8 +33,10 @@
// * All rights reserved
// *
// ******************************************************************
#include <windows.h>
#ifdef _WIN32
#include <windows.h>
#endif
#include <thread>
#include <vector>
#include "Timer.h"
@ -106,7 +108,11 @@ void ClockThread(TimerObject* Timer)
Timer_Destroy(Timer);
return;
}
Timer->Callback(Timer->Opaque);
Timer->Callback(Timer->Opaque);
if (Timer->Exit.load()) {
Timer_Destroy(Timer);
return;
}
NewExpireTime = GetNextExpireTime(Timer);
}
}

View File

@ -34,7 +34,8 @@
// *
// ******************************************************************
#include "Xbox.h" // For HardwareModel
#include "Xbox.h" // For HardwareModel
#include "..\CxbxKrnl\HLEIntercept.h"
PCIBus* g_PCIBus;
SMBus* g_SMBus;
@ -129,9 +130,11 @@ void InitXboxHardware(HardwareModel hardwareModel)
g_EEPROM = new EEPROMDevice();
g_NVNet = new NVNetDevice();
g_NV2A = new NV2ADevice();
g_ADM1032 = new ADM1032Device();
g_USB0 = new USBDevice();
g_USB1 = new USBDevice();
g_ADM1032 = new ADM1032Device();
if (bLLE_USB) {
g_USB0 = new USBDevice();
g_USB1 = new USBDevice();
}
// Connect devices to SM bus
g_SMBus->ConnectDevice(SMBUS_ADDRESS_SYSTEM_MICRO_CONTROLLER, g_SMC); // W 0x20 R 0x21
@ -159,9 +162,11 @@ void InitXboxHardware(HardwareModel hardwareModel)
//g_PCIBus->ConnectDevice(PCI_DEVID(0, PCI_DEVFN(4, 1)), g_MCPX); // MCPX device ID = 0x0808 ?
//g_PCIBus->ConnectDevice(PCI_DEVID(0, PCI_DEVFN(5, 0)), g_NVAPU);
//g_PCIBus->ConnectDevice(PCI_DEVID(0, PCI_DEVFN(6, 0)), g_AC97);
g_PCIBus->ConnectDevice(PCI_DEVID(1, PCI_DEVFN(0, 0)), g_NV2A, NV2A_ADDR);
g_PCIBus->ConnectDevice(PCI_DEVID(0, PCI_DEVFN(2, 0)), g_USB0, USB0_BASE);
g_PCIBus->ConnectDevice(PCI_DEVID(0, PCI_DEVFN(3, 0)), g_USB1, USB1_BASE);
g_PCIBus->ConnectDevice(PCI_DEVID(1, PCI_DEVFN(0, 0)), g_NV2A, NV2A_ADDR);
if (bLLE_USB) {
g_PCIBus->ConnectDevice(PCI_DEVID(0, PCI_DEVFN(2, 0)), g_USB0, USB0_BASE);
g_PCIBus->ConnectDevice(PCI_DEVID(0, PCI_DEVFN(3, 0)), g_USB1, USB1_BASE);
}
// TODO : Handle other SMBUS Addresses, like PIC_ADDRESS, XCALIBUR_ADDRESS
// Resources : http://pablot.com/misc/fancontroller.cpp

View File

@ -81,6 +81,8 @@
extern USBDevice* g_USB0;
extern USBDevice* g_USB1;
Hub* g_HubObjArray[4] = { nullptr };
struct USBHubPort {
USBPort port; // downstream port status
@ -689,3 +691,11 @@ void Hub::HubCleanUp()
m_pPeripheralFuncStruct = nullptr;
m_HubState = nullptr;
}
void Hub::HubDestroy()
{
while (m_UsbDev->m_HostController->m_bFrameTime) {}
m_UsbDev->m_HostController->m_bFrameTime = true;
m_pPeripheralFuncStruct->handle_destroy();
m_UsbDev->m_HostController->m_bFrameTime = false;
}

View File

@ -50,10 +50,10 @@ class Hub
// usb device this hub is attached to
USBDevice* m_UsbDev = nullptr;
// initialize this peripheral
// initialize this hub
int Init(int port);
// destroy hub resources
void HubCleanUp();
// start hub destruction
void HubDestroy();
private:
@ -84,6 +84,8 @@ class Hub
void UsbHubReleasePort(XboxDeviceState* dev);
// retieve the name of the feature of the usb request
std::string GetFeatureName(int feature);
// destroy hub resources
void HubCleanUp();
};
extern Hub* g_HubObjArray[4];

View File

@ -194,7 +194,8 @@ OHCI::OHCI(int Irq, USBDevice* UsbObj)
USBPortOps* ops;
m_IrqNum = Irq;
m_UsbDevice = UsbObj;
m_UsbDevice = UsbObj;
m_bFrameTime = false;
ops = new USBPortOps();
{
using namespace std::placeholders;
@ -229,11 +230,15 @@ void OHCI::OHCI_FrameBoundaryWrapper(void* pVoid)
void OHCI::OHCI_FrameBoundaryWorker()
{
OHCI_HCCA hcca;
OHCI_HCCA hcca;
while (m_bFrameTime) {}
m_bFrameTime = true;
if (OHCI_ReadHCCA(m_Registers.HcHCCA, &hcca)) {
EmuWarning("%s: HCCA read error at physical address 0x%X", m_Registers.HcHCCA, LOG_STR_OHCI);
OHCI_FatalError();
OHCI_FatalError();
m_bFrameTime = false;
return;
}
@ -257,7 +262,8 @@ void OHCI::OHCI_FrameBoundaryWorker()
OHCI_ProcessLists(0);
// Stop if UnrecoverableError happened or OHCI_SOF will crash
if (m_Registers.HcInterruptStatus & OHCI_INTR_UE) {
if (m_Registers.HcInterruptStatus & OHCI_INTR_UE) {
m_bFrameTime = false;
return;
}
@ -302,7 +308,8 @@ void OHCI::OHCI_FrameBoundaryWorker()
if (OHCI_WriteHCCA(m_Registers.HcHCCA, &hcca)) {
EmuWarning("%s: HCCA write error at physical address 0x%X", LOG_STR_OHCI, m_Registers.HcHCCA);
OHCI_FatalError();
}
}
m_bFrameTime = false;
}
void OHCI::OHCI_FatalError()

View File

@ -139,7 +139,11 @@ struct OHCI_Registers
/* OHCI class representing the state of the HC */
class OHCI
{
public:
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
std::atomic_bool m_bFrameTime;
// constructor
OHCI(int Irqn, USBDevice* UsbObj);
// destructor

View File

@ -35,7 +35,8 @@
// ******************************************************************
#include "XidGamepad.h"
#include "USBDevice.h"
#include "USBDevice.h"
#include "Common/Input/InputConfig.h"
#define LOG_STR_GAMEPAD "Gamepad:"
@ -46,6 +47,8 @@
#define HID_SET_REPORT 0x09
#define XID_GET_CAPABILITIES 0x01
XidGamepad* g_XidControllerObjArray[4];
#pragma pack(1)
@ -239,9 +242,10 @@ int XidGamepad::UsbXidClaimPort(XboxDeviceState* dev, int port)
}
}
if (m_UsbDev == nullptr) {
EmuWarning("Port requested %d.%d not found (in use?)", port, 2);
EmuWarning("Port requested %d.2 not found (in use?)", port);
return -1;
}
m_Port = port;
it = m_UsbDev->m_FreePorts.begin() + i;
dev->Port = *it;
(*it)->Dev = dev;
@ -316,6 +320,12 @@ void XidGamepad::UsbXid_HandleControl(XboxDeviceState* dev, USBPacket* p,
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);
}
std::memcpy(data, &m_XidState->in_state, m_XidState->in_state.bLength);
p->ActualLength = m_XidState->in_state.bLength;
}

View File

@ -42,7 +42,6 @@
struct USBXIDState; // forward declare
/* Class which implements an xbox gamepad */
class XidGamepad
{
@ -59,7 +58,9 @@ class XidGamepad
// gamepad state
USBXIDState* m_XidState = nullptr;
// gamepad class functions
USBDeviceClass* m_pPeripheralFuncStruct = nullptr;
USBDeviceClass* m_pPeripheralFuncStruct = nullptr;
// xbox port this gamepad is attached to
int m_Port = 0;
// initialize various member variables/functions
XboxDeviceState* ClassInitFn();
@ -77,4 +78,6 @@ class XidGamepad
void UsbXid_HandleData(XboxDeviceState* dev, USBPacket* p);
};
extern XidGamepad* g_XidControllerObjArray[4];
#endif