From 22170b0907c8727d5a35ae5757a84a68e0a14bfc Mon Sep 17 00:00:00 2001 From: Joshua de Reeper Date: Wed, 11 Dec 2024 13:55:51 +0100 Subject: [PATCH] Libusb Windows --- CMakeLists.txt | 17 - src/Cafe/CMakeLists.txt | 21 +- .../OS/libs/nsyshid/AttachDefaultBackends.cpp | 23 - src/Cafe/OS/libs/nsyshid/Backend.h | 36 +- src/Cafe/OS/libs/nsyshid/BackendLibusb.cpp | 224 +++++---- src/Cafe/OS/libs/nsyshid/BackendLibusb.h | 25 +- .../OS/libs/nsyshid/BackendWindowsHID.cpp | 444 ------------------ src/Cafe/OS/libs/nsyshid/BackendWindowsHID.h | 66 --- src/Cafe/OS/libs/nsyshid/Dimensions.cpp | 9 +- src/Cafe/OS/libs/nsyshid/Dimensions.h | 6 +- src/Cafe/OS/libs/nsyshid/Infinity.cpp | 11 +- src/Cafe/OS/libs/nsyshid/Infinity.h | 8 +- src/Cafe/OS/libs/nsyshid/Skylander.cpp | 21 +- src/Cafe/OS/libs/nsyshid/Skylander.h | 8 +- src/Cafe/OS/libs/nsyshid/nsyshid.cpp | 304 ++++++++---- 15 files changed, 434 insertions(+), 789 deletions(-) delete mode 100644 src/Cafe/OS/libs/nsyshid/BackendWindowsHID.cpp delete mode 100644 src/Cafe/OS/libs/nsyshid/BackendWindowsHID.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2352389e..560728f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,23 +124,6 @@ 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) diff --git a/src/Cafe/CMakeLists.txt b/src/Cafe/CMakeLists.txt index 76dba007..d51d58d5 100644 --- a/src/Cafe/CMakeLists.txt +++ b/src/Cafe/CMakeLists.txt @@ -463,8 +463,6 @@ add_library(CemuCafe OS/libs/nsyshid/BackendEmulated.h OS/libs/nsyshid/BackendLibusb.cpp OS/libs/nsyshid/BackendLibusb.h - OS/libs/nsyshid/BackendWindowsHID.cpp - OS/libs/nsyshid/BackendWindowsHID.h OS/libs/nsyshid/Dimensions.cpp OS/libs/nsyshid/Dimensions.h OS/libs/nsyshid/Infinity.cpp @@ -569,15 +567,16 @@ if (ENABLE_WAYLAND) target_link_libraries(CemuCafe PUBLIC Wayland::Client) endif() -if (ENABLE_NSYSHID_LIBUSB) - if (ENABLE_VCPKG) - find_package(PkgConfig REQUIRED) - pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0) - target_link_libraries(CemuCafe PRIVATE PkgConfig::libusb) - else () - find_package(libusb MODULE REQUIRED) - target_link_libraries(CemuCafe PRIVATE libusb::libusb) - endif () +if (ENABLE_VCPKG) + if(WIN32) + set(PKG_CONFIG_EXECUTABLE "${VCPKG_INSTALLED_DIR}/x64-windows/tools/pkgconf/pkgconf.exe") + endif() + find_package(PkgConfig REQUIRED) + pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0) + target_link_libraries(CemuCafe PRIVATE PkgConfig::libusb) +else () + find_package(libusb MODULE REQUIRED) + target_link_libraries(CemuCafe PRIVATE libusb::libusb) endif () if (ENABLE_WXWIDGETS) diff --git a/src/Cafe/OS/libs/nsyshid/AttachDefaultBackends.cpp b/src/Cafe/OS/libs/nsyshid/AttachDefaultBackends.cpp index fc8e496c..67eb0240 100644 --- a/src/Cafe/OS/libs/nsyshid/AttachDefaultBackends.cpp +++ b/src/Cafe/OS/libs/nsyshid/AttachDefaultBackends.cpp @@ -1,24 +1,12 @@ #include "nsyshid.h" #include "Backend.h" #include "BackendEmulated.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(); @@ -27,17 +15,6 @@ namespace nsyshid::backend AttachBackend(backendLibusb); } } -#endif // NSYSHID_ENABLE_BACKEND_LIBUSB -#if NSYSHID_ENABLE_BACKEND_WINDOWS_HID - // add windows hid backend - { - auto backendWindowsHID = std::make_shared(); - if (backendWindowsHID->IsInitialisedOk()) - { - AttachBackend(backendWindowsHID); - } - } -#endif // NSYSHID_ENABLE_BACKEND_WINDOWS_HID // add emulated backend { auto backendEmulated = std::make_shared(); diff --git a/src/Cafe/OS/libs/nsyshid/Backend.h b/src/Cafe/OS/libs/nsyshid/Backend.h index 12362773..67dad4fe 100644 --- a/src/Cafe/OS/libs/nsyshid/Backend.h +++ b/src/Cafe/OS/libs/nsyshid/Backend.h @@ -1,5 +1,4 @@ -#ifndef CEMU_NSYSHID_BACKEND_H -#define CEMU_NSYSHID_BACKEND_H +#pragma once #include #include @@ -26,9 +25,9 @@ namespace nsyshid struct TransferCommand { uint8* data; - sint32 length; + uint32 length; - TransferCommand(uint8* data, sint32 length) + TransferCommand(uint8* data, uint32 length) : data(data), length(length) { } @@ -39,7 +38,7 @@ namespace nsyshid { sint32 bytesRead; - ReadMessage(uint8* data, sint32 length, sint32 bytesRead) + ReadMessage(uint8* data, uint32 length, sint32 bytesRead) : bytesRead(bytesRead), TransferCommand(data, length) { } @@ -50,7 +49,7 @@ namespace nsyshid { sint32 bytesWritten; - WriteMessage(uint8* data, sint32 length, sint32 bytesWritten) + WriteMessage(uint8* data, uint32 length, sint32 bytesWritten) : bytesWritten(bytesWritten), TransferCommand(data, length) { } @@ -59,14 +58,11 @@ namespace nsyshid struct ReportMessage final : TransferCommand { - uint8* reportData; - sint32 length; - uint8* originalData; - sint32 originalLength; + uint8 reportType; + uint8 reportId; - ReportMessage(uint8* reportData, sint32 length, uint8* originalData, sint32 originalLength) - : reportData(reportData), length(length), originalData(originalData), - originalLength(originalLength), TransferCommand(reportData, length) + ReportMessage(uint8 reportType, uint8 reportId, uint8* data, uint32 length) + : reportType(reportType), reportId(reportId), TransferCommand(data, length) { } using TransferCommand::TransferCommand; @@ -77,7 +73,8 @@ namespace nsyshid static_assert(offsetof(HID_t, ifIndex) == 0xC, ""); static_assert(offsetof(HID_t, protocol) == 0xE, ""); - class Device { + class Device + { public: Device() = delete; @@ -131,16 +128,21 @@ namespace nsyshid virtual bool GetDescriptor(uint8 descType, uint8 descIndex, - uint8 lang, + uint16 lang, uint8* output, uint32 outputMaxLength) = 0; + virtual bool SetIdle(uint8 ifIndex, + uint8 reportId, + uint8 duration) = 0; + virtual bool SetProtocol(uint8 ifIndex, uint8 protocol) = 0; virtual bool SetReport(ReportMessage* message) = 0; }; - class Backend { + class Backend + { public: Backend(); @@ -188,5 +190,3 @@ namespace nsyshid void AttachDefaultBackends(); } } // namespace nsyshid - -#endif // CEMU_NSYSHID_BACKEND_H diff --git a/src/Cafe/OS/libs/nsyshid/BackendLibusb.cpp b/src/Cafe/OS/libs/nsyshid/BackendLibusb.cpp index ab355136..50edd27a 100644 --- a/src/Cafe/OS/libs/nsyshid/BackendLibusb.cpp +++ b/src/Cafe/OS/libs/nsyshid/BackendLibusb.cpp @@ -1,7 +1,5 @@ #include "BackendLibusb.h" -#if NSYSHID_ENABLE_BACKEND_LIBUSB - namespace nsyshid::backend::libusb { BackendLibusb::BackendLibusb() @@ -15,8 +13,8 @@ namespace nsyshid::backend::libusb if (m_initReturnCode < 0) { m_ctx = nullptr; - cemuLog_logDebug(LogType::Force, "nsyshid::BackendLibusb: failed to initialize libusb, return code: {}", - m_initReturnCode); + cemuLog_log(LogType::Force, "nsyshid::BackendLibusb: failed to initialize libusb, return code: {}", + m_initReturnCode); return; } @@ -34,13 +32,13 @@ namespace nsyshid::backend::libusb &m_hotplugCallbackHandle); if (ret != LIBUSB_SUCCESS) { - cemuLog_logDebug(LogType::Force, - "nsyshid::BackendLibusb: failed to register hotplug callback with return code {}", - ret); + cemuLog_log(LogType::Force, + "nsyshid::BackendLibusb: failed to register hotplug callback with return code {}", + ret); } else { - cemuLog_logDebug(LogType::Force, "nsyshid::BackendLibusb: registered hotplug callback"); + cemuLog_log(LogType::Force, "nsyshid::BackendLibusb: registered hotplug callback"); m_callbackRegistered = true; m_hotplugThread = std::thread([this] { while (!m_hotplugThreadStop) @@ -52,9 +50,9 @@ namespace nsyshid::backend::libusb int ret = libusb_handle_events_timeout_completed(m_ctx, &timeout, nullptr); if (ret != 0) { - cemuLog_logDebug(LogType::Force, - "nsyshid::BackendLibusb: hotplug thread: error handling events: {}", - ret); + cemuLog_log(LogType::Force, + "nsyshid::BackendLibusb: hotplug thread: error handling events: {}", + ret); std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } } @@ -63,7 +61,7 @@ namespace nsyshid::backend::libusb } else { - cemuLog_logDebug(LogType::Force, "nsyshid::BackendLibusb: hotplug not supported by this version of libusb"); + cemuLog_log(LogType::Force, "nsyshid::BackendLibusb: hotplug not supported by this version of libusb"); } } @@ -130,7 +128,7 @@ namespace nsyshid::backend::libusb int ret = libusb_get_device_descriptor(dev, &desc); if (ret < 0) { - cemuLog_logDebug(LogType::Force, "nsyshid::BackendLibusb::OnHotplug(): failed to get device descriptor"); + cemuLog_log(LogType::Force, "nsyshid::BackendLibusb::OnHotplug(): failed to get device descriptor"); return 0; } @@ -138,9 +136,9 @@ namespace nsyshid::backend::libusb { case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: { - cemuLog_logDebug(LogType::Force, "nsyshid::BackendLibusb::OnHotplug(): device arrived: {:04x}:{:04x}", - desc.idVendor, - desc.idProduct); + cemuLog_log(LogType::Force, "nsyshid::BackendLibusb::OnHotplug(): device arrived: {:04x}:{:04x}", + desc.idVendor, + desc.idProduct); auto device = CheckAndCreateDevice(dev); if (device != nullptr) { @@ -166,9 +164,9 @@ namespace nsyshid::backend::libusb break; case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: { - cemuLog_logDebug(LogType::Force, "nsyshid::BackendLibusb::OnHotplug(): device left: {:04x}:{:04x}", - desc.idVendor, - desc.idProduct); + cemuLog_log(LogType::Force, "nsyshid::BackendLibusb::OnHotplug(): device left: {:04x}:{:04x}", + desc.idVendor, + desc.idProduct); auto device = FindLibusbDevice(dev); if (device != nullptr) { @@ -203,8 +201,8 @@ namespace nsyshid::backend::libusb int ret = libusb_get_device_descriptor(dev, &desc); if (ret < 0) { - cemuLog_logDebug(LogType::Force, - "nsyshid::BackendLibusb::FindLibusbDevice(): failed to get device descriptor"); + cemuLog_log(LogType::Force, + "nsyshid::BackendLibusb::FindLibusbDevice(): failed to get device descriptor"); return nullptr; } uint8 busNumber = libusb_get_bus_number(dev); @@ -268,8 +266,8 @@ namespace nsyshid::backend::libusb } if (desc.idVendor == 0x0e6f && desc.idProduct == 0x0241) { - cemuLog_logDebug(LogType::Force, - "nsyshid::BackendLibusb::CheckAndCreateDevice(): lego dimensions portal detected"); + cemuLog_log(LogType::Force, + "nsyshid::BackendLibusb::CheckAndCreateDevice(): lego dimensions portal detected"); } auto device = std::make_shared(m_ctx, desc.idVendor, @@ -408,6 +406,7 @@ namespace nsyshid::backend::libusb } libusb_device* dev; libusb_device* found = nullptr; + uint8 numConfigs = 0; for (int i = 0; (dev = devices[i]) != nullptr; i++) { struct libusb_device_descriptor desc; @@ -427,6 +426,7 @@ namespace nsyshid::backend::libusb { // we found our device! found = dev; + numConfigs = desc.bNumConfigurations; break; } } @@ -446,12 +446,13 @@ namespace nsyshid::backend::libusb } this->m_handleInUseCounter = 0; } + + int ret = ClaimAllInterfaces(0); + + if (ret != 0) { - int ret = ClaimAllInterfaces(0); - if (ret != 0) - { - cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::open(): cannot claim interface"); - } + cemuLog_log(LogType::Force, "nsyshid::DeviceLibusb::open(): cannot claim interface for config 0"); + return false; } } @@ -475,7 +476,7 @@ namespace nsyshid::backend::libusb { m_handleInUseCounterDecremented.wait(lock); } - libusb_release_interface(handle, 0); + ReleaseAllInterfacesForCurrentConfig(); libusb_close(handle); m_handleInUseCounter = -1; m_handleInUseCounterDecremented.notify_all(); @@ -492,22 +493,27 @@ namespace nsyshid::backend::libusb auto handleLock = AquireHandleLock(); if (!handleLock->IsValid()) { - cemuLog_logDebug(LogType::Force, - "nsyshid::DeviceLibusb::read(): cannot read from a non-opened device\n"); + cemuLog_log(LogType::Force, + "nsyshid::DeviceLibusb::read(): cannot read from a non-opened device\n"); return ReadResult::Error; } + for (int i = 0; i < m_config_descriptors.size(); i++) + { + ClaimAllInterfaces(i); + } + const unsigned int timeout = 50; int actualLength = 0; int ret = 0; do { - ret = libusb_bulk_transfer(handleLock->GetHandle(), - this->m_libusbEndpointIn, - message->data, - message->length, - &actualLength, - timeout); + ret = libusb_interrupt_transfer(handleLock->GetHandle(), + this->m_libusbEndpointIn, + message->data, + message->length, + &actualLength, + timeout); } while (ret == LIBUSB_ERROR_TIMEOUT && actualLength == 0 && IsOpened()); @@ -520,9 +526,9 @@ namespace nsyshid::backend::libusb message->bytesRead = actualLength; return ReadResult::Success; } - cemuLog_logDebug(LogType::Force, - "nsyshid::DeviceLibusb::read(): failed with error code: {}", - ret); + cemuLog_log(LogType::Force, + "nsyshid::DeviceLibusb::read(): failed at endpoint 0x{:02x} with error message: {}", this->m_libusbEndpointIn, + libusb_error_name(ret)); return ReadResult::Error; } @@ -531,19 +537,24 @@ namespace nsyshid::backend::libusb auto handleLock = AquireHandleLock(); if (!handleLock->IsValid()) { - cemuLog_logDebug(LogType::Force, - "nsyshid::DeviceLibusb::write(): cannot write to a non-opened device\n"); + cemuLog_log(LogType::Force, + "nsyshid::DeviceLibusb::write(): cannot write to a non-opened device\n"); return WriteResult::Error; } + for (int i = 0; i < m_config_descriptors.size(); i++) + { + ClaimAllInterfaces(i); + } + message->bytesWritten = 0; int actualLength = 0; - int ret = libusb_bulk_transfer(handleLock->GetHandle(), - this->m_libusbEndpointOut, - message->data, - message->length, - &actualLength, - 0); + int ret = libusb_interrupt_transfer(handleLock->GetHandle(), + this->m_libusbEndpointOut, + message->data, + message->length, + &actualLength, + 0); if (ret == 0) { @@ -555,22 +566,22 @@ namespace nsyshid::backend::libusb message->length); return WriteResult::Success; } - cemuLog_logDebug(LogType::Force, - "nsyshid::DeviceLibusb::write(): failed with error code: {}", - ret); + cemuLog_log(LogType::Force, + "nsyshid::DeviceLibusb::write(): failed with error code: {}", + ret); return WriteResult::Error; } bool DeviceLibusb::GetDescriptor(uint8 descType, uint8 descIndex, - uint8 lang, + uint16 lang, uint8* output, uint32 outputMaxLength) { auto handleLock = AquireHandleLock(); if (!handleLock->IsValid()) { - cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::getDescriptor(): device is not opened"); + cemuLog_log(LogType::Force, "nsyshid::DeviceLibusb::getDescriptor(): device is not opened"); return false; } @@ -579,7 +590,6 @@ namespace nsyshid::backend::libusb struct libusb_config_descriptor* conf = nullptr; libusb_device* dev = libusb_get_device(handleLock->GetHandle()); int ret = libusb_get_active_config_descriptor(dev, &conf); - if (ret == 0) { std::vector configurationDescriptor(conf->wTotalLength); @@ -656,7 +666,6 @@ namespace nsyshid::backend::libusb extraReadPointer += bLength; } } - for (int endpointIndex = 0; endpointIndex < altsetting.bNumEndpoints; endpointIndex++) { // endpoint descriptor @@ -681,24 +690,63 @@ namespace nsyshid::backend::libusb uint32 bytesWritten = currentWritePtr - &configurationDescriptor[0]; libusb_free_config_descriptor(conf); cemu_assert_debug(bytesWritten <= conf->wTotalLength); - memcpy(output, &configurationDescriptor[0], std::min(outputMaxLength, bytesWritten)); return true; } - else - { - cemuLog_logDebug(LogType::Force, - "nsyshid::DeviceLibusb::getDescriptor(): failed to get config descriptor with error code: {}", - ret); - return false; - } } else { - cemu_assert_unimplemented(); + uint16 wValue = uint16(descType) << 8 | uint16(descIndex); + // HID Get_Descriptor requests are handled via libusb_control_transfer + int ret = libusb_control_transfer(handleLock->GetHandle(), + LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_ENDPOINT_IN, + LIBUSB_REQUEST_GET_DESCRIPTOR, + wValue, + lang, + output, + outputMaxLength, + 0); + if (ret != outputMaxLength) + { + cemuLog_log(LogType::Force, "nsyshid::DeviceLibusb::GetDescriptor(): Control Transfer Failed: {}", libusb_error_name(ret)); + return false; + } } - return false; + return true; + } + + bool DeviceLibusb::SetIdle(uint8 ifIndex, + uint8 reportId, + uint8 duration) + { + auto handleLock = AquireHandleLock(); + if (!handleLock->IsValid()) + { + cemuLog_log(LogType::Force, "nsyshid::DeviceLibusb::SetIdle(): device is not opened"); + return false; + } + + std::array emptyData = {}; + + uint16 wValue = uint16(duration) << 8 | uint16(reportId); + + // HID Set_Idle requests are handled via libusb_control_transfer + int ret = libusb_control_transfer(handleLock->GetHandle(), + LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT, + HID_CLASS_SET_IDLE, // Defined in HID Class Specific Requests (7.2) + wValue, + ifIndex, + emptyData.data(), + emptyData.size(), + 0); + + if (ret != 0) + { + cemuLog_log(LogType::Force, "nsyshid::DeviceLibusb::SetIdle(): Control Transfer Failed: {}", libusb_error_name(ret)); + return false; + } + return true; } template @@ -764,21 +812,27 @@ namespace nsyshid::backend::libusb auto handleLock = AquireHandleLock(); if (!handleLock->IsValid()) { - cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::SetProtocol(): device is not opened"); + cemuLog_log(LogType::Force, "nsyshid::DeviceLibusb::SetProtocol(): device is not opened"); return false; } - if (m_interfaceIndex != ifIndex) - m_interfaceIndex = ifIndex; - ReleaseAllInterfacesForCurrentConfig(); - int ret = libusb_set_configuration(AquireHandleLock()->GetHandle(), protocol); - if (ret == LIBUSB_SUCCESS) - ret = ClaimAllInterfaces(protocol); + std::array emptyData = {}; - if (ret == LIBUSB_SUCCESS) - return true; + int ret = libusb_control_transfer(handleLock->GetHandle(), + LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT, + HID_CLASS_SET_PROTOCOL, // Defined in HID Class Specific Requests (7.2) + protocol, + ifIndex, + emptyData.data(), + emptyData.size(), + 0); - return false; + if (ret != 0) + { + cemuLog_log(LogType::Force, "nsyshid::DeviceLibusb::SetProtocol(): Control Transfer Failed: {}", libusb_error_name(ret)); + return false; + } + return true; } bool DeviceLibusb::SetReport(ReportMessage* message) @@ -786,22 +840,24 @@ namespace nsyshid::backend::libusb auto handleLock = AquireHandleLock(); if (!handleLock->IsValid()) { - cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::SetReport(): device is not opened"); + cemuLog_log(LogType::Force, "nsyshid::DeviceLibusb::SetReport(): device is not opened"); return false; } + uint16 wValue = uint16(message->reportType) << 8 | uint16(message->reportId); + int ret = libusb_control_transfer(handleLock->GetHandle(), LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT, - LIBUSB_REQUEST_SET_CONFIGURATION, - 512, - 0, - message->originalData, - message->originalLength, + HID_CLASS_SET_REPORT, // Defined in HID Class Specific Requests (7.2) + wValue, + m_interfaceIndex, + message->data, + uint16(message->length & 0xFFFF), 0); - if (ret != message->originalLength) + if (ret != message->length) { - cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::SetReport(): Control Transfer Failed: {}", libusb_error_name(ret)); + cemuLog_log(LogType::Force, "nsyshid::DeviceLibusb::SetReport(): Control Transfer Failed at interface {} : {}", m_interfaceIndex, libusb_error_name(ret)); return false; } return true; @@ -854,5 +910,3 @@ namespace nsyshid::backend::libusb return m_handle; } } // namespace nsyshid::backend::libusb - -#endif // NSYSHID_ENABLE_BACKEND_LIBUSB diff --git a/src/Cafe/OS/libs/nsyshid/BackendLibusb.h b/src/Cafe/OS/libs/nsyshid/BackendLibusb.h index a7b23769..6b2d8e1a 100644 --- a/src/Cafe/OS/libs/nsyshid/BackendLibusb.h +++ b/src/Cafe/OS/libs/nsyshid/BackendLibusb.h @@ -1,15 +1,20 @@ -#ifndef CEMU_NSYSHID_BACKEND_LIBUSB_H -#define CEMU_NSYSHID_BACKEND_LIBUSB_H - #include "nsyshid.h" -#if NSYSHID_ENABLE_BACKEND_LIBUSB - #include #include "Backend.h" namespace nsyshid::backend::libusb { + enum : uint8 + { + HID_CLASS_GET_REPORT = 0x01, + HID_CLASS_GET_IDLE = 0x02, + HID_CLASS_GET_PROTOCOL = 0x03, + HID_CLASS_SET_REPORT = 0x09, + HID_CLASS_SET_IDLE = 0x0A, + HID_CLASS_SET_PROTOCOL = 0x0B + }; + class BackendLibusb : public nsyshid::Backend { public: BackendLibusb(); @@ -75,10 +80,14 @@ namespace nsyshid::backend::libusb bool GetDescriptor(uint8 descType, uint8 descIndex, - uint8 lang, + uint16 lang, uint8* output, uint32 outputMaxLength) override; + bool SetIdle(uint8 ifIndex, + uint8 reportId, + uint8 duration) override; + bool SetProtocol(uint8 ifIndex, uint8 protocol) override; int ClaimAllInterfaces(uint8 config_num); @@ -134,7 +143,3 @@ namespace nsyshid::backend::libusb std::unique_ptr AquireHandleLock(); }; } // namespace nsyshid::backend::libusb - -#endif // NSYSHID_ENABLE_BACKEND_LIBUSB - -#endif // CEMU_NSYSHID_BACKEND_LIBUSB_H diff --git a/src/Cafe/OS/libs/nsyshid/BackendWindowsHID.cpp b/src/Cafe/OS/libs/nsyshid/BackendWindowsHID.cpp deleted file mode 100644 index 267111b2..00000000 --- a/src/Cafe/OS/libs/nsyshid/BackendWindowsHID.cpp +++ /dev/null @@ -1,444 +0,0 @@ -#include "BackendWindowsHID.h" - -#if NSYSHID_ENABLE_BACKEND_WINDOWS_HID - -#include -#include -#include - -#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); - } - } - } - 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 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(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_logDebug(LogType::Force, "HID: Input packet length not available or out of range (length = {})", maxPacketInputLength); - maxPacketInputLength = 0x20; - } - if (maxPacketOutputLength <= 0 || maxPacketOutputLength >= 0xF000) - { - cemuLog_logDebug(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(ReadMessage* message) - { - message->bytesRead = 0; - DWORD bt; - OVERLAPPED ovlp = {0}; - ovlp.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - - uint8* tempBuffer = (uint8*)malloc(message->length + 1); - sint32 transferLength = 0; // minus report byte - - _debugPrintHex("HID_READ_BEFORE", message->data, message->length); - - cemuLog_logDebug(LogType::Force, "HidRead Begin (Length 0x{:08x})", message->length); - BOOL readResult = ReadFile(this->m_hFile, tempBuffer, message->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(message->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); - - message->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(WriteMessage* message) - { - message->bytesWritten = 0; - DWORD bt; - OVERLAPPED ovlp = {0}; - ovlp.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - - uint8* tempBuffer = (uint8*)malloc(message->length + 1); - memcpy(tempBuffer + 1, message->data, message->length); - tempBuffer[0] = 0; // report byte? - - cemuLog_logDebug(LogType::Force, "HidWrite Begin (Length 0x{:08x})", message->length); - BOOL writeResult = WriteFile(this->m_hFile, tempBuffer, message->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) - { - message->bytesWritten = message->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(outputMaxLength, sizeof(configurationDescriptor))); - return true; - } - else - { - cemu_assert_unimplemented(); - } - return false; - } - - bool DeviceWindowsHID::SetProtocol(uint8 ifIndex, uint8 protocol) - { - // ToDo: implement this - // pretend that everything is fine - return true; - } - - bool DeviceWindowsHID::SetReport(ReportMessage* message) - { - sint32 retryCount = 0; - while (true) - { - BOOL r = HidD_SetOutputReport(this->m_hFile, message->reportData, message->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]); - } - cemuLog_logDebug(LogType::Force, "[{}] Data: {}", prefix, debugOutput); - } -} // namespace nsyshid::backend::windows - -#endif // NSYSHID_ENABLE_BACKEND_WINDOWS_HID diff --git a/src/Cafe/OS/libs/nsyshid/BackendWindowsHID.h b/src/Cafe/OS/libs/nsyshid/BackendWindowsHID.h deleted file mode 100644 index 9a8a78e9..00000000 --- a/src/Cafe/OS/libs/nsyshid/BackendWindowsHID.h +++ /dev/null @@ -1,66 +0,0 @@ -#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 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(ReadMessage* message) override; - - WriteResult Write(WriteMessage* message) override; - - bool GetDescriptor(uint8 descType, uint8 descIndex, uint8 lang, uint8* output, uint32 outputMaxLength) override; - - bool SetProtocol(uint8 ifIndex, uint8 protocol) override; - - bool SetReport(ReportMessage* message) 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 diff --git a/src/Cafe/OS/libs/nsyshid/Dimensions.cpp b/src/Cafe/OS/libs/nsyshid/Dimensions.cpp index 8a2acc76..b23560f1 100644 --- a/src/Cafe/OS/libs/nsyshid/Dimensions.cpp +++ b/src/Cafe/OS/libs/nsyshid/Dimensions.cpp @@ -426,7 +426,7 @@ namespace nsyshid bool DimensionsToypadDevice::GetDescriptor(uint8 descType, uint8 descIndex, - uint8 lang, + uint16 lang, uint8* output, uint32 outputMaxLength) { @@ -489,6 +489,13 @@ namespace nsyshid return true; } + bool DimensionsToypadDevice::SetIdle(uint8 ifIndex, + uint8 reportId, + uint8 duration) + { + return true; + } + bool DimensionsToypadDevice::SetProtocol(uint8 ifIndex, uint8 protocol) { cemuLog_log(LogType::Force, "Toypad Protocol"); diff --git a/src/Cafe/OS/libs/nsyshid/Dimensions.h b/src/Cafe/OS/libs/nsyshid/Dimensions.h index d5a2a529..00ceff9e 100644 --- a/src/Cafe/OS/libs/nsyshid/Dimensions.h +++ b/src/Cafe/OS/libs/nsyshid/Dimensions.h @@ -25,10 +25,14 @@ namespace nsyshid bool GetDescriptor(uint8 descType, uint8 descIndex, - uint8 lang, + uint16 lang, uint8* output, uint32 outputMaxLength) override; + bool SetIdle(uint8 ifIndex, + uint8 reportId, + uint8 duration) override; + bool SetProtocol(uint8 ifIndex, uint8 protocol) override; bool SetReport(ReportMessage* message) override; diff --git a/src/Cafe/OS/libs/nsyshid/Infinity.cpp b/src/Cafe/OS/libs/nsyshid/Infinity.cpp index ac793109..94ef817e 100644 --- a/src/Cafe/OS/libs/nsyshid/Infinity.cpp +++ b/src/Cafe/OS/libs/nsyshid/Infinity.cpp @@ -387,7 +387,7 @@ namespace nsyshid bool InfinityBaseDevice::GetDescriptor(uint8 descType, uint8 descIndex, - uint8 lang, + uint16 lang, uint8* output, uint32 outputMaxLength) { @@ -450,6 +450,13 @@ namespace nsyshid return true; } + bool InfinityBaseDevice::SetIdle(uint8 ifIndex, + uint8 reportId, + uint8 duration) + { + return true; + } + bool InfinityBaseDevice::SetProtocol(uint8 ifIndex, uint8 protocol) { return true; @@ -492,7 +499,7 @@ namespace nsyshid return response; } - void InfinityUSB::SendCommand(uint8* buf, sint32 originalLength) + void InfinityUSB::SendCommand(uint8* buf, uint32 length) { const uint8 command = buf[2]; const uint8 sequence = buf[3]; diff --git a/src/Cafe/OS/libs/nsyshid/Infinity.h b/src/Cafe/OS/libs/nsyshid/Infinity.h index aa98fd15..81942abd 100644 --- a/src/Cafe/OS/libs/nsyshid/Infinity.h +++ b/src/Cafe/OS/libs/nsyshid/Infinity.h @@ -26,10 +26,14 @@ namespace nsyshid bool GetDescriptor(uint8 descType, uint8 descIndex, - uint8 lang, + uint16 lang, uint8* output, uint32 outputMaxLength) override; + bool SetIdle(uint8 ifIndex, + uint8 reportId, + uint8 duration) override; + bool SetProtocol(uint8 ifIndex, uint8 protocol) override; bool SetReport(ReportMessage* message) override; @@ -53,7 +57,7 @@ namespace nsyshid void Save(); }; - void SendCommand(uint8* buf, sint32 originalLength); + void SendCommand(uint8* buf, uint32 length); std::array GetStatus(); void GetBlankResponse(uint8 sequence, std::array& replyBuf); diff --git a/src/Cafe/OS/libs/nsyshid/Skylander.cpp b/src/Cafe/OS/libs/nsyshid/Skylander.cpp index 1b4515ef..9fab17b6 100644 --- a/src/Cafe/OS/libs/nsyshid/Skylander.cpp +++ b/src/Cafe/OS/libs/nsyshid/Skylander.cpp @@ -564,7 +564,7 @@ namespace nsyshid bool SkylanderPortalDevice::GetDescriptor(uint8 descType, uint8 descIndex, - uint8 lang, + uint16 lang, uint8* output, uint32 outputMaxLength) { @@ -583,7 +583,7 @@ namespace nsyshid *(uint8*)(currentWritePtr + 7) = 0x80; // bmAttributes *(uint8*)(currentWritePtr + 8) = 0xFA; // MaxPower currentWritePtr = currentWritePtr + 9; - // configuration descriptor + // interface descriptor *(uint8*)(currentWritePtr + 0) = 9; // bLength *(uint8*)(currentWritePtr + 1) = 0x04; // bDescriptorType *(uint8*)(currentWritePtr + 2) = 0; // bInterfaceNumber @@ -594,7 +594,7 @@ namespace nsyshid *(uint8*)(currentWritePtr + 7) = 0; // bInterfaceProtocol *(uint8*)(currentWritePtr + 8) = 0; // iInterface currentWritePtr = currentWritePtr + 9; - // configuration descriptor + // HID descriptor *(uint8*)(currentWritePtr + 0) = 9; // bLength *(uint8*)(currentWritePtr + 1) = 0x21; // bDescriptorType *(uint16be*)(currentWritePtr + 2) = 0x0111; // bcdHID @@ -608,7 +608,7 @@ namespace nsyshid *(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType *(uint8*)(currentWritePtr + 2) = 0x81; // bEndpointAddress *(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes - *(uint16be*)(currentWritePtr + 4) = 0x40; // wMaxPacketSize + *(uint16be*)(currentWritePtr + 4) = 0x0040; // wMaxPacketSize *(uint8*)(currentWritePtr + 6) = 0x01; // bInterval currentWritePtr = currentWritePtr + 7; // endpoint descriptor 2 @@ -616,7 +616,7 @@ namespace nsyshid *(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType *(uint8*)(currentWritePtr + 2) = 0x02; // bEndpointAddress *(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes - *(uint16be*)(currentWritePtr + 4) = 0x40; // wMaxPacketSize + *(uint16be*)(currentWritePtr + 4) = 0x0040; // wMaxPacketSize *(uint8*)(currentWritePtr + 6) = 0x01; // bInterval currentWritePtr = currentWritePtr + 7; @@ -627,6 +627,13 @@ namespace nsyshid return true; } + bool SkylanderPortalDevice::SetIdle(uint8 ifIndex, + uint8 reportId, + uint8 duration) + { + return true; + } + bool SkylanderPortalDevice::SetProtocol(uint8 ifIndex, uint8 protocol) { return true; @@ -634,12 +641,12 @@ namespace nsyshid bool SkylanderPortalDevice::SetReport(ReportMessage* message) { - g_skyportal.ControlTransfer(message->originalData, message->originalLength); + g_skyportal.ControlTransfer(message->data, message->length); std::this_thread::sleep_for(std::chrono::milliseconds(1)); return true; } - void SkylanderUSB::ControlTransfer(uint8* buf, sint32 originalLength) + void SkylanderUSB::ControlTransfer(uint8* buf, uint32 length) { std::array interruptResponse = {}; switch (buf[0]) diff --git a/src/Cafe/OS/libs/nsyshid/Skylander.h b/src/Cafe/OS/libs/nsyshid/Skylander.h index 986ef185..9b9580b0 100644 --- a/src/Cafe/OS/libs/nsyshid/Skylander.h +++ b/src/Cafe/OS/libs/nsyshid/Skylander.h @@ -26,10 +26,14 @@ namespace nsyshid bool GetDescriptor(uint8 descType, uint8 descIndex, - uint8 lang, + uint16 lang, uint8* output, uint32 outputMaxLength) override; + bool SetIdle(uint8 ifIndex, + uint8 reportId, + uint8 duration) override; + bool SetProtocol(uint8 ifIndex, uint8 protocol) override; bool SetReport(ReportMessage* message) override; @@ -70,7 +74,7 @@ namespace nsyshid uint8 blue = 0; }; - void ControlTransfer(uint8* buf, sint32 originalLength); + void ControlTransfer(uint8* buf, uint32 length); void Activate(); void Deactivate(); diff --git a/src/Cafe/OS/libs/nsyshid/nsyshid.cpp b/src/Cafe/OS/libs/nsyshid/nsyshid.cpp index 99a736d9..2fe6da07 100644 --- a/src/Cafe/OS/libs/nsyshid/nsyshid.cpp +++ b/src/Cafe/OS/libs/nsyshid/nsyshid.cpp @@ -305,47 +305,37 @@ namespace nsyshid osLib_returnFromFunction(hCPU, 0); } - void export_HIDGetDescriptor(PPCInterpreter_t* hCPU) + void _debugPrintHex(const std::string prefix, const uint8* data, size_t size) { - ppcDefineParamU32(hidHandle, 0); // r3 - ppcDefineParamU8(descType, 1); // r4 - ppcDefineParamU8(descIndex, 2); // r5 - ppcDefineParamU8(lang, 3); // r6 - ppcDefineParamUStr(output, 4); // r7 - ppcDefineParamU32(outputMaxLength, 5); // r8 - ppcDefineParamMPTR(cbFuncMPTR, 6); // r9 - ppcDefineParamMPTR(cbParamMPTR, 7); // r10 + constexpr size_t BYTES_PER_LINE = 16; - int returnValue = -1; - std::shared_ptr device = GetDeviceByHandle(hidHandle, true); - if (device) + std::string out; + for (size_t row_start = 0; row_start < size; row_start += BYTES_PER_LINE) { - memset(output, 0, outputMaxLength); - if (device->GetDescriptor(descType, descIndex, lang, output, outputMaxLength)) + out += fmt::format("{:06x}: ", row_start); + for (size_t i = 0; i < BYTES_PER_LINE; ++i) { - returnValue = 0; + if (row_start + i < size) + { + out += fmt::format("{:02x} ", data[row_start + i]); + } + else + { + out += " "; + } } - else + out += " "; + for (size_t i = 0; i < BYTES_PER_LINE; ++i) { - returnValue = -1; + if (row_start + i < size) + { + char c = static_cast(data[row_start + i]); + out += std::isprint(c, std::locale::classic()) ? c : '.'; + } } + out += "\n"; } - else - { - cemu_assert_suspicious(); - } - osLib_returnFromFunction(hCPU, returnValue); - } - - 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]); - } - cemuLog_logDebug(LogType::Force, "[{}] Data: {}", prefix, debugOutput); + cemuLog_logDebug(LogType::Force, "[{}] Data: \n{}", prefix, out); } void DoHIDTransferCallback(MPTR callbackFuncMPTR, MPTR callbackParamMPTR, uint32 hidHandle, uint32 errorCode, @@ -354,26 +344,152 @@ namespace nsyshid coreinitAsyncCallback_add(callbackFuncMPTR, 5, hidHandle, errorCode, buffer, length, callbackParamMPTR); } - void export_HIDSetIdle(PPCInterpreter_t* hCPU) + void _hidGetDescriptorAsync(std::shared_ptr device, uint8 descType, uint8 descIndex, uint16 lang, uint8* output, uint32 outputMaxLength, MPTR callbackFuncMPTR, MPTR callbackParamMPTR) { - ppcDefineParamU32(hidHandle, 0); // r3 - ppcDefineParamU32(ifIndex, 1); // r4 - ppcDefineParamU32(ukn, 2); // r5 - ppcDefineParamU32(duration, 3); // r6 - ppcDefineParamMPTR(callbackFuncMPTR, 4); // r7 - ppcDefineParamMPTR(callbackParamMPTR, 5); // r8 - cemuLog_logDebug(LogType::Force, "nsyshid.HIDSetIdle(...)"); - - // todo - if (callbackFuncMPTR) + if (device->GetDescriptor(descType, descIndex, lang, output, outputMaxLength)) { - DoHIDTransferCallback(callbackFuncMPTR, callbackParamMPTR, hidHandle, 0, MPTR_NULL, 0); + DoHIDTransferCallback(callbackFuncMPTR, + callbackParamMPTR, + device->m_hid->handle, + 0, + 0, + 0); } else { - cemu_assert_unimplemented(); + DoHIDTransferCallback(callbackFuncMPTR, + callbackParamMPTR, + device->m_hid->handle, + -1, + 0, + 0); + } + } + + void export_HIDGetDescriptor(PPCInterpreter_t* hCPU) + { + ppcDefineParamU32(hidHandle, 0); // r3 + ppcDefineParamU8(descType, 1); // r4 + ppcDefineParamU8(descIndex, 2); // r5 + ppcDefineParamU16(lang, 3); // r6 + ppcDefineParamUStr(output, 4); // r7 + ppcDefineParamU32(outputMaxLength, 5); // r8 + ppcDefineParamMPTR(cbFuncMPTR, 6); // r9 + ppcDefineParamMPTR(cbParamMPTR, 7); // r10 + cemuLog_logDebug(LogType::Force, "nsyshid.HIDGetDescriptor(0x{:08x}, 0x{:02x}, 0x{:02x}, 0x{:04x}, 0x{:x}, 0x{:08x}, 0x{:08x}, 0x{:08x})", + hCPU->gpr[3], hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->gpr[7], hCPU->gpr[8], hCPU->gpr[9], hCPU->gpr[10]); + + std::shared_ptr device = GetDeviceByHandle(hidHandle, true); + if (device == nullptr) + { + cemuLog_log(LogType::Force, "nsyshid.HIDGetDescriptor(): Unable to find device with hid handle {}", hidHandle); + osLib_returnFromFunction(hCPU, -1); + return; + } + + // issue request (synchronous or asynchronous) + sint32 returnCode = 0; + if (cbFuncMPTR == MPTR_NULL) + { + // synchronous + returnCode = -1; + if (device->GetDescriptor(descType, descIndex, lang, output, outputMaxLength)) + { + returnCode = outputMaxLength; + } + } + else + { + // asynchronous + std::thread(&_hidGetDescriptorAsync, device, descType, descIndex, lang, output, outputMaxLength, cbFuncMPTR, cbParamMPTR) + .detach(); + returnCode = 0; + } + osLib_returnFromFunction(hCPU, returnCode); + } + + void _hidSetIdleAsync(std::shared_ptr device, uint8 ifIndex, uint8 reportId, uint8 duration, MPTR callbackFuncMPTR, MPTR callbackParamMPTR) + { + if (device->SetIdle(ifIndex, reportId, duration)) + { + DoHIDTransferCallback(callbackFuncMPTR, + callbackParamMPTR, + device->m_hid->handle, + 0, + 0, + 0); + } + else + { + DoHIDTransferCallback(callbackFuncMPTR, + callbackParamMPTR, + device->m_hid->handle, + -1, + 0, + 0); + } + } + + void export_HIDSetIdle(PPCInterpreter_t* hCPU) + { + ppcDefineParamU32(hidHandle, 0); // r3 + ppcDefineParamU8(ifIndex, 1); // r4 + ppcDefineParamU8(reportId, 2); // r5 + ppcDefineParamU8(duration, 3); // r6 + ppcDefineParamMPTR(callbackFuncMPTR, 4); // r7 + ppcDefineParamMPTR(callbackParamMPTR, 5); // r8 + cemuLog_logDebug(LogType::Force, "nsyshid.HIDSetIdle(0x{:08x}, 0x{:02x}, 0x{:02x}, 0x{:02x}, 0x{:08x}, 0x{:08x})", hCPU->gpr[3], + hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->gpr[7], hCPU->gpr[8]); + + std::shared_ptr device = GetDeviceByHandle(hidHandle, true); + if (device == nullptr) + { + cemuLog_log(LogType::Force, "nsyshid.HIDSetIdle(): Unable to find device with hid handle {}", hidHandle); + osLib_returnFromFunction(hCPU, -1); + return; + } + + // issue request (synchronous or asynchronous) + sint32 returnCode = 0; + if (callbackFuncMPTR == MPTR_NULL) + { + // synchronous + returnCode = -1; + if (device->SetIdle(ifIndex, reportId, duration)) + { + returnCode = 0; + } + } + else + { + // asynchronous + std::thread(&_hidSetIdleAsync, device, ifIndex, reportId, duration, callbackFuncMPTR, callbackParamMPTR) + .detach(); + returnCode = 0; + } + osLib_returnFromFunction(hCPU, returnCode); + } + + void _hidSetProtocolAsync(std::shared_ptr device, uint8 ifIndex, uint8 protocol, MPTR callbackFuncMPTR, MPTR callbackParamMPTR) + { + if (device->SetProtocol(ifIndex, protocol)) + { + DoHIDTransferCallback(callbackFuncMPTR, + callbackParamMPTR, + device->m_hid->handle, + 0, + 0, + 0); + } + else + { + DoHIDTransferCallback(callbackFuncMPTR, + callbackParamMPTR, + device->m_hid->handle, + -1, + 0, + 0); } - osLib_returnFromFunction(hCPU, 0); // for non-async version, return number of bytes transferred } void export_HIDSetProtocol(PPCInterpreter_t* hCPU) @@ -383,51 +499,51 @@ namespace nsyshid ppcDefineParamU8(protocol, 2); // r5 ppcDefineParamMPTR(callbackFuncMPTR, 3); // r6 ppcDefineParamMPTR(callbackParamMPTR, 4); // r7 - cemuLog_logDebug(LogType::Force, "nsyshid.HIDSetProtocol(...)"); + cemuLog_logDebug(LogType::Force, "nsyshid.HIDSetProtocol(0x{:08x}, 0x{:02x}, 0x{:02x}, 0x{:08x}, 0x{:08x})", hCPU->gpr[3], + hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->gpr[7]); std::shared_ptr device = GetDeviceByHandle(hidHandle, true); - sint32 returnCode = -1; - if (device) + if (device == nullptr) { - if (!device->IsOpened()) + cemuLog_log(LogType::Force, "nsyshid.HIDSetProtocol(): Unable to find device with hid handle {}", hidHandle); + osLib_returnFromFunction(hCPU, -1); + return; + } + // issue request (synchronous or asynchronous) + sint32 returnCode = 0; + if (callbackFuncMPTR == MPTR_NULL) + { + // synchronous + returnCode = -1; + if (device->SetProtocol(ifIndex, protocol)) { - cemuLog_logDebug(LogType::Force, "nsyshid.HIDSetProtocol(): error: device is not opened"); - } - else - { - if (device->SetProtocol(ifIndex, protocol)) - { - returnCode = 0; - } + returnCode = 0; } } else { - cemu_assert_suspicious(); - } - - if (callbackFuncMPTR) - { - DoHIDTransferCallback(callbackFuncMPTR, callbackParamMPTR, hidHandle, 0, MPTR_NULL, 0); + // asynchronous + std::thread(&_hidSetProtocolAsync, device, ifIndex, protocol, callbackFuncMPTR, callbackParamMPTR) + .detach(); + returnCode = 0; } osLib_returnFromFunction(hCPU, returnCode); } // handler for async HIDSetReport transfers - void _hidSetReportAsync(std::shared_ptr device, uint8* reportData, sint32 length, - uint8* originalData, - sint32 originalLength, MPTR callbackFuncMPTR, MPTR callbackParamMPTR) + void _hidSetReportAsync(std::shared_ptr device, uint8 reportType, uint8 reportId, uint8* data, uint32 length, + MPTR callbackFuncMPTR, MPTR callbackParamMPTR) { cemuLog_logDebug(LogType::Force, "_hidSetReportAsync begin"); - ReportMessage message(reportData, length, originalData, originalLength); + ReportMessage message(reportType, reportId, data, length); if (device->SetReport(&message)) { DoHIDTransferCallback(callbackFuncMPTR, callbackParamMPTR, device->m_hid->handle, 0, - memory_getVirtualOffsetFromPointer(originalData), - originalLength); + memory_getVirtualOffsetFromPointer(data), + length); } else { @@ -435,24 +551,22 @@ namespace nsyshid callbackParamMPTR, device->m_hid->handle, -1, - memory_getVirtualOffsetFromPointer(originalData), - 0); + memory_getVirtualOffsetFromPointer(data), + length); } - free(reportData); } // handler for synchronous HIDSetReport transfers - sint32 _hidSetReportSync(std::shared_ptr device, uint8* reportData, sint32 length, - uint8* originalData, sint32 originalLength, coreinit::OSEvent* event) + sint32 _hidSetReportSync(std::shared_ptr device, uint8 reportType, uint8 reportId, + uint8* data, uint32 length, coreinit::OSEvent* event) { - _debugPrintHex("_hidSetReportSync Begin", reportData, length); + _debugPrintHex("_hidSetReportSync Begin", data, length); sint32 returnCode = 0; - ReportMessage message(reportData, length, originalData, originalLength); + ReportMessage message(reportType, reportId, data, length); if (device->SetReport(&message)) { - returnCode = originalLength; + returnCode = length; } - free(reportData); cemuLog_logDebug(LogType::Force, "_hidSetReportSync end. returnCode: {}", returnCode); coreinit::OSSignalEvent(event); return returnCode; @@ -461,19 +575,19 @@ namespace nsyshid void export_HIDSetReport(PPCInterpreter_t* hCPU) { ppcDefineParamU32(hidHandle, 0); // r3 - ppcDefineParamU32(reportRelatedUkn, 1); // r4 - ppcDefineParamU32(reportId, 2); // r5 + ppcDefineParamU8(reportType, 1); // r4 + ppcDefineParamU8(reportId, 2); // r5 ppcDefineParamUStr(data, 3); // r6 ppcDefineParamU32(dataLength, 4); // r7 ppcDefineParamMPTR(callbackFuncMPTR, 5); // r8 ppcDefineParamMPTR(callbackParamMPTR, 6); // r9 - cemuLog_logDebug(LogType::Force, "nsyshid.HIDSetReport({},0x{:02x},0x{:02x},...)", hidHandle, reportRelatedUkn, - reportId); + cemuLog_logDebug(LogType::Force, "nsyshid.HIDSetReport(0x{:08x}, 0x{:02x}, 0x{:02x}, 0x{:08x}, 0x{:08x}, 0x{:08x}, 0x{:08x})", hCPU->gpr[3], + hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->gpr[7], hCPU->gpr[8], hCPU->gpr[9]); _debugPrintHex("HIDSetReport", data, dataLength); #ifdef CEMU_DEBUG_ASSERT - if (reportRelatedUkn != 2 || reportId != 0) + if (reportType != 2 || reportId != 0) assert_dbg(); #endif @@ -485,15 +599,6 @@ namespace nsyshid return; } - // prepare report data - // note: Currently we need to pad the data to 0x20 bytes for it to work (plus one extra byte for HidD_SetOutputReport) - // Does IOSU pad data to 0x20 byte? Also check if this is specific to Skylanders portal - sint32 paddedLength = (dataLength + 0x1F) & ~0x1F; - uint8* reportData = (uint8*)malloc(paddedLength + 1); - memset(reportData, 0, paddedLength + 1); - reportData[0] = 0; - memcpy(reportData + 1, data, dataLength); - // issue request (synchronous or asynchronous) sint32 returnCode = 0; if (callbackFuncMPTR == MPTR_NULL) @@ -501,15 +606,14 @@ namespace nsyshid // synchronous StackAllocator event; coreinit::OSInitEvent(&event, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_AUTO); - std::future res = std::async(std::launch::async, &_hidSetReportSync, device, reportData, - paddedLength + 1, data, dataLength, &event); + std::future res = std::async(std::launch::async, &_hidSetReportSync, device, reportType, reportId, data, dataLength, &event); coreinit::OSWaitEvent(&event); returnCode = res.get(); } else { // asynchronous - std::thread(&_hidSetReportAsync, device, reportData, paddedLength + 1, data, dataLength, + std::thread(&_hidSetReportAsync, device, reportType, reportId, data, dataLength, callbackFuncMPTR, callbackParamMPTR) .detach(); returnCode = 0; @@ -586,7 +690,7 @@ namespace nsyshid ppcDefineParamMPTR(callbackFuncMPTR, 3); // r6 ppcDefineParamMPTR(callbackParamMPTR, 4); // r7 cemuLog_logDebug(LogType::Force, "nsyshid.HIDRead(0x{:x},0x{:08x},0x{:08x},0x{:08x},0x{:08x})", hCPU->gpr[3], - hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->gpr[7]); + hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->gpr[7]); std::shared_ptr device = GetDeviceByHandle(hidHandle, true); if (device == nullptr) @@ -683,7 +787,7 @@ namespace nsyshid ppcDefineParamMPTR(callbackFuncMPTR, 3); // r6 ppcDefineParamMPTR(callbackParamMPTR, 4); // r7 cemuLog_logDebug(LogType::Force, "nsyshid.HIDWrite(0x{:x},0x{:08x},0x{:08x},0x{:08x},0x{:08x})", hCPU->gpr[3], - hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->gpr[7]); + hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->gpr[7]); std::shared_ptr device = GetDeviceByHandle(hidHandle, true); if (device == nullptr) @@ -718,7 +822,7 @@ namespace nsyshid ppcDefineParamTypePtr(ukn0, uint32be, 1); ppcDefineParamTypePtr(ukn1, uint32be, 2); cemuLog_logDebug(LogType::Force, "nsyshid.HIDDecodeError(0x{:08x},0x{:08x},0x{:08x})", hCPU->gpr[3], - hCPU->gpr[4], hCPU->gpr[5]); + hCPU->gpr[4], hCPU->gpr[5]); // todo *ukn0 = 0x3FF;