Added libusb class + libusb log option

This commit is contained in:
ergo720 2021-10-31 14:28:04 +01:00
parent b748c5f61a
commit 006af26a6e
12 changed files with 351 additions and 3 deletions

View File

@ -65,6 +65,7 @@ file (GLOB CXBXR_HEADER_COMMON
"${CXBXR_ROOT_DIR}/src/common/input/layout_xbox_device.h"
"${CXBXR_ROOT_DIR}/src/common/input/InputDevice.h"
"${CXBXR_ROOT_DIR}/src/common/input/InputManager.h"
"${CXBXR_ROOT_DIR}/src/common/input/LibusbDevice.h"
"${CXBXR_ROOT_DIR}/src/common/input/SdlJoystick.h"
"${CXBXR_ROOT_DIR}/src/common/input/XInputPad.h"
"${CXBXR_ROOT_DIR}/src/common/input/RawDevice.h"
@ -236,6 +237,7 @@ file (GLOB CXBXR_SOURCE_COMMON
"${CXBXR_ROOT_DIR}/src/common/input/DInputKeyboardMouse.cpp"
"${CXBXR_ROOT_DIR}/src/common/input/InputDevice.cpp"
"${CXBXR_ROOT_DIR}/src/common/input/InputManager.cpp"
"${CXBXR_ROOT_DIR}/src/common/input/LibusbDevice.cpp"
"${CXBXR_ROOT_DIR}/src/common/input/SdlJoystick.cpp"
"${CXBXR_ROOT_DIR}/src/common/input/XInputPad.cpp"
"${CXBXR_ROOT_DIR}/src/common/input/RawDevice.cpp"

View File

@ -82,6 +82,7 @@ const char* g_EnumModules2String[to_underlying(CXBXR_MODULE::MAX)] = {
"XMO ",
"RINP ",
"JVS ",
"LIBUSB ",
"KRNL ",
"LOG ",
"XBOX ",

View File

@ -90,6 +90,7 @@ typedef enum class _CXBXR_MODULE: unsigned int {
XMO,
RINP,
JVS,
LIBUSB,
// kernel
KRNL,
LOG,

View File

@ -77,6 +77,14 @@ std::string GetInputDeviceName(int dev_type)
str = "Arcade joystick";
break;
case to_underlying(XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER):
str = "Passthrough steel battalion controller";
break;
case to_underlying(XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER):
str = "Passthrough original xbox gamepad";
break;
case to_underlying(XBOX_INPUT_DEVICE::DEVICE_INVALID):
str = "None";
break;

View File

@ -56,6 +56,9 @@ typedef enum class _XBOX_INPUT_DEVICE : int {
STEEL_BATTALION_CONTROLLER,
ARCADE_STICK,
DEVICE_MAX,
// Devices with the HW_ prefix (= hardware) indicate a real xbox device. Always add these after DEVICE_MAX
HW_STEEL_BATTALION_CONTROLLER,
HW_XBOX_CONTROLLER,
}
XBOX_INPUT_DEVICE;
@ -120,6 +123,8 @@ public:
bool GetPort(std::string_view Port) const;
// sets the port this device is attached to
void SetPort(std::string_view Port, bool Connect);
// retuns true if it is a libusb device, false otherwise
virtual bool IsLibusb() const { return false; };
protected:

View File

@ -41,6 +41,7 @@
#include "XInputPad.h"
#include "RawDevice.h"
#include "DInputKeyboardMouse.h"
#include "LibusbDevice.h"
#include "InputManager.h"
#include "..\devices\usb\XidGamepad.h"
#include "core\kernel\exports\EmuKrnl.h" // For EmuLog
@ -91,11 +92,12 @@ void InputDeviceManager::Initialize(bool is_gui, HWND hwnd)
m_Cv.wait(lck, []() {
return (Sdl::InitStatus != Sdl::NOT_INIT) &&
(XInput::InitStatus != XInput::NOT_INIT) &&
(RawInput::InitStatus != RawInput::NOT_INIT);
(RawInput::InitStatus != RawInput::NOT_INIT) &&
(Libusb::InitStatus != Libusb::NOT_INIT);
});
lck.unlock();
if (Sdl::InitStatus < 0 || XInput::InitStatus < 0 || RawInput::InitStatus < 0) {
if (Sdl::InitStatus < 0 || XInput::InitStatus < 0 || RawInput::InitStatus < 0 || Libusb::InitStatus < 0) {
CxbxrKrnlAbort("Failed to initialize input subsystem! Consult debug log for more information");
}
@ -616,6 +618,7 @@ void InputDeviceManager::RefreshDevices()
XInput::PopulateDevices();
DInput::PopulateDevices();
Sdl::PopulateDevices();
Libusb::PopulateDevices();
lck.lock();
m_Cv.wait(lck, []() {
return Sdl::PopulateOK;
@ -719,7 +722,7 @@ void InputDeviceManager::HotplugHandler(bool is_sdl)
std::unique_lock<std::mutex> lck(m_Mtx);
auto it = std::remove_if(m_Devices.begin(), m_Devices.end(), [](const auto &Device) {
if (StrStartsWith(Device->GetAPI(), "XInput")) {
if (Device->IsLibusb() || StrStartsWith(Device->GetAPI(), "XInput")) {
return true;
}
return false;
@ -730,6 +733,9 @@ void InputDeviceManager::HotplugHandler(bool is_sdl)
lck.unlock();
XInput::PopulateDevices();
// Unfortunately, as documented in this issue https://github.com/libusb/libusb/issues/86, when this was written libusb did not yet support
// device hotplug on Windows, so we add the below call here. This will only work if rawinput detects the libusb device.
Libusb::PopulateDevices();
}
for (int port = PORT_1; port <= PORT_4; ++port) {

View File

@ -0,0 +1,239 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// * GNU General Public License for more details.
// *
// * You should have recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2021 ergo720
// *
// * All rights reserved
// *
// ******************************************************************
#define LOG_PREFIX CXBXR_MODULE::LIBUSB
#include "LibusbDevice.h"
#include "InputManager.h"
#include "core\kernel\support\Emu.h"
// Sanitiy check: ensure out libusb version is high enough for libusb_get_device_descriptor to succeed
static_assert(LIBUSB_API_VERSION >= 0x01000102);
namespace Libusb
{
int InitStatus = NOT_INIT;
static libusb_device **List = nullptr;
// These come from here https://github.com/xboxdrv/xboxdrv/blob/ac6ebb1228962220482ea03743cadbe18754246c/src/xpad_device.cpp#L29
static constexpr uint16_t SupportedDevices_VidPid[][2] = { // vid, pid
0x0d2f, 0x0002,
0x045e, 0x0202,
0x045e, 0x0285,
0x045e, 0x0287,
0x045e, 0x0289,
0x046d, 0xca84,
0x046d, 0xca88,
0x05fd, 0x1007,
0x05fd, 0x107a,
0x0738, 0x4516,
0x0738, 0x4522,
0x0738, 0x4526,
0x0738, 0x4536,
0x0738, 0x4556,
0x0c12, 0x8802,
0x0c12, 0x8810,
0x0c12, 0x9902,
0x0e4c, 0x1097,
0x0e4c, 0x2390,
0x0e6f, 0x0003,
0x0e6f, 0x0005,
0x0e6f, 0x0006,
0x0f30, 0x0202,
0x0f30, 0x8888,
0x102c, 0xff0c,
0x044f, 0x0f07,
0x0e8f, 0x3008,
};
static constexpr const char *SupportedDevices_Name[] = {
"Andamiro Pump It Up pad",
"Microsoft X-Box pad v1 (US)",
"Microsoft X-Box pad (Japan)",
"Microsoft Xbox Controller S",
"Microsoft X-Box pad v2 (US)",
"Logitech Xbox Cordless Controller",
"Logitech Compact Controller for Xbox",
"Mad Catz Controller (unverified)",
"InterAct 'PowerPad Pro' X-Box pad (Germany)",
"Mad Catz Control Pad",
"Mad Catz LumiCON",
"Mad Catz Control Pad Pro",
"Mad Catz MicroCON",
"Mad Catz Lynx Wireless Controller",
"Zeroplus Xbox Controller",
"Zeroplus Xbox Controller",
"HAMA VibraX - *FAULTY HARDWARE*",
"Radica Gamester Controller",
"Radica Games Jtech Controller",
"Logic3 Freebird wireless Controller",
"Eclipse wireless Controller",
"Edge wireless Controller",
"Joytech Advanced Controller",
"BigBen XBMiniPad Controller",
"Joytech Wireless Advanced Controller",
"Thrustmaster, Inc. Controller",
"Generic xbox control (dealextreme)",
};
static_assert(ARRAY_SIZE(SupportedDevices_VidPid) == ARRAY_SIZE(SupportedDevices_Name));
void Init(std::mutex &Mtx)
{
std::unique_lock<std::mutex> lck(Mtx);
// We only use a single libusb session per cxbxr process, so we do not need to use a libusb context
if (libusb_init(nullptr) != 0) {
EmuLog(LOG_LEVEL::ERROR2, "Failed to initialize Libusb!");
InitStatus = INIT_ERROR;
return;
}
InitStatus = INIT_SUCCESS;
}
void DeInit()
{
InitStatus = NOT_INIT;
libusb_exit(nullptr);
}
void PopulateDevices()
{
// NOTE: the libusb docs say that the list is always appended with a NULL element at the end
ssize_t DevicesConnected = libusb_get_device_list(nullptr, &List) - 1;
if (DevicesConnected < 0) {
EmuLog(LOG_LEVEL::ERROR2, "Failed to enumerate devices. The error was: %s", libusb_strerror(DevicesConnected));
return;
}
for (ssize_t i = 0; i < DevicesConnected; ++i) {
libusb_device *LibusbDev = List[i];
libusb_device_descriptor Desc;
libusb_get_device_descriptor(LibusbDev, &Desc); // always succeeds when LIBUSB_API_VERSION >= 0x01000102
auto Device = std::make_shared<LibusbDevice>(&Desc, LibusbDev);
if (Device->IsLibusb()) {
g_InputDeviceManager.AddDevice(std::move(Device));
}
}
libusb_free_device_list(List, 1);
List = nullptr;
}
void GetDeviceChanges()
{
g_InputDeviceManager.RemoveDevice([](const auto &Device) {
const LibusbDevice *dev = dynamic_cast<const LibusbDevice *>(Device);
return dev->IsLibusb();
});
PopulateDevices();
}
bool LibusbDevice::UpdateInput()
{
// not sure of this yet
return true;
}
LibusbDevice::LibusbDevice(libusb_device_descriptor *Desc, libusb_device *Dev)
{
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
// The SBC's VID and PID are taken from https://xboxdevwiki.net/Xbox_Input_Devices#Steel_Battalion_Controller
if ((Desc->idVendor == 0x0a7b) && (Desc->idProduct == 0xd000)) {
m_Type = XBOX_INPUT_DEVICE::HW_STEEL_BATTALION_CONTROLLER;
m_Name = "Steel battalion controller";
assert(Desc->bcdUSB == 0x110); // must be a usb 1.1 device
}
else {
for (size_t i = 0; i < ARRAY_SIZE(SupportedDevices_VidPid); ++i) {
if ((Desc->idVendor = SupportedDevices_VidPid[i][0]) && (Desc->idProduct == SupportedDevices_VidPid[i][1])) {
m_Type = XBOX_INPUT_DEVICE::HW_XBOX_CONTROLLER;
m_Name = SupportedDevices_Name[i];
assert(Desc->bcdUSB == 0x110); // must be a usb 1.1 device
break;
}
}
}
if (m_Type == XBOX_INPUT_DEVICE::DEVICE_INVALID) { return; }
// Duke, S and SBC have 1 configuration, 1 interface and 2 endpoints (input and output) and use the default alternate setting zero.
// The code below assumes that third-party controllers follow suit.
if (libusb_open(Dev, &m_hDev) == 0) {
libusb_config_descriptor *Desc;
if (libusb_get_active_config_descriptor(Dev, &Desc) == 0) {
if (Desc->bNumInterfaces == 1) {
auto Iface = Desc->interface[0];
if (Iface.num_altsetting == 1) {
auto Setting = Iface.altsetting[0];
if (Setting.bNumEndpoints == 2) {
for (uint8_t i = 0; i < 2; ++i) {
auto Endpoint = Setting.endpoint[i];
if (Endpoint.bmAttributes & LIBUSB_ENDPOINT_TRANSFER_TYPE_INTERRUPT) {
if (Endpoint.bEndpointAddress & 0x80) {
m_EndpointIn = Endpoint.bEndpointAddress;
m_IntervalIn = Endpoint.bInterval;
}
else {
m_EndpointOut = Endpoint.bEndpointAddress;
m_IntervalOut = Endpoint.bInterval;
}
}
}
}
else {
EmuLog(LOG_LEVEL::INFO, "Rejected device because of unexpected number of endpoints, bNumEndpoints: %d", Setting.bNumEndpoints);
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
}
}
else {
EmuLog(LOG_LEVEL::INFO, "Rejected device because of unexpected number of alternative settings, num_altsetting: %d", Iface.num_altsetting);
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
}
}
else {
EmuLog(LOG_LEVEL::INFO, "Rejected device because of unexpected number of interfaces, bNumInterfaces: %d", Desc->bNumInterfaces);
m_Type = XBOX_INPUT_DEVICE::DEVICE_INVALID;
}
libusb_free_config_descriptor(Desc);
}
}
}
std::string LibusbDevice::GetDeviceName() const
{
return m_Name;
}
std::string LibusbDevice::GetAPI() const
{
return "Libusb";
}
}

View File

@ -0,0 +1,80 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// * GNU General Public License for more details.
// *
// * You should have recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2021 ergo720
// *
// * All rights reserved
// *
// ******************************************************************
#pragma once
#include "InputDevice.h"
// Suppress warning in libusb about zero sized array
#pragma warning(push)
#pragma warning(disable: 4200)
#include "libusb.h"
#pragma warning(pop)
namespace Libusb
{
typedef enum _INIT_STATUS : int
{
NOT_INIT = -2,
INIT_ERROR,
INIT_SUCCESS,
}
INIT_STATUS;
extern int InitStatus;
// initialize Libusb
void Init(std::mutex &Mtx);
// shutdown Libusb
void DeInit();
// refresh the device list in response to a refresh command from the input GUI
void PopulateDevices();
// update the device list
void GetDeviceChanges();
class LibusbDevice : public InputDevice
{
public:
bool UpdateInput() override;
LibusbDevice(libusb_device_descriptor *Desc, libusb_device *Dev);
std::string GetDeviceName() const override;
std::string GetAPI() const override;
bool IsLibusb() const override { return m_Type != XBOX_INPUT_DEVICE::DEVICE_INVALID; }
private:
XBOX_INPUT_DEVICE m_Type;
std::string m_Name;
libusb_device_handle *m_hDev;
unsigned char m_EndpointIn;
unsigned char m_EndpointOut;
uint8_t m_IntervalIn;
uint8_t m_IntervalOut;
};
}

View File

@ -41,6 +41,7 @@
#include "SdlJoystick.h"
#include "XInputPad.h"
#include "DInputKeyboardMouse.h"
#include "LibusbDevice.h"
#include "InputManager.h"
// These values are those used by Dolphin!
@ -156,6 +157,7 @@ namespace Sdl
else {
XInput::GetDeviceChanges();
DInput::GetDeviceChanges();
Libusb::GetDeviceChanges();
std::string port = std::to_string(*static_cast<int *>(Event.user.data1));
int port_num, slot;
PortStr2Int(port, &port_num, &slot);

View File

@ -84,6 +84,7 @@ static int g_DlgIndexes[] = {
IDC_LOG_XMO,
IDC_LOG_RINP,
IDC_LOG_JVS,
IDC_LOG_LIBUSB,
// Kernel
IDC_LOG_KRNL,
IDC_LOG_LOG,
@ -386,6 +387,7 @@ INT_PTR CALLBACK DlgLogConfigProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM
case IDC_LOG_DINP:
case IDC_LOG_RINP:
case IDC_LOG_XINP:
case IDC_LOG_LIBUSB:
case IDC_LOG_JVS:
case IDC_LOG_SDL:
case IDC_LOG_FILE:

View File

@ -508,6 +508,7 @@ BEGIN
CONTROL "VSHCACHE",IDC_LOG_VSHCACHE,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,68,140,53,10
CONTROL "RINP",IDC_LOG_RINP,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,202,195,32,10
CONTROL "JVS",IDC_LOG_JVS,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,38,206,28,10
CONTROL "LIBUSB",IDC_LOG_LIBUSB,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,80,206,41,10
END
IDD_ABOUT DIALOGEX 0, 0, 310, 177

View File

@ -94,6 +94,7 @@
#define IDC_LOG_VSHCACHE 962
#define IDC_LOG_RINP 963
#define IDC_LOG_JVS 964
#define IDC_LOG_LIBUSB 965
#define IDC_DEVICE_LIST_TOP_SLOT 995
#define IDC_DEVICE_LIST_BOTTOM_SLOT 996
#define IDC_DEVICE_TOP_SLOT 997