nsyshid: Add backends for cross platform USB passthrough support (#950)

This commit is contained in:
Simon
2023-09-19 01:27:40 +02:00
committed by GitHub
parent 2a735f1fb7
commit 98b5a8758a
17 changed files with 2297 additions and 523 deletions
+1 -1
View File
@@ -232,7 +232,7 @@ jobs:
- name: "Install system dependencies"
run: |
brew update
brew install llvm@15 ninja nasm molten-vk
brew install llvm@15 ninja nasm molten-vk automake libtool
- name: "Bootstrap vcpkg"
run: |
+1 -1
View File
@@ -86,7 +86,7 @@ You can skip this section if you have an Intel Mac. Every time you compile, you
### Installing dependencies
`brew install boost git cmake llvm ninja nasm molten-vk`
`brew install boost git cmake llvm ninja nasm molten-vk automake libtool`
### Build Cemu using cmake and clang
1. `git clone --recursive https://github.com/cemu-project/Cemu`
+17
View File
@@ -102,6 +102,23 @@ if (WIN32)
endif()
option(ENABLE_CUBEB "Enabled cubeb backend" ON)
# usb hid backends
if (WIN32)
option(ENABLE_NSYSHID_WINDOWS_HID "Enables the native Windows HID backend for nsyshid" ON)
endif ()
# libusb and windows hid backends shouldn't be active at the same time; otherwise we'd see all devices twice!
if (NOT ENABLE_NSYSHID_WINDOWS_HID)
option(ENABLE_NSYSHID_LIBUSB "Enables the libusb backend for nsyshid" ON)
else ()
set(ENABLE_NSYSHID_LIBUSB OFF CACHE BOOL "" FORCE)
endif ()
if (ENABLE_NSYSHID_WINDOWS_HID)
add_compile_definitions(NSYSHID_ENABLE_BACKEND_WINDOWS_HID)
endif ()
if (ENABLE_NSYSHID_LIBUSB)
add_compile_definitions(NSYSHID_ENABLE_BACKEND_LIBUSB)
endif ()
option(ENABLE_WXWIDGETS "Build with wxWidgets UI (Currently required)" ON)
set(THREADS_PREFER_PTHREAD_FLAG true)
+20
View File
@@ -0,0 +1,20 @@
# SPDX-FileCopyrightText: 2022 Andrea Pappacoda <andrea@pappacoda.it>
# SPDX-License-Identifier: ISC
find_package(libusb CONFIG)
if (NOT libusb_FOUND)
find_package(PkgConfig)
if (PKG_CONFIG_FOUND)
pkg_search_module(libusb IMPORTED_TARGET GLOBAL libusb-1.0 libusb)
if (libusb_FOUND)
add_library(libusb::libusb ALIAS PkgConfig::libusb)
endif ()
endif ()
endif ()
find_package_handle_standard_args(libusb
REQUIRED_VARS
libusb_LINK_LIBRARIES
libusb_FOUND
VERSION_VAR libusb_VERSION
)
+19
View File
@@ -434,6 +434,14 @@ add_library(CemuCafe
OS/libs/nn_uds/nn_uds.h
OS/libs/nsyshid/nsyshid.cpp
OS/libs/nsyshid/nsyshid.h
OS/libs/nsyshid/Backend.h
OS/libs/nsyshid/AttachDefaultBackends.cpp
OS/libs/nsyshid/Whitelist.cpp
OS/libs/nsyshid/Whitelist.h
OS/libs/nsyshid/BackendLibusb.cpp
OS/libs/nsyshid/BackendLibusb.h
OS/libs/nsyshid/BackendWindowsHID.cpp
OS/libs/nsyshid/BackendWindowsHID.h
OS/libs/nsyskbd/nsyskbd.cpp
OS/libs/nsyskbd/nsyskbd.h
OS/libs/nsysnet/nsysnet.cpp
@@ -524,6 +532,17 @@ if (ENABLE_WAYLAND)
target_link_libraries(CemuCafe PUBLIC Wayland::Client)
endif()
if (ENABLE_NSYSHID_LIBUSB)
if (ENABLE_VCPKG)
find_package(libusb CONFIG REQUIRED)
target_include_directories(CemuCafe PRIVATE ${LIBUSB_INCLUDE_DIRS})
target_link_libraries(CemuCafe PRIVATE ${LIBUSB_LIBRARIES})
else ()
find_package(libusb MODULE REQUIRED)
target_link_libraries(CemuCafe PRIVATE libusb::libusb)
endif ()
endif ()
if (ENABLE_WXWIDGETS)
target_link_libraries(CemuCafe PRIVATE wx::base wx::core)
endif()
@@ -0,0 +1,41 @@
#include "nsyshid.h"
#include "Backend.h"
#if NSYSHID_ENABLE_BACKEND_LIBUSB
#include "BackendLibusb.h"
#endif
#if NSYSHID_ENABLE_BACKEND_WINDOWS_HID
#include "BackendWindowsHID.h"
#endif
namespace nsyshid::backend
{
void AttachDefaultBackends()
{
#if NSYSHID_ENABLE_BACKEND_LIBUSB
// add libusb backend
{
auto backendLibusb = std::make_shared<backend::libusb::BackendLibusb>();
if (backendLibusb->IsInitialisedOk())
{
AttachBackend(backendLibusb);
}
}
#endif // NSYSHID_ENABLE_BACKEND_LIBUSB
#if NSYSHID_ENABLE_BACKEND_WINDOWS_HID
// add windows hid backend
{
auto backendWindowsHID = std::make_shared<backend::windows::BackendWindowsHID>();
if (backendWindowsHID->IsInitialisedOk())
{
AttachBackend(backendWindowsHID);
}
}
#endif // NSYSHID_ENABLE_BACKEND_WINDOWS_HID
}
} // namespace nsyshid::backend
+141
View File
@@ -0,0 +1,141 @@
#ifndef CEMU_NSYSHID_BACKEND_H
#define CEMU_NSYSHID_BACKEND_H
#include <list>
#include <memory>
#include <mutex>
#include "Common/precompiled.h"
namespace nsyshid
{
typedef struct
{
/* +0x00 */ uint32be handle;
/* +0x04 */ uint32 ukn04;
/* +0x08 */ uint16 vendorId; // little-endian ?
/* +0x0A */ uint16 productId; // little-endian ?
/* +0x0C */ uint8 ifIndex;
/* +0x0D */ uint8 subClass;
/* +0x0E */ uint8 protocol;
/* +0x0F */ uint8 paddingGuessed0F;
/* +0x10 */ uint16be maxPacketSizeRX;
/* +0x12 */ uint16be maxPacketSizeTX;
} HID_t;
static_assert(offsetof(HID_t, vendorId) == 0x8, "");
static_assert(offsetof(HID_t, productId) == 0xA, "");
static_assert(offsetof(HID_t, ifIndex) == 0xC, "");
static_assert(offsetof(HID_t, protocol) == 0xE, "");
class Device {
public:
Device() = delete;
Device(uint16 vendorId,
uint16 productId,
uint8 interfaceIndex,
uint8 interfaceSubClass,
uint8 protocol);
Device(const Device& device) = delete;
Device& operator=(const Device& device) = delete;
virtual ~Device() = default;
HID_t* m_hid; // this info is passed to applications and must remain intact
uint16 m_vendorId;
uint16 m_productId;
uint8 m_interfaceIndex;
uint8 m_interfaceSubClass;
uint8 m_protocol;
uint16 m_maxPacketSizeRX;
uint16 m_maxPacketSizeTX;
virtual void AssignHID(HID_t* hid);
virtual bool Open() = 0;
virtual void Close() = 0;
virtual bool IsOpened() = 0;
enum class ReadResult
{
Success,
Error,
ErrorTimeout,
};
virtual ReadResult Read(uint8* data, sint32 length, sint32& bytesRead) = 0;
enum class WriteResult
{
Success,
Error,
ErrorTimeout,
};
virtual WriteResult Write(uint8* data, sint32 length, sint32& bytesWritten) = 0;
virtual bool GetDescriptor(uint8 descType,
uint8 descIndex,
uint8 lang,
uint8* output,
uint32 outputMaxLength) = 0;
virtual bool SetProtocol(uint32 ifIndef, uint32 protocol) = 0;
virtual bool SetReport(uint8* reportData, sint32 length, uint8* originalData, sint32 originalLength) = 0;
};
class Backend {
public:
Backend();
Backend(const Backend& backend) = delete;
Backend& operator=(const Backend& backend) = delete;
virtual ~Backend() = default;
void DetachAllDevices();
// called from nsyshid when this backend is attached - do not call this yourself!
void OnAttach();
// called from nsyshid when this backend is detached - do not call this yourself!
void OnDetach();
bool IsBackendAttached();
virtual bool IsInitialisedOk() = 0;
protected:
// try to attach a device - only works if this backend is attached
bool AttachDevice(const std::shared_ptr<Device>& device);
void DetachDevice(const std::shared_ptr<Device>& device);
std::shared_ptr<Device> FindDevice(std::function<bool(const std::shared_ptr<Device>&)> isWantedDevice);
bool IsDeviceWhitelisted(uint16 vendorId, uint16 productId);
// called from OnAttach() - attach devices that your backend can see here
virtual void AttachVisibleDevices() = 0;
private:
std::list<std::shared_ptr<Device>> m_devices;
std::recursive_mutex m_devicesMutex;
bool m_isAttached;
};
namespace backend
{
void AttachDefaultBackends();
}
} // namespace nsyshid
#endif // CEMU_NSYSHID_BACKEND_H
File diff suppressed because it is too large Load Diff
+129
View File
@@ -0,0 +1,129 @@
#ifndef CEMU_NSYSHID_BACKEND_LIBUSB_H
#define CEMU_NSYSHID_BACKEND_LIBUSB_H
#include "nsyshid.h"
#if NSYSHID_ENABLE_BACKEND_LIBUSB
#include <libusb-1.0/libusb.h>
#include "Backend.h"
namespace nsyshid::backend::libusb
{
class BackendLibusb : public nsyshid::Backend {
public:
BackendLibusb();
~BackendLibusb();
bool IsInitialisedOk() override;
protected:
void AttachVisibleDevices() override;
private:
libusb_context* m_ctx;
int m_initReturnCode;
bool m_callbackRegistered;
libusb_hotplug_callback_handle m_hotplugCallbackHandle;
std::thread m_hotplugThread;
std::atomic<bool> m_hotplugThreadStop;
// called by libusb
static int HotplugCallback(libusb_context* ctx, libusb_device* dev,
libusb_hotplug_event event, void* user_data);
int OnHotplug(libusb_device* dev, libusb_hotplug_event event);
std::shared_ptr<Device> CheckAndCreateDevice(libusb_device* dev);
std::shared_ptr<Device> FindLibusbDevice(libusb_device* dev);
bool FindDefaultDeviceEndpoints(libusb_device* dev,
bool& endpointInFound, uint8& endpointIn, uint16& endpointInMaxPacketSize,
bool& endpointOutFound, uint8& endpointOut, uint16& endpointOutMaxPacketSize);
};
class DeviceLibusb : public nsyshid::Device {
public:
DeviceLibusb(libusb_context* ctx,
uint16 vendorId,
uint16 productId,
uint8 interfaceIndex,
uint8 interfaceSubClass,
uint8 protocol,
uint8 libusbBusNumber,
uint8 libusbDeviceAddress);
~DeviceLibusb() override;
bool Open() override;
void Close() override;
bool IsOpened() override;
ReadResult Read(uint8* data, sint32 length, sint32& bytesRead) override;
WriteResult Write(uint8* data, sint32 length, sint32& bytesWritten) override;
bool GetDescriptor(uint8 descType,
uint8 descIndex,
uint8 lang,
uint8* output,
uint32 outputMaxLength) override;
bool SetProtocol(uint32 ifIndex, uint32 protocol) override;
bool SetReport(uint8* reportData, sint32 length, uint8* originalData, sint32 originalLength) override;
uint8 m_libusbBusNumber;
uint8 m_libusbDeviceAddress;
bool m_libusbHasEndpointIn;
uint8 m_libusbEndpointIn;
bool m_libusbHasEndpointOut;
uint8 m_libusbEndpointOut;
private:
void CloseDevice();
libusb_context* m_ctx;
std::mutex m_handleMutex;
std::atomic<sint32> m_handleInUseCounter;
std::condition_variable m_handleInUseCounterDecremented;
libusb_device_handle* m_libusbHandle;
class HandleLock {
public:
HandleLock() = delete;
HandleLock(libusb_device_handle** handle,
std::mutex& handleMutex,
std::atomic<sint32>& handleInUseCounter,
std::condition_variable& handleInUseCounterDecremented,
DeviceLibusb& device);
~HandleLock();
HandleLock(const HandleLock&) = delete;
HandleLock& operator=(const HandleLock&) = delete;
bool IsValid();
libusb_device_handle* GetHandle();
private:
libusb_device_handle* m_handle;
std::mutex& m_handleMutex;
std::atomic<sint32>& m_handleInUseCounter;
std::condition_variable& m_handleInUseCounterDecremented;
};
std::unique_ptr<HandleLock> AquireHandleLock();
};
} // namespace nsyshid::backend::libusb
#endif // NSYSHID_ENABLE_BACKEND_LIBUSB
#endif // CEMU_NSYSHID_BACKEND_LIBUSB_H
@@ -0,0 +1,454 @@
#include "BackendWindowsHID.h"
#if NSYSHID_ENABLE_BACKEND_WINDOWS_HID
#include <setupapi.h>
#include <initguid.h>
#include <hidsdi.h>
#pragma comment(lib, "Setupapi.lib")
#pragma comment(lib, "hid.lib")
DEFINE_GUID(GUID_DEVINTERFACE_HID,
0x4D1E55B2L, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30);
namespace nsyshid::backend::windows
{
BackendWindowsHID::BackendWindowsHID()
{
}
void BackendWindowsHID::AttachVisibleDevices()
{
// add all currently connected devices
HDEVINFO hDevInfo;
SP_DEVICE_INTERFACE_DATA DevIntfData;
PSP_DEVICE_INTERFACE_DETAIL_DATA DevIntfDetailData;
SP_DEVINFO_DATA DevData;
DWORD dwSize, dwMemberIdx;
hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_HID, NULL, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
if (hDevInfo != INVALID_HANDLE_VALUE)
{
DevIntfData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
dwMemberIdx = 0;
SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &GUID_DEVINTERFACE_HID,
dwMemberIdx, &DevIntfData);
while (GetLastError() != ERROR_NO_MORE_ITEMS)
{
DevData.cbSize = sizeof(DevData);
SetupDiGetDeviceInterfaceDetail(
hDevInfo, &DevIntfData, NULL, 0, &dwSize, NULL);
DevIntfDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
dwSize);
DevIntfDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &DevIntfData,
DevIntfDetailData, dwSize, &dwSize, &DevData))
{
HANDLE hHIDDevice = OpenDevice(DevIntfDetailData->DevicePath);
if (hHIDDevice != INVALID_HANDLE_VALUE)
{
auto device = CheckAndCreateDevice(DevIntfDetailData->DevicePath, hHIDDevice);
if (device != nullptr)
{
if (IsDeviceWhitelisted(device->m_vendorId, device->m_productId))
{
if (!AttachDevice(device))
{
cemuLog_log(LogType::Force,
"nsyshid::BackendWindowsHID: failed to attach device: {:04x}:{:04x}",
device->m_vendorId,
device->m_productId);
}
}
else
{
cemuLog_log(LogType::Force,
"nsyshid::BackendWindowsHID: device not on whitelist: {:04x}:{:04x}",
device->m_vendorId,
device->m_productId);
}
}
CloseHandle(hHIDDevice);
}
}
HeapFree(GetProcessHeap(), 0, DevIntfDetailData);
// next
SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &GUID_DEVINTERFACE_HID, ++dwMemberIdx, &DevIntfData);
}
SetupDiDestroyDeviceInfoList(hDevInfo);
}
}
BackendWindowsHID::~BackendWindowsHID()
{
}
bool BackendWindowsHID::IsInitialisedOk()
{
return true;
}
std::shared_ptr<Device> BackendWindowsHID::CheckAndCreateDevice(wchar_t* devicePath, HANDLE hDevice)
{
HIDD_ATTRIBUTES hidAttr;
hidAttr.Size = sizeof(HIDD_ATTRIBUTES);
if (HidD_GetAttributes(hDevice, &hidAttr) == FALSE)
return nullptr;
auto device = std::make_shared<DeviceWindowsHID>(hidAttr.VendorID,
hidAttr.ProductID,
1,
2,
0,
_wcsdup(devicePath));
// get additional device info
sint32 maxPacketInputLength = -1;
sint32 maxPacketOutputLength = -1;
PHIDP_PREPARSED_DATA ppData = nullptr;
if (HidD_GetPreparsedData(hDevice, &ppData))
{
HIDP_CAPS caps;
if (HidP_GetCaps(ppData, &caps) == HIDP_STATUS_SUCCESS)
{
// length includes the report id byte
maxPacketInputLength = caps.InputReportByteLength - 1;
maxPacketOutputLength = caps.OutputReportByteLength - 1;
}
HidD_FreePreparsedData(ppData);
}
if (maxPacketInputLength <= 0 || maxPacketInputLength >= 0xF000)
{
cemuLog_log(LogType::Force, "HID: Input packet length not available or out of range (length = {})",
maxPacketInputLength);
maxPacketInputLength = 0x20;
}
if (maxPacketOutputLength <= 0 || maxPacketOutputLength >= 0xF000)
{
cemuLog_log(LogType::Force, "HID: Output packet length not available or out of range (length = {})",
maxPacketOutputLength);
maxPacketOutputLength = 0x20;
}
device->m_maxPacketSizeRX = maxPacketInputLength;
device->m_maxPacketSizeTX = maxPacketOutputLength;
return device;
}
DeviceWindowsHID::DeviceWindowsHID(uint16 vendorId,
uint16 productId,
uint8 interfaceIndex,
uint8 interfaceSubClass,
uint8 protocol,
wchar_t* devicePath)
: Device(vendorId,
productId,
interfaceIndex,
interfaceSubClass,
protocol),
m_devicePath(devicePath),
m_hFile(INVALID_HANDLE_VALUE)
{
}
DeviceWindowsHID::~DeviceWindowsHID()
{
if (m_hFile != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hFile);
m_hFile = INVALID_HANDLE_VALUE;
}
}
bool DeviceWindowsHID::Open()
{
if (IsOpened())
{
return true;
}
m_hFile = OpenDevice(m_devicePath);
if (m_hFile == INVALID_HANDLE_VALUE)
{
return false;
}
HidD_SetNumInputBuffers(m_hFile, 2); // don't cache too many reports
return true;
}
void DeviceWindowsHID::Close()
{
if (m_hFile != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hFile);
m_hFile = INVALID_HANDLE_VALUE;
}
}
bool DeviceWindowsHID::IsOpened()
{
return m_hFile != INVALID_HANDLE_VALUE;
}
Device::ReadResult DeviceWindowsHID::Read(uint8* data, sint32 length, sint32& bytesRead)
{
bytesRead = 0;
DWORD bt;
OVERLAPPED ovlp = {0};
ovlp.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
uint8* tempBuffer = (uint8*)malloc(length + 1);
sint32 transferLength = 0; // minus report byte
_debugPrintHex("HID_READ_BEFORE", data, length);
cemuLog_logDebug(LogType::Force, "HidRead Begin (Length 0x{:08x})", length);
BOOL readResult = ReadFile(this->m_hFile, tempBuffer, length + 1, &bt, &ovlp);
if (readResult != FALSE)
{
// sometimes we get the result immediately
if (bt == 0)
transferLength = 0;
else
transferLength = bt - 1;
cemuLog_logDebug(LogType::Force, "HidRead Result received immediately (error 0x{:08x}) Length 0x{:08x}",
GetLastError(), transferLength);
}
else
{
// wait for result
cemuLog_logDebug(LogType::Force, "HidRead WaitForResult (error 0x{:08x})", GetLastError());
// async hid read is never supposed to return unless there is a response? Lego Dimensions stops HIDRead calls as soon as one of them fails with a non-zero error (which includes time out)
DWORD r = WaitForSingleObject(ovlp.hEvent, 2000 * 100);
if (r == WAIT_TIMEOUT)
{
cemuLog_logDebug(LogType::Force, "HidRead internal timeout (error 0x{:08x})", GetLastError());
// return -108 in case of timeout
free(tempBuffer);
CloseHandle(ovlp.hEvent);
return ReadResult::ErrorTimeout;
}
cemuLog_logDebug(LogType::Force, "HidRead WaitHalfComplete");
GetOverlappedResult(this->m_hFile, &ovlp, &bt, false);
if (bt == 0)
transferLength = 0;
else
transferLength = bt - 1;
cemuLog_logDebug(LogType::Force, "HidRead WaitComplete Length: 0x{:08x}", transferLength);
}
sint32 returnCode = 0;
ReadResult result = ReadResult::Success;
if (bt != 0)
{
memcpy(data, tempBuffer + 1, transferLength);
sint32 hidReadLength = transferLength;
char debugOutput[1024] = {0};
for (sint32 i = 0; i < transferLength; i++)
{
sprintf(debugOutput + i * 3, "%02x ", tempBuffer[1 + i]);
}
cemuLog_logDebug(LogType::Force, "HIDRead data: {}", debugOutput);
bytesRead = transferLength;
result = ReadResult::Success;
}
else
{
cemuLog_log(LogType::Force, "Failed HID read");
result = ReadResult::Error;
}
free(tempBuffer);
CloseHandle(ovlp.hEvent);
return result;
}
Device::WriteResult DeviceWindowsHID::Write(uint8* data, sint32 length, sint32& bytesWritten)
{
bytesWritten = 0;
DWORD bt;
OVERLAPPED ovlp = {0};
ovlp.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
uint8* tempBuffer = (uint8*)malloc(length + 1);
memcpy(tempBuffer + 1, data, length);
tempBuffer[0] = 0; // report byte?
cemuLog_logDebug(LogType::Force, "HidWrite Begin (Length 0x{:08x})", length);
BOOL writeResult = WriteFile(this->m_hFile, tempBuffer, length + 1, &bt, &ovlp);
if (writeResult != FALSE)
{
// sometimes we get the result immediately
cemuLog_logDebug(LogType::Force, "HidWrite Result received immediately (error 0x{:08x}) Length 0x{:08x}",
GetLastError());
}
else
{
// wait for result
cemuLog_logDebug(LogType::Force, "HidWrite WaitForResult (error 0x{:08x})", GetLastError());
// todo - check for error type
DWORD r = WaitForSingleObject(ovlp.hEvent, 2000);
if (r == WAIT_TIMEOUT)
{
cemuLog_logDebug(LogType::Force, "HidWrite internal timeout");
// return -108 in case of timeout
free(tempBuffer);
CloseHandle(ovlp.hEvent);
return WriteResult::ErrorTimeout;
}
cemuLog_logDebug(LogType::Force, "HidWrite WaitHalfComplete");
GetOverlappedResult(this->m_hFile, &ovlp, &bt, false);
cemuLog_logDebug(LogType::Force, "HidWrite WaitComplete");
}
free(tempBuffer);
CloseHandle(ovlp.hEvent);
if (bt != 0)
{
bytesWritten = length;
return WriteResult::Success;
}
return WriteResult::Error;
}
bool DeviceWindowsHID::GetDescriptor(uint8 descType,
uint8 descIndex,
uint8 lang,
uint8* output,
uint32 outputMaxLength)
{
if (!IsOpened())
{
cemuLog_logDebug(LogType::Force, "nsyshid::DeviceWindowsHID::getDescriptor(): device is not opened");
return false;
}
if (descType == 0x02)
{
uint8 configurationDescriptor[0x29];
uint8* currentWritePtr;
// configuration descriptor
currentWritePtr = configurationDescriptor + 0;
*(uint8*)(currentWritePtr + 0) = 9; // bLength
*(uint8*)(currentWritePtr + 1) = 2; // bDescriptorType
*(uint16be*)(currentWritePtr + 2) = 0x0029; // wTotalLength
*(uint8*)(currentWritePtr + 4) = 1; // bNumInterfaces
*(uint8*)(currentWritePtr + 5) = 1; // bConfigurationValue
*(uint8*)(currentWritePtr + 6) = 0; // iConfiguration
*(uint8*)(currentWritePtr + 7) = 0x80; // bmAttributes
*(uint8*)(currentWritePtr + 8) = 0xFA; // MaxPower
currentWritePtr = currentWritePtr + 9;
// configuration descriptor
*(uint8*)(currentWritePtr + 0) = 9; // bLength
*(uint8*)(currentWritePtr + 1) = 0x04; // bDescriptorType
*(uint8*)(currentWritePtr + 2) = 0; // bInterfaceNumber
*(uint8*)(currentWritePtr + 3) = 0; // bAlternateSetting
*(uint8*)(currentWritePtr + 4) = 2; // bNumEndpoints
*(uint8*)(currentWritePtr + 5) = 3; // bInterfaceClass
*(uint8*)(currentWritePtr + 6) = 0; // bInterfaceSubClass
*(uint8*)(currentWritePtr + 7) = 0; // bInterfaceProtocol
*(uint8*)(currentWritePtr + 8) = 0; // iInterface
currentWritePtr = currentWritePtr + 9;
// configuration descriptor
*(uint8*)(currentWritePtr + 0) = 9; // bLength
*(uint8*)(currentWritePtr + 1) = 0x21; // bDescriptorType
*(uint16be*)(currentWritePtr + 2) = 0x0111; // bcdHID
*(uint8*)(currentWritePtr + 4) = 0x00; // bCountryCode
*(uint8*)(currentWritePtr + 5) = 0x01; // bNumDescriptors
*(uint8*)(currentWritePtr + 6) = 0x22; // bDescriptorType
*(uint16be*)(currentWritePtr + 7) = 0x001D; // wDescriptorLength
currentWritePtr = currentWritePtr + 9;
// endpoint descriptor 1
*(uint8*)(currentWritePtr + 0) = 7; // bLength
*(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType
*(uint8*)(currentWritePtr + 2) = 0x81; // bEndpointAddress
*(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes
*(uint16be*)(currentWritePtr + 4) =
this->m_maxPacketSizeRX; // wMaxPacketSize
*(uint8*)(currentWritePtr + 6) = 0x01; // bInterval
currentWritePtr = currentWritePtr + 7;
// endpoint descriptor 2
*(uint8*)(currentWritePtr + 0) = 7; // bLength
*(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType
*(uint8*)(currentWritePtr + 2) = 0x02; // bEndpointAddress
*(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes
*(uint16be*)(currentWritePtr + 4) =
this->m_maxPacketSizeTX; // wMaxPacketSize
*(uint8*)(currentWritePtr + 6) = 0x01; // bInterval
currentWritePtr = currentWritePtr + 7;
cemu_assert_debug((currentWritePtr - configurationDescriptor) == 0x29);
memcpy(output, configurationDescriptor,
std::min<uint32>(outputMaxLength, sizeof(configurationDescriptor)));
return true;
}
else
{
cemu_assert_unimplemented();
}
return false;
}
bool DeviceWindowsHID::SetProtocol(uint32 ifIndef, uint32 protocol)
{
// ToDo: implement this
// pretend that everything is fine
return true;
}
bool DeviceWindowsHID::SetReport(uint8* reportData, sint32 length, uint8* originalData, sint32 originalLength)
{
sint32 retryCount = 0;
while (true)
{
BOOL r = HidD_SetOutputReport(this->m_hFile, reportData, length);
if (r != FALSE)
break;
Sleep(20); // retry
retryCount++;
if (retryCount >= 50)
{
cemuLog_log(LogType::Force, "nsyshid::DeviceWindowsHID::SetReport(): HID SetReport failed");
return false;
}
}
return true;
}
HANDLE OpenDevice(wchar_t* devicePath)
{
return CreateFile(devicePath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ |
FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
}
void _debugPrintHex(std::string prefix, uint8* data, size_t len)
{
char debugOutput[1024] = {0};
len = std::min(len, (size_t)100);
for (sint32 i = 0; i < len; i++)
{
sprintf(debugOutput + i * 3, "%02x ", data[i]);
}
fmt::print("{} Data: {}\n", prefix, debugOutput);
cemuLog_logDebug(LogType::Force, "[{}] Data: {}", prefix, debugOutput);
}
} // namespace nsyshid::backend::windows
#endif // NSYSHID_ENABLE_BACKEND_WINDOWS_HID
@@ -0,0 +1,66 @@
#ifndef CEMU_NSYSHID_BACKEND_WINDOWS_HID_H
#define CEMU_NSYSHID_BACKEND_WINDOWS_HID_H
#include "nsyshid.h"
#if NSYSHID_ENABLE_BACKEND_WINDOWS_HID
#include "Backend.h"
namespace nsyshid::backend::windows
{
class BackendWindowsHID : public nsyshid::Backend {
public:
BackendWindowsHID();
~BackendWindowsHID();
bool IsInitialisedOk() override;
protected:
void AttachVisibleDevices() override;
private:
std::shared_ptr<Device> CheckAndCreateDevice(wchar_t* devicePath, HANDLE hDevice);
};
class DeviceWindowsHID : public nsyshid::Device {
public:
DeviceWindowsHID(uint16 vendorId,
uint16 productId,
uint8 interfaceIndex,
uint8 interfaceSubClass,
uint8 protocol,
wchar_t* devicePath);
~DeviceWindowsHID();
bool Open() override;
void Close() override;
bool IsOpened() override;
ReadResult Read(uint8* data, sint32 length, sint32& bytesRead) override;
WriteResult Write(uint8* data, sint32 length, sint32& bytesWritten) override;
bool GetDescriptor(uint8 descType, uint8 descIndex, uint8 lang, uint8* output, uint32 outputMaxLength) override;
bool SetProtocol(uint32 ifIndef, uint32 protocol) override;
bool SetReport(uint8* reportData, sint32 length, uint8* originalData, sint32 originalLength) override;
private:
wchar_t* m_devicePath;
HANDLE m_hFile;
};
HANDLE OpenDevice(wchar_t* devicePath);
void _debugPrintHex(std::string prefix, uint8* data, size_t len);
} // namespace nsyshid::backend::windows
#endif // NSYSHID_ENABLE_BACKEND_WINDOWS_HID
#endif // CEMU_NSYSHID_BACKEND_WINDOWS_HID_H
+53
View File
@@ -0,0 +1,53 @@
#include "Whitelist.h"
namespace nsyshid
{
Whitelist& Whitelist::GetInstance()
{
static Whitelist whitelist;
return whitelist;
}
Whitelist::Whitelist()
{
// add known devices
{
// lego dimensions portal
m_devices.emplace_back(0x0e6f, 0x0241);
// skylanders portal
m_devices.emplace_back(0x1430, 0x0150);
// disney infinity base
m_devices.emplace_back(0x0e6f, 0x0129);
}
}
bool Whitelist::IsDeviceWhitelisted(uint16 vendorId, uint16 productId)
{
auto it = std::find(m_devices.begin(), m_devices.end(),
std::tuple<uint16, uint16>(vendorId, productId));
return it != m_devices.end();
}
void Whitelist::AddDevice(uint16 vendorId, uint16 productId)
{
if (!IsDeviceWhitelisted(vendorId, productId))
{
m_devices.emplace_back(vendorId, productId);
}
}
void Whitelist::RemoveDevice(uint16 vendorId, uint16 productId)
{
m_devices.remove(std::tuple<uint16, uint16>(vendorId, productId));
}
std::list<std::tuple<uint16, uint16>> Whitelist::GetDevices()
{
return m_devices;
}
void Whitelist::RemoveAllDevices()
{
m_devices.clear();
}
} // namespace nsyshid
+32
View File
@@ -0,0 +1,32 @@
#ifndef CEMU_NSYSHID_WHITELIST_H
#define CEMU_NSYSHID_WHITELIST_H
namespace nsyshid
{
class Whitelist {
public:
static Whitelist& GetInstance();
Whitelist(const Whitelist&) = delete;
Whitelist& operator=(const Whitelist&) = delete;
bool IsDeviceWhitelisted(uint16 vendorId, uint16 productId);
void AddDevice(uint16 vendorId, uint16 productId);
void RemoveDevice(uint16 vendorId, uint16 productId);
std::list<std::tuple<uint16, uint16>> GetDevices();
void RemoveAllDevices();
private:
Whitelist();
// vendorId, productId
std::list<std::tuple<uint16, uint16>> m_devices;
};
} // namespace nsyshid
#endif // CEMU_NSYSHID_WHITELIST_H
File diff suppressed because it is too large Load Diff
+8 -1
View File
@@ -1,5 +1,12 @@
#pragma once
namespace nsyshid
{
class Backend;
void AttachBackend(const std::shared_ptr<Backend>& backend);
void DetachBackend(const std::shared_ptr<Backend>& backend);
void load();
}
} // namespace nsyshid
@@ -3,6 +3,9 @@
#include <hidsdi.h>
#include <SetupAPI.h>
#pragma comment(lib, "Setupapi.lib")
#pragma comment(lib, "hid.lib")
WinWiimoteDevice::WinWiimoteDevice(HANDLE handle, std::vector<uint8_t> identifier)
: m_handle(handle), m_identifier(std::move(identifier))
{
+2 -1
View File
@@ -46,6 +46,7 @@
"name": "curl",
"default-features": false,
"features": [ "openssl" ]
}
},
"libusb"
]
}