Merge pull request #1763 from skidau/GC-Adapter-error-recovery

Added an error recovery mechanism for the GC Adapter.
This commit is contained in:
skidau 2015-01-07 12:40:14 +11:00
commit c0ff677674
7 changed files with 138 additions and 16 deletions

View File

@ -343,6 +343,7 @@ void SConfig::SaveCoreSettings(IniFile& ini)
core->Set("GFXBackend", m_LocalCoreStartupParameter.m_strVideoBackend);
core->Set("GPUDeterminismMode", m_LocalCoreStartupParameter.m_strGPUDeterminismMode);
core->Set("GameCubeAdapter", m_GameCubeAdapter);
core->Set("GameCubeAdapterThread", m_GameCubeAdapterThread);
}
void SConfig::SaveMovieSettings(IniFile& ini)
@ -578,6 +579,7 @@ void SConfig::LoadCoreSettings(IniFile& ini)
core->Get("GFXBackend", &m_LocalCoreStartupParameter.m_strVideoBackend, "");
core->Get("GPUDeterminismMode", &m_LocalCoreStartupParameter.m_strGPUDeterminismMode, "auto");
core->Get("GameCubeAdapter", &m_GameCubeAdapter, true);
core->Get("GameCubeAdapterThread", &m_GameCubeAdapterThread, true);
}
void SConfig::LoadMovieSettings(IniFile& ini)

View File

@ -108,6 +108,7 @@ struct SConfig : NonCopyable
// Input settings
bool m_BackgroundInput;
bool m_GameCubeAdapter;
bool m_GameCubeAdapterThread;
SysConf* m_SYSCONF;

View File

