Added GameCube Adapter support.
The libusb driver must be installed on the adapter (e.g. zadig can be used to install the driver in Windows). GameCube pad controllers are supported and will override the current input device assigned to the port. GameCube controller buttons are auto-configured and cannot be re-assigned. Rumble is supported. Hotplug is supported while playing a game. If a controller is unplugged from the adapter, Dolphin will fallback to using the host input device on that port. If a port on the adapter is unused, Dolphin will use the host input device for that port, allowing a mixture of host input devices and controllers connected to the adapter. The adapter support can be disabled in the Controllers config if the OS driver is preferred (allowing the pad buttons to be reconfigured). One adapter per system is supported.
This commit is contained in:
parent
1ad8d4629c
commit
8d4a47d40c
|
@ -247,7 +247,8 @@ set(LIBS
|
||||||
if(LIBUSB_FOUND)
|
if(LIBUSB_FOUND)
|
||||||
# Using shared LibUSB
|
# Using shared LibUSB
|
||||||
set(LIBS ${LIBS} ${LIBUSB_LIBRARIES})
|
set(LIBS ${LIBS} ${LIBUSB_LIBRARIES})
|
||||||
set(SRCS ${SRCS} IPC_HLE/WII_IPC_HLE_Device_hid.cpp)
|
set(SRCS ${SRCS} IPC_HLE/WII_IPC_HLE_Device_hid.cpp
|
||||||
|
HW/SI_GCAdapter.cpp)
|
||||||
endif(LIBUSB_FOUND)
|
endif(LIBUSB_FOUND)
|
||||||
|
|
||||||
set(LIBS ${LIBS} ${POLARSSL_LIBRARY})
|
set(LIBS ${LIBS} ${POLARSSL_LIBRARY})
|
||||||
|
|
|
@ -341,6 +341,7 @@ void SConfig::SaveCoreSettings(IniFile& ini)
|
||||||
core->Set("FrameSkip", m_FrameSkip);
|
core->Set("FrameSkip", m_FrameSkip);
|
||||||
core->Set("GFXBackend", m_LocalCoreStartupParameter.m_strVideoBackend);
|
core->Set("GFXBackend", m_LocalCoreStartupParameter.m_strVideoBackend);
|
||||||
core->Set("GPUDeterminismMode", m_LocalCoreStartupParameter.m_strGPUDeterminismMode);
|
core->Set("GPUDeterminismMode", m_LocalCoreStartupParameter.m_strGPUDeterminismMode);
|
||||||
|
core->Set("GameCubeAdapter", m_GameCubeAdapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SConfig::SaveMovieSettings(IniFile& ini)
|
void SConfig::SaveMovieSettings(IniFile& ini)
|
||||||
|
@ -574,6 +575,7 @@ void SConfig::LoadCoreSettings(IniFile& ini)
|
||||||
core->Get("FrameSkip", &m_FrameSkip, 0);
|
core->Get("FrameSkip", &m_FrameSkip, 0);
|
||||||
core->Get("GFXBackend", &m_LocalCoreStartupParameter.m_strVideoBackend, "");
|
core->Get("GFXBackend", &m_LocalCoreStartupParameter.m_strVideoBackend, "");
|
||||||
core->Get("GPUDeterminismMode", &m_LocalCoreStartupParameter.m_strGPUDeterminismMode, "auto");
|
core->Get("GPUDeterminismMode", &m_LocalCoreStartupParameter.m_strGPUDeterminismMode, "auto");
|
||||||
|
core->Get("GameCubeAdapter", &m_GameCubeAdapter, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SConfig::LoadMovieSettings(IniFile& ini)
|
void SConfig::LoadMovieSettings(IniFile& ini)
|
||||||
|
|
|
@ -107,6 +107,7 @@ struct SConfig : NonCopyable
|
||||||
|
|
||||||
// Input settings
|
// Input settings
|
||||||
bool m_BackgroundInput;
|
bool m_BackgroundInput;
|
||||||
|
bool m_GameCubeAdapter;
|
||||||
|
|
||||||
SysConf* m_SYSCONF;
|
SysConf* m_SYSCONF;
|
||||||
|
|
||||||
|
|
|
@ -147,6 +147,13 @@
|
||||||
<ClCompile Include="HW\SI_DeviceGBA.cpp" />
|
<ClCompile Include="HW\SI_DeviceGBA.cpp" />
|
||||||
<ClCompile Include="HW\SI_DeviceGCController.cpp" />
|
<ClCompile Include="HW\SI_DeviceGCController.cpp" />
|
||||||
<ClCompile Include="HW\SI_DeviceGCSteeringWheel.cpp" />
|
<ClCompile Include="HW\SI_DeviceGCSteeringWheel.cpp" />
|
||||||
|
<ClCompile Include="HW\SI_GCAdapter.cpp">
|
||||||
|
<!--
|
||||||
|
Disable "nonstandard extension used : zero-sized array in struct/union" warning,
|
||||||
|
which is hit in libusb.h.
|
||||||
|
-->
|
||||||
|
<DisableSpecificWarnings>4200;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="HW\Sram.cpp" />
|
<ClCompile Include="HW\Sram.cpp" />
|
||||||
<ClCompile Include="HW\StreamADPCM.cpp" />
|
<ClCompile Include="HW\StreamADPCM.cpp" />
|
||||||
<ClCompile Include="HW\SystemTimers.cpp" />
|
<ClCompile Include="HW\SystemTimers.cpp" />
|
||||||
|
@ -176,7 +183,7 @@
|
||||||
<ClCompile Include="IPC_HLE\WII_IPC_HLE_Device_hid.cpp">
|
<ClCompile Include="IPC_HLE\WII_IPC_HLE_Device_hid.cpp">
|
||||||
<!--
|
<!--
|
||||||
Disable "nonstandard extension used : zero-sized array in struct/union" warning,
|
Disable "nonstandard extension used : zero-sized array in struct/union" warning,
|
||||||
which is hit in libusb.h (and this is the only file which uses that header).
|
which is hit in libusb.h.
|
||||||
-->
|
-->
|
||||||
<DisableSpecificWarnings>4200;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
<DisableSpecificWarnings>4200;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -345,6 +352,7 @@
|
||||||
<ClInclude Include="HW\SI_DeviceGBA.h" />
|
<ClInclude Include="HW\SI_DeviceGBA.h" />
|
||||||
<ClInclude Include="HW\SI_DeviceGCController.h" />
|
<ClInclude Include="HW\SI_DeviceGCController.h" />
|
||||||
<ClInclude Include="HW\SI_DeviceGCSteeringWheel.h" />
|
<ClInclude Include="HW\SI_DeviceGCSteeringWheel.h" />
|
||||||
|
<ClInclude Include="HW\SI_GCAdapter.h" />
|
||||||
<ClInclude Include="HW\Sram.h" />
|
<ClInclude Include="HW\Sram.h" />
|
||||||
<ClInclude Include="HW\StreamADPCM.h" />
|
<ClInclude Include="HW\StreamADPCM.h" />
|
||||||
<ClInclude Include="HW\SystemTimers.h" />
|
<ClInclude Include="HW\SystemTimers.h" />
|
||||||
|
|
|
@ -15,6 +15,9 @@
|
||||||
#include "Core/HW/ProcessorInterface.h"
|
#include "Core/HW/ProcessorInterface.h"
|
||||||
#include "Core/HW/SI.h"
|
#include "Core/HW/SI.h"
|
||||||
#include "Core/HW/SI_DeviceGBA.h"
|
#include "Core/HW/SI_DeviceGBA.h"
|
||||||
|
#if defined(__LIBUSB__) || defined (_WIN32)
|
||||||
|
#include "Core/HW/SI_GCAdapter.h"
|
||||||
|
#endif
|
||||||
#include "Core/HW/SystemTimers.h"
|
#include "Core/HW/SystemTimers.h"
|
||||||
#include "Core/HW/VideoInterface.h"
|
#include "Core/HW/VideoInterface.h"
|
||||||
|
|
||||||
|
@ -56,56 +59,6 @@ enum
|
||||||
SI_EXI_CLOCK_COUNT = 0x3C,
|
SI_EXI_CLOCK_COUNT = 0x3C,
|
||||||
};
|
};
|
||||||
|
|
||||||
// SI Channel Output
|
|
||||||
union USIChannelOut
|
|
||||||
{
|
|
||||||
u32 Hex;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
u32 OUTPUT1 : 8;
|
|
||||||
u32 OUTPUT0 : 8;
|
|
||||||
u32 CMD : 8;
|
|
||||||
u32 : 8;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// SI Channel Input High u32
|
|
||||||
union USIChannelIn_Hi
|
|
||||||
{
|
|
||||||
u32 Hex;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
u32 INPUT3 : 8;
|
|
||||||
u32 INPUT2 : 8;
|
|
||||||
u32 INPUT1 : 8;
|
|
||||||
u32 INPUT0 : 6;
|
|
||||||
u32 ERRLATCH : 1; // 0: no error 1: Error latched. Check SISR.
|
|
||||||
u32 ERRSTAT : 1; // 0: no error 1: error on last transfer
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// SI Channel Input Low u32
|
|
||||||
union USIChannelIn_Lo
|
|
||||||
{
|
|
||||||
u32 Hex;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
u32 INPUT7 : 8;
|
|
||||||
u32 INPUT6 : 8;
|
|
||||||
u32 INPUT5 : 8;
|
|
||||||
u32 INPUT4 : 8;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// SI Channel
|
|
||||||
struct SSIChannel
|
|
||||||
{
|
|
||||||
USIChannelOut m_Out;
|
|
||||||
USIChannelIn_Hi m_InHi;
|
|
||||||
USIChannelIn_Lo m_InLo;
|
|
||||||
ISIDevice* m_pDevice;
|
|
||||||
};
|
|
||||||
|
|
||||||
// SI Poll: Controls how often a device is polled
|
// SI Poll: Controls how often a device is polled
|
||||||
union USIPoll
|
union USIPoll
|
||||||
{
|
{
|
||||||
|
@ -384,7 +337,9 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||||
g_Channel[1].m_pDevice->SendCommand(g_Channel[1].m_Out.Hex, g_Poll.EN1);
|
g_Channel[1].m_pDevice->SendCommand(g_Channel[1].m_Out.Hex, g_Poll.EN1);
|
||||||
g_Channel[2].m_pDevice->SendCommand(g_Channel[2].m_Out.Hex, g_Poll.EN2);
|
g_Channel[2].m_pDevice->SendCommand(g_Channel[2].m_Out.Hex, g_Poll.EN2);
|
||||||
g_Channel[3].m_pDevice->SendCommand(g_Channel[3].m_Out.Hex, g_Poll.EN3);
|
g_Channel[3].m_pDevice->SendCommand(g_Channel[3].m_Out.Hex, g_Poll.EN3);
|
||||||
|
#if defined(__LIBUSB__) || defined (_WIN32)
|
||||||
|
SI_GCAdapter::Output(g_Channel);
|
||||||
|
#endif
|
||||||
g_StatusReg.WR = 0;
|
g_StatusReg.WR = 0;
|
||||||
g_StatusReg.WRST0 = 0;
|
g_StatusReg.WRST0 = 0;
|
||||||
g_StatusReg.WRST1 = 0;
|
g_StatusReg.WRST1 = 0;
|
||||||
|
@ -473,22 +428,30 @@ static void SetNoResponse(u32 channel)
|
||||||
void ChangeDeviceCallback(u64 userdata, int cyclesLate)
|
void ChangeDeviceCallback(u64 userdata, int cyclesLate)
|
||||||
{
|
{
|
||||||
u8 channel = (u8)(userdata >> 32);
|
u8 channel = (u8)(userdata >> 32);
|
||||||
|
SIDevices device = (SIDevices)(u32)userdata;
|
||||||
|
|
||||||
g_Channel[channel].m_Out.Hex = 0;
|
// Skip redundant (spammed) device changes
|
||||||
g_Channel[channel].m_InHi.Hex = 0;
|
if (GetDeviceType(channel) != device)
|
||||||
g_Channel[channel].m_InLo.Hex = 0;
|
{
|
||||||
|
g_Channel[channel].m_Out.Hex = 0;
|
||||||
|
g_Channel[channel].m_InHi.Hex = 0;
|
||||||
|
g_Channel[channel].m_InLo.Hex = 0;
|
||||||
|
|
||||||
SetNoResponse(channel);
|
SetNoResponse(channel);
|
||||||
|
|
||||||
AddDevice((SIDevices)(u32)userdata, channel);
|
AddDevice(device, channel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChangeDevice(SIDevices device, int channel)
|
void ChangeDevice(SIDevices device, int channel)
|
||||||
{
|
{
|
||||||
// Called from GUI, so we need to make it thread safe.
|
// Called from GUI, so we need to make it thread safe.
|
||||||
// Let the hardware see no device for .5b cycles
|
// Let the hardware see no device for .5b cycles
|
||||||
CoreTiming::ScheduleEvent_Threadsafe(0, changeDevice, ((u64)channel << 32) | SIDEVICE_NONE);
|
if (GetDeviceType(channel) != device)
|
||||||
CoreTiming::ScheduleEvent_Threadsafe(500000000, changeDevice, ((u64)channel << 32) | device);
|
{
|
||||||
|
CoreTiming::ScheduleEvent_Threadsafe(0, changeDevice, ((u64)channel << 32) | SIDEVICE_NONE);
|
||||||
|
CoreTiming::ScheduleEvent_Threadsafe(500000000, changeDevice, ((u64)channel << 32) | device);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateDevices()
|
void UpdateDevices()
|
||||||
|
@ -499,6 +462,29 @@ void UpdateDevices()
|
||||||
g_StatusReg.RDST2 = !!g_Channel[2].m_pDevice->GetData(g_Channel[2].m_InHi.Hex, g_Channel[2].m_InLo.Hex);
|
g_StatusReg.RDST2 = !!g_Channel[2].m_pDevice->GetData(g_Channel[2].m_InHi.Hex, g_Channel[2].m_InLo.Hex);
|
||||||
g_StatusReg.RDST3 = !!g_Channel[3].m_pDevice->GetData(g_Channel[3].m_InHi.Hex, g_Channel[3].m_InLo.Hex);
|
g_StatusReg.RDST3 = !!g_Channel[3].m_pDevice->GetData(g_Channel[3].m_InHi.Hex, g_Channel[3].m_InLo.Hex);
|
||||||
|
|
||||||
|
// Check for connected GC Adapter
|
||||||
|
#if defined(__LIBUSB__) || defined (_WIN32)
|
||||||
|
if (SConfig::GetInstance().m_GameCubeAdapter)
|
||||||
|
{
|
||||||
|
g_StatusReg.RDST0 |= (SI_GCAdapter::GetDeviceType(0) != SIDEVICE_NONE);
|
||||||
|
g_StatusReg.RDST1 |= (SI_GCAdapter::GetDeviceType(1) != SIDEVICE_NONE);
|
||||||
|
g_StatusReg.RDST2 |= (SI_GCAdapter::GetDeviceType(2) != SIDEVICE_NONE);
|
||||||
|
g_StatusReg.RDST3 |= (SI_GCAdapter::GetDeviceType(3) != SIDEVICE_NONE);
|
||||||
|
|
||||||
|
for (int chan = 0; chan < MAX_SI_CHANNELS; chan++)
|
||||||
|
{
|
||||||
|
SIDevices connected_device = SI_GCAdapter::GetDeviceType(chan);
|
||||||
|
SIDevices configured_device = SConfig::GetInstance().m_SIDevice[chan];
|
||||||
|
|
||||||
|
if (connected_device != SIDEVICE_NONE)
|
||||||
|
ChangeDevice(connected_device, chan);
|
||||||
|
else
|
||||||
|
ChangeDevice(configured_device, chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
SI_GCAdapter::Input(g_Channel);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
UpdateInterrupts();
|
UpdateInterrupts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,56 @@ enum
|
||||||
namespace SerialInterface
|
namespace SerialInterface
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// SI Channel Output
|
||||||
|
union USIChannelOut
|
||||||
|
{
|
||||||
|
u32 Hex;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u32 OUTPUT1 : 8;
|
||||||
|
u32 OUTPUT0 : 8;
|
||||||
|
u32 CMD : 8;
|
||||||
|
u32 : 8;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// SI Channel Input High u32
|
||||||
|
union USIChannelIn_Hi
|
||||||
|
{
|
||||||
|
u32 Hex;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u32 INPUT3 : 8;
|
||||||
|
u32 INPUT2 : 8;
|
||||||
|
u32 INPUT1 : 8;
|
||||||
|
u32 INPUT0 : 6;
|
||||||
|
u32 ERRLATCH : 1; // 0: no error 1: Error latched. Check SISR.
|
||||||
|
u32 ERRSTAT : 1; // 0: no error 1: error on last transfer
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// SI Channel Input Low u32
|
||||||
|
union USIChannelIn_Lo
|
||||||
|
{
|
||||||
|
u32 Hex;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
u32 INPUT7 : 8;
|
||||||
|
u32 INPUT6 : 8;
|
||||||
|
u32 INPUT5 : 8;
|
||||||
|
u32 INPUT4 : 8;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// SI Channel
|
||||||
|
struct SSIChannel
|
||||||
|
{
|
||||||
|
USIChannelOut m_Out;
|
||||||
|
USIChannelIn_Hi m_InHi;
|
||||||
|
USIChannelIn_Lo m_InLo;
|
||||||
|
ISIDevice* m_pDevice;
|
||||||
|
};
|
||||||
|
|
||||||
void Init();
|
void Init();
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
void DoState(PointerWrap &p);
|
void DoState(PointerWrap &p);
|
||||||
|
|
|
@ -0,0 +1,284 @@
|
||||||
|
// Copyright 2013 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <libusb.h>
|
||||||
|
|
||||||
|
#include "Core/ConfigManager.h"
|
||||||
|
#include "Core/HW/SI_GCAdapter.h"
|
||||||
|
#include "InputCommon/GCPadStatus.h"
|
||||||
|
|
||||||
|
static const u8 ENDPOINT_IN = 0x81;
|
||||||
|
static const u8 ENDPOINT_OUT = 0x02;
|
||||||
|
|
||||||
|
namespace SI_GCAdapter
|
||||||
|
{
|
||||||
|
|
||||||
|
static libusb_device_handle* handle = nullptr;
|
||||||
|
static bool controller_connected[MAX_SI_CHANNELS] = { false, false, false, false };
|
||||||
|
static u8 controller_payload[37];
|
||||||
|
static u8 controller_last_rumble[4];
|
||||||
|
static int controller_payload_size = 0;
|
||||||
|
|
||||||
|
static std::thread adapter_thread;
|
||||||
|
static bool adapter_thread_running;
|
||||||
|
|
||||||
|
static bool libusb_driver_not_supported = false;
|
||||||
|
|
||||||
|
void Read()
|
||||||
|
{
|
||||||
|
while (adapter_thread_running)
|
||||||
|
{
|
||||||
|
libusb_interrupt_transfer(handle, ENDPOINT_IN, controller_payload, sizeof(controller_payload), &controller_payload_size, 0);
|
||||||
|
Common::YieldCPU();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Init()
|
||||||
|
{
|
||||||
|
if (handle != nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
libusb_driver_not_supported = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_SI_CHANNELS; i++)
|
||||||
|
{
|
||||||
|
controller_connected[i] = false;
|
||||||
|
controller_last_rumble[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = libusb_init(nullptr);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
ERROR_LOG(SERIALINTERFACE, "libusb_init failed with error: %d", ret);
|
||||||
|
Shutdown();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
libusb_device** list;
|
||||||
|
ssize_t cnt = libusb_get_device_list(nullptr, &list);
|
||||||
|
for (int d = 0; d < cnt; d++)
|
||||||
|
{
|
||||||
|
libusb_device* device = list[d];
|
||||||
|
libusb_device_descriptor desc;
|
||||||
|
int dRet = libusb_get_device_descriptor(device, &desc);
|
||||||
|
if (dRet)
|
||||||
|
{
|
||||||
|
// could not aquire the descriptor, no point in trying to use it.
|
||||||
|
ERROR_LOG(SERIALINTERFACE, "libusb_get_device_descriptor failed with error: %d", dRet);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (desc.idVendor == 0x057e && desc.idProduct == 0x0337)
|
||||||
|
{
|
||||||
|
NOTICE_LOG(SERIALINTERFACE, "Found GC Adapter with Vendor: %X Product: %X Devnum: %d", desc.idVendor, desc.idProduct, 1);
|
||||||
|
|
||||||
|
u8 bus = libusb_get_bus_number(device);
|
||||||
|
u8 port = libusb_get_device_address(device);
|
||||||
|
ret = libusb_open(device, &handle);
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
if (ret == LIBUSB_ERROR_ACCESS)
|
||||||
|
{
|
||||||
|
if (dRet)
|
||||||
|
{
|
||||||
|
ERROR_LOG(SERIALINTERFACE, "Dolphin does not have access to this device: Bus %03d Device %03d: ID ????:???? (couldn't get id).",
|
||||||
|
bus,
|
||||||
|
port
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ERROR_LOG(SERIALINTERFACE, "Dolphin does not have access to this device: Bus %03d Device %03d: ID %04X:%04X.",
|
||||||
|
bus,
|
||||||
|
port,
|
||||||
|
desc.idVendor,
|
||||||
|
desc.idProduct
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ERROR_LOG(SERIALINTERFACE, "libusb_open failed to open device with error = %d", ret);
|
||||||
|
if (ret == LIBUSB_ERROR_NOT_SUPPORTED)
|
||||||
|
libusb_driver_not_supported = true;
|
||||||
|
}
|
||||||
|
Shutdown();
|
||||||
|
}
|
||||||
|
else if ((ret = libusb_kernel_driver_active(handle, 0)) == 1)
|
||||||
|
{
|
||||||
|
if ((ret = libusb_detach_kernel_driver(handle, 0)) && ret != LIBUSB_ERROR_NOT_SUPPORTED)
|
||||||
|
{
|
||||||
|
ERROR_LOG(SERIALINTERFACE, "libusb_detach_kernel_driver failed with error: %d", ret);
|
||||||
|
Shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ret != 0 && ret != LIBUSB_ERROR_NOT_SUPPORTED)
|
||||||
|
{
|
||||||
|
ERROR_LOG(SERIALINTERFACE, "libusb_kernel_driver_active error ret = %d", ret);
|
||||||
|
Shutdown();
|
||||||
|
}
|
||||||
|
else if ((ret = libusb_claim_interface(handle, 0)))
|
||||||
|
{
|
||||||
|
ERROR_LOG(SERIALINTERFACE, "libusb_claim_interface failed with error: %d", ret);
|
||||||
|
Shutdown();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int tmp = 0;
|
||||||
|
unsigned char payload = 0x13;
|
||||||
|
libusb_interrupt_transfer(handle, ENDPOINT_OUT, &payload, sizeof(payload), &tmp, 0);
|
||||||
|
|
||||||
|
RefreshConnectedDevices();
|
||||||
|
|
||||||
|
adapter_thread_running = true;
|
||||||
|
adapter_thread = std::thread(Read);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
libusb_free_device_list(list, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shutdown()
|
||||||
|
{
|
||||||
|
if (handle == nullptr || !SConfig::GetInstance().m_GameCubeAdapter)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (adapter_thread_running)
|
||||||
|
{
|
||||||
|
adapter_thread_running = false;
|
||||||
|
adapter_thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
libusb_close(handle);
|
||||||
|
libusb_driver_not_supported = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_SI_CHANNELS; i++)
|
||||||
|
controller_connected[i] = false;
|
||||||
|
|
||||||
|
handle = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Input(SerialInterface::SSIChannel* g_Channel)
|
||||||
|
{
|
||||||
|
if (handle == nullptr || !SConfig::GetInstance().m_GameCubeAdapter)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (controller_payload_size != 0x25 || controller_payload[0] != 0x21)
|
||||||
|
{
|
||||||
|
ERROR_LOG(SERIALINTERFACE, "error reading payload (size: %d)", controller_payload_size);
|
||||||
|
Shutdown();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int chan = 0; chan < MAX_SI_CHANNELS; chan++)
|
||||||
|
{
|
||||||
|
bool connected = (controller_payload[1 + (9 * chan)] > 0);
|
||||||
|
if (connected && !controller_connected[chan])
|
||||||
|
NOTICE_LOG(SERIALINTERFACE, "New device connected to Port %d of Type: %02x", chan + 1, controller_payload[1 + (9 * chan)]);
|
||||||
|
|
||||||
|
controller_connected[chan] = connected;
|
||||||
|
|
||||||
|
if (controller_connected[chan])
|
||||||
|
{
|
||||||
|
g_Channel[chan].m_InHi.Hex = 0;
|
||||||
|
g_Channel[chan].m_InLo.Hex = 0;
|
||||||
|
for (int j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
g_Channel[chan].m_InHi.Hex |= (controller_payload[2 + chan + j + (8 * chan) + 0] << (8 * (3 - j)));
|
||||||
|
g_Channel[chan].m_InLo.Hex |= (controller_payload[2 + chan + j + (8 * chan) + 4] << (8 * (3 - j)));
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 buttons_0 = ((g_Channel[chan].m_InHi.Hex >> 24) & 0xf0) >> 4;
|
||||||
|
u8 buttons_1 = (g_Channel[chan].m_InHi.Hex >> 24) & 0x0f;
|
||||||
|
u8 buttons_2 = ((g_Channel[chan].m_InHi.Hex >> 16) & 0xf0) >> 4;
|
||||||
|
u8 buttons_3 = (g_Channel[chan].m_InHi.Hex >> 16) & 0x0f;
|
||||||
|
g_Channel[chan].m_InHi.Hex = buttons_3 << 28 | buttons_1 << 24 | (buttons_2) << 20 | buttons_0 << 16 | (g_Channel[chan].m_InHi.Hex & 0x0000ffff);
|
||||||
|
|
||||||
|
if (controller_payload[1 + (9 * chan)] == 0x10)
|
||||||
|
g_Channel[chan].m_InHi.Hex |= (PAD_USE_ORIGIN << 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Output(SerialInterface::SSIChannel* g_Channel)
|
||||||
|
{
|
||||||
|
if (handle == nullptr || !SConfig::GetInstance().m_GameCubeAdapter)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool rumble_update = false;
|
||||||
|
for (int chan = 0; chan < MAX_SI_CHANNELS; chan++)
|
||||||
|
{
|
||||||
|
u8 current_rumble = g_Channel[chan].m_Out.Hex & 0xff;
|
||||||
|
if (current_rumble != controller_last_rumble[chan])
|
||||||
|
rumble_update = true;
|
||||||
|
|
||||||
|
controller_last_rumble[chan] = current_rumble;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rumble_update)
|
||||||
|
{
|
||||||
|
unsigned char rumble[5] = { 0x11, static_cast<u8>(g_Channel[0].m_Out.Hex & 0xff), static_cast<u8>(g_Channel[1].m_Out.Hex & 0xff), static_cast<u8>(g_Channel[2].m_Out.Hex & 0xff), static_cast<u8>(g_Channel[3].m_Out.Hex & 0xff) };
|
||||||
|
int size = 0;
|
||||||
|
libusb_interrupt_transfer(handle, ENDPOINT_OUT, rumble, sizeof(rumble), &size, 0);
|
||||||
|
|
||||||
|
if (size != 0x05)
|
||||||
|
{
|
||||||
|
WARN_LOG(SERIALINTERFACE, "error reading rumble (size: %d)", size);
|
||||||
|
Shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SIDevices GetDeviceType(int channel)
|
||||||
|
{
|
||||||
|
if (handle == nullptr || !SConfig::GetInstance().m_GameCubeAdapter)
|
||||||
|
return SIDEVICE_NONE;
|
||||||
|
|
||||||
|
if (controller_connected[channel])
|
||||||
|
return SIDEVICE_GC_CONTROLLER;
|
||||||
|
else
|
||||||
|
return SIDEVICE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RefreshConnectedDevices()
|
||||||
|
{
|
||||||
|
if (handle == nullptr || !SConfig::GetInstance().m_GameCubeAdapter)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int size = 0;
|
||||||
|
libusb_interrupt_transfer(handle, ENDPOINT_IN, controller_payload, sizeof(controller_payload), &size, 0);
|
||||||
|
|
||||||
|
if (size != 0x25 || controller_payload[0] != 0x21)
|
||||||
|
{
|
||||||
|
WARN_LOG(SERIALINTERFACE, "error reading payload (size: %d)", size);
|
||||||
|
Shutdown();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int chan = 0; chan < MAX_SI_CHANNELS; chan++)
|
||||||
|
{
|
||||||
|
bool connected = (controller_payload[1 + (9 * chan)] > 0);
|
||||||
|
if (connected && !controller_connected[chan])
|
||||||
|
NOTICE_LOG(SERIALINTERFACE, "New device connected to Port %d of Type: %02x", chan + 1, controller_payload[1 + (9 * chan)]);
|
||||||
|
|
||||||
|
controller_connected[chan] = connected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsDetected()
|
||||||
|
{
|
||||||
|
return handle != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsDriverDetected()
|
||||||
|
{
|
||||||
|
return !libusb_driver_not_supported;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end of namespace SI_GCAdapter
|
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright 2013 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Common/Thread.h"
|
||||||
|
#include "Core/HW/SI.h"
|
||||||
|
|
||||||
|
struct libusb_device_handle;
|
||||||
|
struct libusb_device_descriptor;
|
||||||
|
struct libusb_config_descriptor;
|
||||||
|
struct libusb_interface_descriptor;
|
||||||
|
struct libusb_endpoint_descriptor;
|
||||||
|
struct libusb_transfer;
|
||||||
|
|
||||||
|
namespace SI_GCAdapter
|
||||||
|
{
|
||||||
|
|
||||||
|
void Init();
|
||||||
|
void Shutdown();
|
||||||
|
void Input(SerialInterface::SSIChannel* g_Channel);
|
||||||
|
void Output(SerialInterface::SSIChannel* g_Channel);
|
||||||
|
SIDevices GetDeviceType(int channel);
|
||||||
|
void RefreshConnectedDevices();
|
||||||
|
bool IsDetected();
|
||||||
|
bool IsDriverDetected();
|
||||||
|
|
||||||
|
} // end of namespace SI_GCAdapter
|
|
@ -27,6 +27,9 @@
|
||||||
#include "Core/NetPlayProto.h"
|
#include "Core/NetPlayProto.h"
|
||||||
#include "Core/HW/GCPad.h"
|
#include "Core/HW/GCPad.h"
|
||||||
#include "Core/HW/SI.h"
|
#include "Core/HW/SI.h"
|
||||||
|
#if defined(__LIBUSB__) || defined (_WIN32)
|
||||||
|
#include "Core/HW/SI_GCAdapter.h"
|
||||||
|
#endif
|
||||||
#include "Core/HW/Wiimote.h"
|
#include "Core/HW/Wiimote.h"
|
||||||
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
#include "Core/HW/WiimoteReal/WiimoteReal.h"
|
||||||
#include "DolphinWX/ControllerConfigDiag.h"
|
#include "DolphinWX/ControllerConfigDiag.h"
|
||||||
|
@ -68,7 +71,7 @@ ControllerConfigDiag::ControllerConfigDiag(wxWindow* const parent)
|
||||||
|
|
||||||
wxStaticBoxSizer* ControllerConfigDiag::CreateGamecubeSizer()
|
wxStaticBoxSizer* ControllerConfigDiag::CreateGamecubeSizer()
|
||||||
{
|
{
|
||||||
wxStaticBoxSizer* const gamecube_static_sizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("GameCube Controllers"));
|
wxStaticBoxSizer* const gamecube_static_sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("GameCube Controllers"));
|
||||||
wxFlexGridSizer* const gamecube_flex_sizer = new wxFlexGridSizer(3, 5, 5);
|
wxFlexGridSizer* const gamecube_flex_sizer = new wxFlexGridSizer(3, 5, 5);
|
||||||
|
|
||||||
wxStaticText* pad_labels[4];
|
wxStaticText* pad_labels[4];
|
||||||
|
@ -101,8 +104,22 @@ wxStaticBoxSizer* ControllerConfigDiag::CreateGamecubeSizer()
|
||||||
if (NetPlay::IsNetPlayRunning() || Movie::IsMovieActive())
|
if (NetPlay::IsNetPlayRunning() || Movie::IsMovieActive())
|
||||||
pad_type_choices[i]->Disable();
|
pad_type_choices[i]->Disable();
|
||||||
|
|
||||||
|
SIDevices selected_device = SConfig::GetInstance().m_SIDevice[i];
|
||||||
|
|
||||||
|
#if defined(__LIBUSB__) || defined (_WIN32)
|
||||||
|
if (Core::GetState() != Core::CORE_UNINITIALIZED)
|
||||||
|
{
|
||||||
|
SI_GCAdapter::RefreshConnectedDevices();
|
||||||
|
if (SI_GCAdapter::GetDeviceType(i) != SIDEVICE_NONE)
|
||||||
|
{
|
||||||
|
pad_type_choices[i]->Disable();
|
||||||
|
selected_device = SI_GCAdapter::GetDeviceType(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Set the saved pad type as the default choice.
|
// Set the saved pad type as the default choice.
|
||||||
switch (SConfig::GetInstance().m_SIDevice[i])
|
switch (selected_device)
|
||||||
{
|
{
|
||||||
case SIDEVICE_GC_CONTROLLER:
|
case SIDEVICE_GC_CONTROLLER:
|
||||||
pad_type_choices[i]->SetStringSelection(m_gc_pad_type_strs[1]);
|
pad_type_choices[i]->SetStringSelection(m_gc_pad_type_strs[1]);
|
||||||
|
@ -134,6 +151,36 @@ wxStaticBoxSizer* ControllerConfigDiag::CreateGamecubeSizer()
|
||||||
}
|
}
|
||||||
|
|
||||||
gamecube_static_sizer->Add(gamecube_flex_sizer, 1, wxEXPAND, 5);
|
gamecube_static_sizer->Add(gamecube_flex_sizer, 1, wxEXPAND, 5);
|
||||||
|
gamecube_static_sizer->AddSpacer(5);
|
||||||
|
|
||||||
|
wxStaticBoxSizer* const gamecube_adapter_group = new wxStaticBoxSizer(wxHORIZONTAL, this, _("GameCube Adapter"));
|
||||||
|
wxBoxSizer* const gamecube_adapter_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
|
||||||
|
wxCheckBox* const gamecube_adapter = new wxCheckBox(this, wxID_ANY, _("Direct Connect"));
|
||||||
|
gamecube_adapter->Bind(wxEVT_CHECKBOX, &ControllerConfigDiag::OnGameCubeAdapter, this);
|
||||||
|
|
||||||
|
gamecube_adapter_sizer->Add(gamecube_adapter, 0, wxEXPAND);
|
||||||
|
gamecube_adapter_group->Add(gamecube_adapter_sizer, 0, wxEXPAND);
|
||||||
|
gamecube_static_sizer->Add(gamecube_adapter_group, 0, wxEXPAND);
|
||||||
|
|
||||||
|
#if defined(__LIBUSB__) || defined (_WIN32)
|
||||||
|
if (!SI_GCAdapter::IsDetected())
|
||||||
|
{
|
||||||
|
if (!SI_GCAdapter::IsDriverDetected())
|
||||||
|
gamecube_adapter->SetLabelText(_("Driver Not Detected"));
|
||||||
|
else
|
||||||
|
gamecube_adapter->SetLabelText(_("Adapter Not Detected"));
|
||||||
|
gamecube_adapter->SetValue(false);
|
||||||
|
gamecube_adapter->Disable();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gamecube_adapter->SetValue(SConfig::GetInstance().m_GameCubeAdapter);
|
||||||
|
if (Core::GetState() != Core::CORE_UNINITIALIZED)
|
||||||
|
gamecube_adapter->Disable();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return gamecube_static_sizer;
|
return gamecube_static_sizer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,11 @@ public:
|
||||||
SConfig::GetInstance().m_WiimoteEnableSpeaker = event.IsChecked();
|
SConfig::GetInstance().m_WiimoteEnableSpeaker = event.IsChecked();
|
||||||
event.Skip();
|
event.Skip();
|
||||||
}
|
}
|
||||||
|
void OnGameCubeAdapter(wxCommandEvent& event)
|
||||||
|
{
|
||||||
|
SConfig::GetInstance().m_GameCubeAdapter = event.IsChecked();
|
||||||
|
event.Skip();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
wxStaticBoxSizer* CreateGamecubeSizer();
|
wxStaticBoxSizer* CreateGamecubeSizer();
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
#include "Common/Logging/LogManager.h"
|
#include "Common/Logging/LogManager.h"
|
||||||
|
|
||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
|
#if defined(__LIBUSB__) || defined (_WIN32)
|
||||||
|
#include "Core/HW/SI_GCAdapter.h"
|
||||||
|
#endif
|
||||||
#include "Core/HW/Wiimote.h"
|
#include "Core/HW/Wiimote.h"
|
||||||
|
|
||||||
#include "UICommon/UICommon.h"
|
#include "UICommon/UICommon.h"
|
||||||
|
@ -22,7 +25,9 @@ void Init()
|
||||||
SConfig::Init();
|
SConfig::Init();
|
||||||
VideoBackend::PopulateList();
|
VideoBackend::PopulateList();
|
||||||
WiimoteReal::LoadSettings();
|
WiimoteReal::LoadSettings();
|
||||||
|
#if defined(__LIBUSB__) || defined (_WIN32)
|
||||||
|
SI_GCAdapter::Init();
|
||||||
|
#endif
|
||||||
VideoBackend::ActivateBackend(SConfig::GetInstance().m_LocalCoreStartupParameter.m_strVideoBackend);
|
VideoBackend::ActivateBackend(SConfig::GetInstance().m_LocalCoreStartupParameter.m_strVideoBackend);
|
||||||
|
|
||||||
SetEnableAlert(SConfig::GetInstance().m_LocalCoreStartupParameter.bUsePanicHandlers);
|
SetEnableAlert(SConfig::GetInstance().m_LocalCoreStartupParameter.bUsePanicHandlers);
|
||||||
|
@ -30,6 +35,9 @@ void Init()
|
||||||
|
|
||||||
void Shutdown()
|
void Shutdown()
|
||||||
{
|
{
|
||||||
|
#if defined(__LIBUSB__) || defined (_WIN32)
|
||||||
|
SI_GCAdapter::Shutdown();
|
||||||
|
#endif
|
||||||
WiimoteReal::Shutdown();
|
WiimoteReal::Shutdown();
|
||||||
VideoBackend::ClearList();
|
VideoBackend::ClearList();
|
||||||
SConfig::Shutdown();
|
SConfig::Shutdown();
|
||||||
|
|
Loading…
Reference in New Issue