@ -25,7 +25,7 @@ CSIDevice_GCController::CSIDevice_GCController(SIDevices device, int _iDeviceNum
, m_LastButtonCombo(COMBO_NONE)
{
memset(&m_Origin, 0, sizeof(SOrigin));
m_Origin.uCommand = CMD_ORIGIN;
m_Origin.uButton = 0x00;
m_Origin.uOriginStickX = 0x80; // center
m_Origin.uOriginStickY = 0x80;
m_Origin.uSubStickStickX = 0x80;
@ -35,6 +35,23 @@ CSIDevice_GCController::CSIDevice_GCController(SIDevices device, int _iDeviceNum
// Dunno if we need to do this, game/lib should set it?
m_Mode = 0x03;
#if defined(__LIBUSB__) || defined (_WIN32)
if (SI_GCAdapter::IsDetected())
{
GCPadStatus pad_origin;
memset(&pad_origin, 0, sizeof(pad_origin));
SI_GCAdapter::Input(ISIDevice::m_iDeviceNumber, &pad_origin);
m_Origin.uButton = pad_origin.button;
m_Origin.uOriginStickX = pad_origin.stickX;
m_Origin.uOriginStickY = pad_origin.stickY;
m_Origin.uSubStickStickX = pad_origin.substickX;
m_Origin.uSubStickStickY = pad_origin.substickY;
m_Origin.uTrigger_L = pad_origin.triggerLeft;
m_Origin.uTrigger_R = pad_origin.triggerRight;
}
#endif
}
int CSIDevice_GCController::RunBuffer(u8* _pBuffer, int _iLength)

View File

@ -22,8 +22,7 @@ protected:
struct SOrigin
{
u8 uCommand; // Maybe should be button bits?
u8 unk_1; // ..and this would be the other half
u16 uButton;
u8 uOriginStickX;
u8 uOriginStickY;
u8 uSubStickStickX;

View File

@ -5,7 +5,10 @@
#include <libusb.h>
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/CoreTiming.h"
#include "Core/HW/SI_GCAdapter.h"
#include "Core/HW/SystemTimers.h"
namespace SI_GCAdapter
{
@ -16,12 +19,17 @@ enum ControllerTypes
CONTROLLER_WIRELESS = 2
};
static bool s_detected = false;
static libusb_device_handle* s_handle = nullptr;
static libusb_transfer* s_irq_transfer_read = nullptr;
static libusb_transfer* s_irq_transfer_write = nullptr;
static u8 s_controller_type[MAX_SI_CHANNELS] = { CONTROLLER_NONE, CONTROLLER_NONE, CONTROLLER_NONE, CONTROLLER_NONE };
static u8 s_controller_rumble[4];
static std::mutex s_mutex;
static u8 s_controller_payload[37];
static u8 s_controller_payload_swap[37];
static int s_controller_payload_size = 0;
static std::thread s_adapter_thread;
@ -33,17 +41,36 @@ static libusb_context* s_libusb_context = nullptr;
static u8 s_endpoint_in = 0;
static u8 s_endpoint_out = 0;
static u64 s_last_init = 0;
#if defined(_WIN32)
#define LIBUSB_CALL WINAPI
#else
#define LIBUSB_CALL
#endif
extern "C"
{
void LIBUSB_CALL read_callback(libusb_transfer* transfer);
}
static void HandleEvents()
{
while (s_adapter_thread_running.IsSet())
{
libusb_handle_events(NULL);
Common::YieldCPU();
}
}
static void Read()
{
while (s_adapter_thread_running.IsSet())
{
u8 controller_payload_swap[37];
libusb_interrupt_transfer(s_handle, s_endpoint_in, controller_payload_swap, sizeof(controller_payload_swap), &s_controller_payload_size, 0);
libusb_interrupt_transfer(s_handle, s_endpoint_in, s_controller_payload_swap, sizeof(s_controller_payload_swap), &s_controller_payload_size, 16);
{
std::lock_guard<std::mutex> lk(s_mutex);
std::swap(controller_payload_swap, s_controller_payload);
std::swap(s_controller_payload_swap, s_controller_payload);
}
Common::YieldCPU();
@ -55,6 +82,14 @@ void Init()
if (s_handle != nullptr)
return;
if (Core::GetState() != Core::CORE_UNINITIALIZED)
{
if ((CoreTiming::GetTicks() - s_last_init) < SystemTimers::GetTicksPerSecond())
return;
s_last_init = CoreTiming::GetTicks();
}
s_libusb_driver_not_supported = false;
for (int i = 0; i < MAX_SI_CHANNELS; i++)
@ -163,11 +198,26 @@ void Init()
int tmp = 0;
unsigned char payload = 0x13;
libusb_interrupt_transfer(s_handle, s_endpoint_out, &payload, sizeof(payload), &tmp, 0);
libusb_interrupt_transfer(s_handle, s_endpoint_out, &payload, sizeof(payload), &tmp, 16);
if (SConfig::GetInstance().m_GameCubeAdapterThread)
{
s_adapter_thread_running.Set(true);
s_adapter_thread = std::thread(Read);
}
else
{
s_irq_transfer_read = libusb_alloc_transfer(0);
s_irq_transfer_write = libusb_alloc_transfer(0);
libusb_fill_interrupt_transfer(s_irq_transfer_read, s_handle, s_endpoint_in, s_controller_payload_swap, sizeof(s_controller_payload_swap), read_callback, NULL, 16);
libusb_submit_transfer(s_irq_transfer_read);
s_adapter_thread_running.Set(true);
s_adapter_thread = std::thread(HandleEvents);
}
s_detected = true;
}
}
}
@ -185,8 +235,16 @@ void Shutdown()
s_adapter_thread.join();
}
if (!SConfig::GetInstance().m_GameCubeAdapterThread)
{
libusb_free_transfer(s_irq_transfer_read);
libusb_free_transfer(s_irq_transfer_write);
}
if (s_handle)
{
libusb_release_interface(s_handle, 0);
libusb_reset_device(s_handle);
libusb_close(s_handle);
s_handle = nullptr;
}
@ -205,9 +263,17 @@ void Shutdown()
void Input(int chan, GCPadStatus* pad)
{
if (s_handle == nullptr || !SConfig::GetInstance().m_GameCubeAdapter)
if (!SConfig::GetInstance().m_GameCubeAdapter)
return;
if (s_handle == nullptr)
{
if (s_detected)
Init();
else
return;
}
u8 controller_payload_copy[37];
{
@ -217,7 +283,8 @@ void Input(int chan, GCPadStatus* pad)
if (s_controller_payload_size != sizeof(controller_payload_copy) || controller_payload_copy[0] != LIBUSB_DT_HID)
{
ERROR_LOG(SERIALINTERFACE, "error reading payload (size: %d, type: %02x)", s_controller_payload_size, controller_payload_copy[0]);
INFO_LOG(SERIALINTERFACE, "error reading payload (size: %d, type: %02x)", s_controller_payload_size, controller_payload_copy[0]);
Shutdown();
}
else
{
@ -269,14 +336,22 @@ void Output(int chan, u8 rumble_command)
unsigned char rumble[5] = { 0x11, s_controller_rumble[0], s_controller_rumble[1], s_controller_rumble[2], s_controller_rumble[3] };
int size = 0;
libusb_interrupt_transfer(s_handle, s_endpoint_out, rumble, sizeof(rumble), &size, 0);
if (SConfig::GetInstance().m_GameCubeAdapterThread)
{
libusb_interrupt_transfer(s_handle, s_endpoint_out, rumble, sizeof(rumble), &size, 16);
if (size != 0x05)
{
WARN_LOG(SERIALINTERFACE, "error writing rumble (size: %d)", size);
INFO_LOG(SERIALINTERFACE, "error writing rumble (size: %d)", size);
Shutdown();
}
}
else
{
libusb_fill_interrupt_transfer(s_irq_transfer_write, s_handle, s_endpoint_out, rumble, sizeof(rumble), NULL, &size, 16);
libusb_submit_transfer(s_irq_transfer_write);
}
}
}
bool IsDetected()
@ -289,4 +364,17 @@ bool IsDriverDetected()
return !s_libusb_driver_not_supported;
}
void LIBUSB_CALL read_callback(libusb_transfer *transfer)
{
if (transfer->status == LIBUSB_TRANSFER_COMPLETED)
{
{
std::lock_guard<std::mutex> lk(s_mutex);
s_controller_payload_size = transfer->actual_length;
memcpy(s_controller_payload, s_controller_payload_swap, s_controller_payload_size);
}
libusb_submit_transfer(s_irq_transfer_read);
}
}
} // end of namespace SI_GCAdapter

View File

@ -146,7 +146,11 @@ wxStaticBoxSizer* ControllerConfigDiag::CreateGamecubeSizer()
wxCheckBox* const gamecube_adapter = new wxCheckBox(this, wxID_ANY, _("Direct Connect"));
gamecube_adapter->Bind(wxEVT_CHECKBOX, &ControllerConfigDiag::OnGameCubeAdapter, this);
wxCheckBox* const gamecube_adapter_thread = new wxCheckBox(this, wxID_ANY, _("Use Thread"));
gamecube_adapter_thread->Bind(wxEVT_CHECKBOX, &ControllerConfigDiag::OnGameCubeAdapterThread, this);
gamecube_adapter_sizer->Add(gamecube_adapter, 0, wxEXPAND);
gamecube_adapter_sizer->Add(gamecube_adapter_thread, 0, wxEXPAND);
gamecube_adapter_group->Add(gamecube_adapter_sizer, 0, wxEXPAND);
gamecube_static_sizer->Add(gamecube_adapter_group, 0, wxEXPAND);
@ -159,12 +163,18 @@ wxStaticBoxSizer* ControllerConfigDiag::CreateGamecubeSizer()
gamecube_adapter->SetLabelText(_("Adapter Not Detected"));
gamecube_adapter->SetValue(false);
gamecube_adapter->Disable();
gamecube_adapter_thread->SetValue(false);
gamecube_adapter_thread->Disable();
}
else
{
gamecube_adapter->SetValue(SConfig::GetInstance().m_GameCubeAdapter);
gamecube_adapter_thread->SetValue(SConfig::GetInstance().m_GameCubeAdapterThread);
if (Core::GetState() != Core::CORE_UNINITIALIZED)
{
gamecube_adapter->Disable();
gamecube_adapter_thread->Disable();
}
}
#endif

View File

@ -64,6 +64,11 @@ public:
SConfig::GetInstance().m_GameCubeAdapter = event.IsChecked();
event.Skip();
}
void OnGameCubeAdapterThread(wxCommandEvent& event)
{
SConfig::GetInstance().m_GameCubeAdapterThread = event.IsChecked();
event.Skip();
}
private:
wxStaticBoxSizer* CreateGamecubeSizer();