mirror of https://github.com/cemu-project/Cemu.git
Libusb Windows
This commit is contained in:
parent
f61539a262
commit
22170b0907
|
@ -124,23 +124,6 @@ if (WIN32)
|
||||||
endif()
|
endif()
|
||||||
option(ENABLE_CUBEB "Enabled cubeb backend" ON)
|
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)
|
option(ENABLE_WXWIDGETS "Build with wxWidgets UI (Currently required)" ON)
|
||||||
|
|
||||||
set(THREADS_PREFER_PTHREAD_FLAG true)
|
set(THREADS_PREFER_PTHREAD_FLAG true)
|
||||||
|
|
|
@ -463,8 +463,6 @@ add_library(CemuCafe
|
||||||
OS/libs/nsyshid/BackendEmulated.h
|
OS/libs/nsyshid/BackendEmulated.h
|
||||||
OS/libs/nsyshid/BackendLibusb.cpp
|
OS/libs/nsyshid/BackendLibusb.cpp
|
||||||
OS/libs/nsyshid/BackendLibusb.h
|
OS/libs/nsyshid/BackendLibusb.h
|
||||||
OS/libs/nsyshid/BackendWindowsHID.cpp
|
|
||||||
OS/libs/nsyshid/BackendWindowsHID.h
|
|
||||||
OS/libs/nsyshid/Dimensions.cpp
|
OS/libs/nsyshid/Dimensions.cpp
|
||||||
OS/libs/nsyshid/Dimensions.h
|
OS/libs/nsyshid/Dimensions.h
|
||||||
OS/libs/nsyshid/Infinity.cpp
|
OS/libs/nsyshid/Infinity.cpp
|
||||||
|
@ -569,15 +567,16 @@ if (ENABLE_WAYLAND)
|
||||||
target_link_libraries(CemuCafe PUBLIC Wayland::Client)
|
target_link_libraries(CemuCafe PUBLIC Wayland::Client)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ENABLE_NSYSHID_LIBUSB)
|
if (ENABLE_VCPKG)
|
||||||
if (ENABLE_VCPKG)
|
if(WIN32)
|
||||||
find_package(PkgConfig REQUIRED)
|
set(PKG_CONFIG_EXECUTABLE "${VCPKG_INSTALLED_DIR}/x64-windows/tools/pkgconf/pkgconf.exe")
|
||||||
pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0)
|
endif()
|
||||||
target_link_libraries(CemuCafe PRIVATE PkgConfig::libusb)
|
find_package(PkgConfig REQUIRED)
|
||||||
else ()
|
pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0)
|
||||||
find_package(libusb MODULE REQUIRED)
|
target_link_libraries(CemuCafe PRIVATE PkgConfig::libusb)
|
||||||
target_link_libraries(CemuCafe PRIVATE libusb::libusb)
|
else ()
|
||||||
endif ()
|
find_package(libusb MODULE REQUIRED)
|
||||||
|
target_link_libraries(CemuCafe PRIVATE libusb::libusb)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (ENABLE_WXWIDGETS)
|
if (ENABLE_WXWIDGETS)
|
||||||
|
|
|
@ -1,24 +1,12 @@
|
||||||
#include "nsyshid.h"
|
#include "nsyshid.h"
|
||||||
#include "Backend.h"
|
#include "Backend.h"
|
||||||
#include "BackendEmulated.h"
|
#include "BackendEmulated.h"
|
||||||
|
|
||||||
#if NSYSHID_ENABLE_BACKEND_LIBUSB
|
|
||||||
|
|
||||||
#include "BackendLibusb.h"
|
#include "BackendLibusb.h"
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if NSYSHID_ENABLE_BACKEND_WINDOWS_HID
|
|
||||||
|
|
||||||
#include "BackendWindowsHID.h"
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace nsyshid::backend
|
namespace nsyshid::backend
|
||||||
{
|
{
|
||||||
void AttachDefaultBackends()
|
void AttachDefaultBackends()
|
||||||
{
|
{
|
||||||
#if NSYSHID_ENABLE_BACKEND_LIBUSB
|
|
||||||
// add libusb backend
|
// add libusb backend
|
||||||
{
|
{
|
||||||
auto backendLibusb = std::make_shared<backend::libusb::BackendLibusb>();
|
auto backendLibusb = std::make_shared<backend::libusb::BackendLibusb>();
|
||||||
|
@ -27,17 +15,6 @@ namespace nsyshid::backend
|
||||||
AttachBackend(backendLibusb);
|
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
|
|
||||||
// add emulated backend
|
// add emulated backend
|
||||||
{
|
{
|
||||||
auto backendEmulated = std::make_shared<backend::emulated::BackendEmulated>();
|
auto backendEmulated = std::make_shared<backend::emulated::BackendEmulated>();
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#ifndef CEMU_NSYSHID_BACKEND_H
|
#pragma once
|
||||||
#define CEMU_NSYSHID_BACKEND_H
|
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -26,9 +25,9 @@ namespace nsyshid
|
||||||
struct TransferCommand
|
struct TransferCommand
|
||||||
{
|
{
|
||||||
uint8* data;
|
uint8* data;
|
||||||
sint32 length;
|
uint32 length;
|
||||||
|
|
||||||
TransferCommand(uint8* data, sint32 length)
|
TransferCommand(uint8* data, uint32 length)
|
||||||
: data(data), length(length)
|
: data(data), length(length)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -39,7 +38,7 @@ namespace nsyshid
|
||||||
{
|
{
|
||||||
sint32 bytesRead;
|
sint32 bytesRead;
|
||||||
|
|
||||||
ReadMessage(uint8* data, sint32 length, sint32 bytesRead)
|
ReadMessage(uint8* data, uint32 length, sint32 bytesRead)
|
||||||
: bytesRead(bytesRead), TransferCommand(data, length)
|
: bytesRead(bytesRead), TransferCommand(data, length)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -50,7 +49,7 @@ namespace nsyshid
|
||||||
{
|
{
|
||||||
sint32 bytesWritten;
|
sint32 bytesWritten;
|
||||||
|
|
||||||
WriteMessage(uint8* data, sint32 length, sint32 bytesWritten)
|
WriteMessage(uint8* data, uint32 length, sint32 bytesWritten)
|
||||||
: bytesWritten(bytesWritten), TransferCommand(data, length)
|
: bytesWritten(bytesWritten), TransferCommand(data, length)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -59,14 +58,11 @@ namespace nsyshid
|
||||||
|
|
||||||
struct ReportMessage final : TransferCommand
|
struct ReportMessage final : TransferCommand
|
||||||
{
|
{
|
||||||
uint8* reportData;
|
uint8 reportType;
|
||||||
sint32 length;
|
uint8 reportId;
|
||||||
uint8* originalData;
|
|
||||||
sint32 originalLength;
|
|
||||||
|
|
||||||
ReportMessage(uint8* reportData, sint32 length, uint8* originalData, sint32 originalLength)
|
ReportMessage(uint8 reportType, uint8 reportId, uint8* data, uint32 length)
|
||||||
: reportData(reportData), length(length), originalData(originalData),
|
: reportType(reportType), reportId(reportId), TransferCommand(data, length)
|
||||||
originalLength(originalLength), TransferCommand(reportData, length)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
using TransferCommand::TransferCommand;
|
using TransferCommand::TransferCommand;
|
||||||
|
@ -77,7 +73,8 @@ namespace nsyshid
|
||||||
static_assert(offsetof(HID_t, ifIndex) == 0xC, "");
|
static_assert(offsetof(HID_t, ifIndex) == 0xC, "");
|
||||||
static_assert(offsetof(HID_t, protocol) == 0xE, "");
|
static_assert(offsetof(HID_t, protocol) == 0xE, "");
|
||||||
|
|
||||||
class Device {
|
class Device
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
Device() = delete;
|
Device() = delete;
|
||||||
|
|
||||||
|
@ -131,16 +128,21 @@ namespace nsyshid
|
||||||
|
|
||||||
virtual bool GetDescriptor(uint8 descType,
|
virtual bool GetDescriptor(uint8 descType,
|
||||||
uint8 descIndex,
|
uint8 descIndex,
|
||||||
uint8 lang,
|
uint16 lang,
|
||||||
uint8* output,
|
uint8* output,
|
||||||
uint32 outputMaxLength) = 0;
|
uint32 outputMaxLength) = 0;
|
||||||
|
|
||||||
|
virtual bool SetIdle(uint8 ifIndex,
|
||||||
|
uint8 reportId,
|
||||||
|
uint8 duration) = 0;
|
||||||
|
|
||||||
virtual bool SetProtocol(uint8 ifIndex, uint8 protocol) = 0;
|
virtual bool SetProtocol(uint8 ifIndex, uint8 protocol) = 0;
|
||||||
|
|
||||||
virtual bool SetReport(ReportMessage* message) = 0;
|
virtual bool SetReport(ReportMessage* message) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Backend {
|
class Backend
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
Backend();
|
Backend();
|
||||||
|
|
||||||
|
@ -188,5 +190,3 @@ namespace nsyshid
|
||||||
void AttachDefaultBackends();
|
void AttachDefaultBackends();
|
||||||
}
|
}
|
||||||
} // namespace nsyshid
|
} // namespace nsyshid
|
||||||
|
|
||||||
#endif // CEMU_NSYSHID_BACKEND_H
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include "BackendLibusb.h"
|
#include "BackendLibusb.h"
|
||||||
|
|
||||||
#if NSYSHID_ENABLE_BACKEND_LIBUSB
|
|
||||||
|
|
||||||
namespace nsyshid::backend::libusb
|
namespace nsyshid::backend::libusb
|
||||||
{
|
{
|
||||||
BackendLibusb::BackendLibusb()
|
BackendLibusb::BackendLibusb()
|
||||||
|
@ -15,8 +13,8 @@ namespace nsyshid::backend::libusb
|
||||||
if (m_initReturnCode < 0)
|
if (m_initReturnCode < 0)
|
||||||
{
|
{
|
||||||
m_ctx = nullptr;
|
m_ctx = nullptr;
|
||||||
cemuLog_logDebug(LogType::Force, "nsyshid::BackendLibusb: failed to initialize libusb, return code: {}",
|
cemuLog_log(LogType::Force, "nsyshid::BackendLibusb: failed to initialize libusb, return code: {}",
|
||||||
m_initReturnCode);
|
m_initReturnCode);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,13 +32,13 @@ namespace nsyshid::backend::libusb
|
||||||
&m_hotplugCallbackHandle);
|
&m_hotplugCallbackHandle);
|
||||||
if (ret != LIBUSB_SUCCESS)
|
if (ret != LIBUSB_SUCCESS)
|
||||||
{
|
{
|
||||||
cemuLog_logDebug(LogType::Force,
|
cemuLog_log(LogType::Force,
|
||||||
"nsyshid::BackendLibusb: failed to register hotplug callback with return code {}",
|
"nsyshid::BackendLibusb: failed to register hotplug callback with return code {}",
|
||||||
ret);
|
ret);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cemuLog_logDebug(LogType::Force, "nsyshid::BackendLibusb: registered hotplug callback");
|
cemuLog_log(LogType::Force, "nsyshid::BackendLibusb: registered hotplug callback");
|
||||||
m_callbackRegistered = true;
|
m_callbackRegistered = true;
|
||||||
m_hotplugThread = std::thread([this] {
|
m_hotplugThread = std::thread([this] {
|
||||||
while (!m_hotplugThreadStop)
|
while (!m_hotplugThreadStop)
|
||||||
|
@ -52,9 +50,9 @@ namespace nsyshid::backend::libusb
|
||||||
int ret = libusb_handle_events_timeout_completed(m_ctx, &timeout, nullptr);
|
int ret = libusb_handle_events_timeout_completed(m_ctx, &timeout, nullptr);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
{
|
{
|
||||||
cemuLog_logDebug(LogType::Force,
|
cemuLog_log(LogType::Force,
|
||||||
"nsyshid::BackendLibusb: hotplug thread: error handling events: {}",
|
"nsyshid::BackendLibusb: hotplug thread: error handling events: {}",
|
||||||
ret);
|
ret);
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,7 +61,7 @@ namespace nsyshid::backend::libusb
|
||||||
}
|
}
|
||||||
else
|
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);
|
int ret = libusb_get_device_descriptor(dev, &desc);
|
||||||
if (ret < 0)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,9 +136,9 @@ namespace nsyshid::backend::libusb
|
||||||
{
|
{
|
||||||
case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED:
|
case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED:
|
||||||
{
|
{
|
||||||
cemuLog_logDebug(LogType::Force, "nsyshid::BackendLibusb::OnHotplug(): device arrived: {:04x}:{:04x}",
|
cemuLog_log(LogType::Force, "nsyshid::BackendLibusb::OnHotplug(): device arrived: {:04x}:{:04x}",
|
||||||
desc.idVendor,
|
desc.idVendor,
|
||||||
desc.idProduct);
|
desc.idProduct);
|
||||||
auto device = CheckAndCreateDevice(dev);
|
auto device = CheckAndCreateDevice(dev);
|
||||||
if (device != nullptr)
|
if (device != nullptr)
|
||||||
{
|
{
|
||||||
|
@ -166,9 +164,9 @@ namespace nsyshid::backend::libusb
|
||||||
break;
|
break;
|
||||||
case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT:
|
case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT:
|
||||||
{
|
{
|
||||||
cemuLog_logDebug(LogType::Force, "nsyshid::BackendLibusb::OnHotplug(): device left: {:04x}:{:04x}",
|
cemuLog_log(LogType::Force, "nsyshid::BackendLibusb::OnHotplug(): device left: {:04x}:{:04x}",
|
||||||
desc.idVendor,
|
desc.idVendor,
|
||||||
desc.idProduct);
|
desc.idProduct);
|
||||||
auto device = FindLibusbDevice(dev);
|
auto device = FindLibusbDevice(dev);
|
||||||
if (device != nullptr)
|
if (device != nullptr)
|
||||||
{
|
{
|
||||||
|
@ -203,8 +201,8 @@ namespace nsyshid::backend::libusb
|
||||||
int ret = libusb_get_device_descriptor(dev, &desc);
|
int ret = libusb_get_device_descriptor(dev, &desc);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
cemuLog_logDebug(LogType::Force,
|
cemuLog_log(LogType::Force,
|
||||||
"nsyshid::BackendLibusb::FindLibusbDevice(): failed to get device descriptor");
|
"nsyshid::BackendLibusb::FindLibusbDevice(): failed to get device descriptor");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
uint8 busNumber = libusb_get_bus_number(dev);
|
uint8 busNumber = libusb_get_bus_number(dev);
|
||||||
|
@ -268,8 +266,8 @@ namespace nsyshid::backend::libusb
|
||||||
}
|
}
|
||||||
if (desc.idVendor == 0x0e6f && desc.idProduct == 0x0241)
|
if (desc.idVendor == 0x0e6f && desc.idProduct == 0x0241)
|
||||||
{
|
{
|
||||||
cemuLog_logDebug(LogType::Force,
|
cemuLog_log(LogType::Force,
|
||||||
"nsyshid::BackendLibusb::CheckAndCreateDevice(): lego dimensions portal detected");
|
"nsyshid::BackendLibusb::CheckAndCreateDevice(): lego dimensions portal detected");
|
||||||
}
|
}
|
||||||
auto device = std::make_shared<DeviceLibusb>(m_ctx,
|
auto device = std::make_shared<DeviceLibusb>(m_ctx,
|
||||||
desc.idVendor,
|
desc.idVendor,
|
||||||
|
@ -408,6 +406,7 @@ namespace nsyshid::backend::libusb
|
||||||
}
|
}
|
||||||
libusb_device* dev;
|
libusb_device* dev;
|
||||||
libusb_device* found = nullptr;
|
libusb_device* found = nullptr;
|
||||||
|
uint8 numConfigs = 0;
|
||||||
for (int i = 0; (dev = devices[i]) != nullptr; i++)
|
for (int i = 0; (dev = devices[i]) != nullptr; i++)
|
||||||
{
|
{
|
||||||
struct libusb_device_descriptor desc;
|
struct libusb_device_descriptor desc;
|
||||||
|
@ -427,6 +426,7 @@ namespace nsyshid::backend::libusb
|
||||||
{
|
{
|
||||||
// we found our device!
|
// we found our device!
|
||||||
found = dev;
|
found = dev;
|
||||||
|
numConfigs = desc.bNumConfigurations;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -446,12 +446,13 @@ namespace nsyshid::backend::libusb
|
||||||
}
|
}
|
||||||
this->m_handleInUseCounter = 0;
|
this->m_handleInUseCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ret = ClaimAllInterfaces(0);
|
||||||
|
|
||||||
|
if (ret != 0)
|
||||||
{
|
{
|
||||||
int ret = ClaimAllInterfaces(0);
|
cemuLog_log(LogType::Force, "nsyshid::DeviceLibusb::open(): cannot claim interface for config 0");
|
||||||
if (ret != 0)
|
return false;
|
||||||
{
|
|
||||||
cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::open(): cannot claim interface");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -475,7 +476,7 @@ namespace nsyshid::backend::libusb
|
||||||
{
|
{
|
||||||
m_handleInUseCounterDecremented.wait(lock);
|
m_handleInUseCounterDecremented.wait(lock);
|
||||||
}
|
}
|
||||||
libusb_release_interface(handle, 0);
|
ReleaseAllInterfacesForCurrentConfig();
|
||||||
libusb_close(handle);
|
libusb_close(handle);
|
||||||
m_handleInUseCounter = -1;
|
m_handleInUseCounter = -1;
|
||||||
m_handleInUseCounterDecremented.notify_all();
|
m_handleInUseCounterDecremented.notify_all();
|
||||||
|
@ -492,22 +493,27 @@ namespace nsyshid::backend::libusb
|
||||||
auto handleLock = AquireHandleLock();
|
auto handleLock = AquireHandleLock();
|
||||||
if (!handleLock->IsValid())
|
if (!handleLock->IsValid())
|
||||||
{
|
{
|
||||||
cemuLog_logDebug(LogType::Force,
|
cemuLog_log(LogType::Force,
|
||||||
"nsyshid::DeviceLibusb::read(): cannot read from a non-opened device\n");
|
"nsyshid::DeviceLibusb::read(): cannot read from a non-opened device\n");
|
||||||
return ReadResult::Error;
|
return ReadResult::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < m_config_descriptors.size(); i++)
|
||||||
|
{
|
||||||
|
ClaimAllInterfaces(i);
|
||||||
|
}
|
||||||
|
|
||||||
const unsigned int timeout = 50;
|
const unsigned int timeout = 50;
|
||||||
int actualLength = 0;
|
int actualLength = 0;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
ret = libusb_bulk_transfer(handleLock->GetHandle(),
|
ret = libusb_interrupt_transfer(handleLock->GetHandle(),
|
||||||
this->m_libusbEndpointIn,
|
this->m_libusbEndpointIn,
|
||||||
message->data,
|
message->data,
|
||||||
message->length,
|
message->length,
|
||||||
&actualLength,
|
&actualLength,
|
||||||
timeout);
|
timeout);
|
||||||
}
|
}
|
||||||
while (ret == LIBUSB_ERROR_TIMEOUT && actualLength == 0 && IsOpened());
|
while (ret == LIBUSB_ERROR_TIMEOUT && actualLength == 0 && IsOpened());
|
||||||
|
|
||||||
|
@ -520,9 +526,9 @@ namespace nsyshid::backend::libusb
|
||||||
message->bytesRead = actualLength;
|
message->bytesRead = actualLength;
|
||||||
return ReadResult::Success;
|
return ReadResult::Success;
|
||||||
}
|
}
|
||||||
cemuLog_logDebug(LogType::Force,
|
cemuLog_log(LogType::Force,
|
||||||
"nsyshid::DeviceLibusb::read(): failed with error code: {}",
|
"nsyshid::DeviceLibusb::read(): failed at endpoint 0x{:02x} with error message: {}", this->m_libusbEndpointIn,
|
||||||
ret);
|
libusb_error_name(ret));
|
||||||
return ReadResult::Error;
|
return ReadResult::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,19 +537,24 @@ namespace nsyshid::backend::libusb
|
||||||
auto handleLock = AquireHandleLock();
|
auto handleLock = AquireHandleLock();
|
||||||
if (!handleLock->IsValid())
|
if (!handleLock->IsValid())
|
||||||
{
|
{
|
||||||
cemuLog_logDebug(LogType::Force,
|
cemuLog_log(LogType::Force,
|
||||||
"nsyshid::DeviceLibusb::write(): cannot write to a non-opened device\n");
|
"nsyshid::DeviceLibusb::write(): cannot write to a non-opened device\n");
|
||||||
return WriteResult::Error;
|
return WriteResult::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < m_config_descriptors.size(); i++)
|
||||||
|
{
|
||||||
|
ClaimAllInterfaces(i);
|
||||||
|
}
|
||||||
|
|
||||||
message->bytesWritten = 0;
|
message->bytesWritten = 0;
|
||||||
int actualLength = 0;
|
int actualLength = 0;
|
||||||
int ret = libusb_bulk_transfer(handleLock->GetHandle(),
|
int ret = libusb_interrupt_transfer(handleLock->GetHandle(),
|
||||||
this->m_libusbEndpointOut,
|
this->m_libusbEndpointOut,
|
||||||
message->data,
|
message->data,
|
||||||
message->length,
|
message->length,
|
||||||
&actualLength,
|
&actualLength,
|
||||||
0);
|
0);
|
||||||
|
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
{
|
{
|
||||||
|
@ -555,22 +566,22 @@ namespace nsyshid::backend::libusb
|
||||||
message->length);
|
message->length);
|
||||||
return WriteResult::Success;
|
return WriteResult::Success;
|
||||||
}
|
}
|
||||||
cemuLog_logDebug(LogType::Force,
|
cemuLog_log(LogType::Force,
|
||||||
"nsyshid::DeviceLibusb::write(): failed with error code: {}",
|
"nsyshid::DeviceLibusb::write(): failed with error code: {}",
|
||||||
ret);
|
ret);
|
||||||
return WriteResult::Error;
|
return WriteResult::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeviceLibusb::GetDescriptor(uint8 descType,
|
bool DeviceLibusb::GetDescriptor(uint8 descType,
|
||||||
uint8 descIndex,
|
uint8 descIndex,
|
||||||
uint8 lang,
|
uint16 lang,
|
||||||
uint8* output,
|
uint8* output,
|
||||||
uint32 outputMaxLength)
|
uint32 outputMaxLength)
|
||||||
{
|
{
|
||||||
auto handleLock = AquireHandleLock();
|
auto handleLock = AquireHandleLock();
|
||||||
if (!handleLock->IsValid())
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -579,7 +590,6 @@ namespace nsyshid::backend::libusb
|
||||||
struct libusb_config_descriptor* conf = nullptr;
|
struct libusb_config_descriptor* conf = nullptr;
|
||||||
libusb_device* dev = libusb_get_device(handleLock->GetHandle());
|
libusb_device* dev = libusb_get_device(handleLock->GetHandle());
|
||||||
int ret = libusb_get_active_config_descriptor(dev, &conf);
|
int ret = libusb_get_active_config_descriptor(dev, &conf);
|
||||||
|
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
{
|
{
|
||||||
std::vector<uint8> configurationDescriptor(conf->wTotalLength);
|
std::vector<uint8> configurationDescriptor(conf->wTotalLength);
|
||||||
|
@ -656,7 +666,6 @@ namespace nsyshid::backend::libusb
|
||||||
extraReadPointer += bLength;
|
extraReadPointer += bLength;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int endpointIndex = 0; endpointIndex < altsetting.bNumEndpoints; endpointIndex++)
|
for (int endpointIndex = 0; endpointIndex < altsetting.bNumEndpoints; endpointIndex++)
|
||||||
{
|
{
|
||||||
// endpoint descriptor
|
// endpoint descriptor
|
||||||
|
@ -681,24 +690,63 @@ namespace nsyshid::backend::libusb
|
||||||
uint32 bytesWritten = currentWritePtr - &configurationDescriptor[0];
|
uint32 bytesWritten = currentWritePtr - &configurationDescriptor[0];
|
||||||
libusb_free_config_descriptor(conf);
|
libusb_free_config_descriptor(conf);
|
||||||
cemu_assert_debug(bytesWritten <= conf->wTotalLength);
|
cemu_assert_debug(bytesWritten <= conf->wTotalLength);
|
||||||
|
|
||||||
memcpy(output, &configurationDescriptor[0],
|
memcpy(output, &configurationDescriptor[0],
|
||||||
std::min<uint32>(outputMaxLength, bytesWritten));
|
std::min<uint32>(outputMaxLength, bytesWritten));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
cemuLog_logDebug(LogType::Force,
|
|
||||||
"nsyshid::DeviceLibusb::getDescriptor(): failed to get config descriptor with error code: {}",
|
|
||||||
ret);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
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<uint8, 0> 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<typename Configs, typename Function>
|
template<typename Configs, typename Function>
|
||||||
|
@ -764,21 +812,27 @@ namespace nsyshid::backend::libusb
|
||||||
auto handleLock = AquireHandleLock();
|
auto handleLock = AquireHandleLock();
|
||||||
if (!handleLock->IsValid())
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
if (m_interfaceIndex != ifIndex)
|
|
||||||
m_interfaceIndex = ifIndex;
|
|
||||||
|
|
||||||
ReleaseAllInterfacesForCurrentConfig();
|
std::array<uint8, 0> emptyData = {};
|
||||||
int ret = libusb_set_configuration(AquireHandleLock()->GetHandle(), protocol);
|
|
||||||
if (ret == LIBUSB_SUCCESS)
|
|
||||||
ret = ClaimAllInterfaces(protocol);
|
|
||||||
|
|
||||||
if (ret == LIBUSB_SUCCESS)
|
int ret = libusb_control_transfer(handleLock->GetHandle(),
|
||||||
return true;
|
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)
|
bool DeviceLibusb::SetReport(ReportMessage* message)
|
||||||
|
@ -786,22 +840,24 @@ namespace nsyshid::backend::libusb
|
||||||
auto handleLock = AquireHandleLock();
|
auto handleLock = AquireHandleLock();
|
||||||
if (!handleLock->IsValid())
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16 wValue = uint16(message->reportType) << 8 | uint16(message->reportId);
|
||||||
|
|
||||||
int ret = libusb_control_transfer(handleLock->GetHandle(),
|
int ret = libusb_control_transfer(handleLock->GetHandle(),
|
||||||
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT,
|
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT,
|
||||||
LIBUSB_REQUEST_SET_CONFIGURATION,
|
HID_CLASS_SET_REPORT, // Defined in HID Class Specific Requests (7.2)
|
||||||
512,
|
wValue,
|
||||||
0,
|
m_interfaceIndex,
|
||||||
message->originalData,
|
message->data,
|
||||||
message->originalLength,
|
uint16(message->length & 0xFFFF),
|
||||||
0);
|
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 false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -854,5 +910,3 @@ namespace nsyshid::backend::libusb
|
||||||
return m_handle;
|
return m_handle;
|
||||||
}
|
}
|
||||||
} // namespace nsyshid::backend::libusb
|
} // namespace nsyshid::backend::libusb
|
||||||
|
|
||||||
#endif // NSYSHID_ENABLE_BACKEND_LIBUSB
|
|
||||||
|
|
|
@ -1,15 +1,20 @@
|
||||||
#ifndef CEMU_NSYSHID_BACKEND_LIBUSB_H
|
|
||||||
#define CEMU_NSYSHID_BACKEND_LIBUSB_H
|
|
||||||
|
|
||||||
#include "nsyshid.h"
|
#include "nsyshid.h"
|
||||||
|
|
||||||
#if NSYSHID_ENABLE_BACKEND_LIBUSB
|
|
||||||
|
|
||||||
#include <libusb-1.0/libusb.h>
|
#include <libusb-1.0/libusb.h>
|
||||||
#include "Backend.h"
|
#include "Backend.h"
|
||||||
|
|
||||||
namespace nsyshid::backend::libusb
|
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 {
|
class BackendLibusb : public nsyshid::Backend {
|
||||||
public:
|
public:
|
||||||
BackendLibusb();
|
BackendLibusb();
|
||||||
|
@ -75,10 +80,14 @@ namespace nsyshid::backend::libusb
|
||||||
|
|
||||||
bool GetDescriptor(uint8 descType,
|
bool GetDescriptor(uint8 descType,
|
||||||
uint8 descIndex,
|
uint8 descIndex,
|
||||||
uint8 lang,
|
uint16 lang,
|
||||||
uint8* output,
|
uint8* output,
|
||||||
uint32 outputMaxLength) override;
|
uint32 outputMaxLength) override;
|
||||||
|
|
||||||
|
bool SetIdle(uint8 ifIndex,
|
||||||
|
uint8 reportId,
|
||||||
|
uint8 duration) override;
|
||||||
|
|
||||||
bool SetProtocol(uint8 ifIndex, uint8 protocol) override;
|
bool SetProtocol(uint8 ifIndex, uint8 protocol) override;
|
||||||
|
|
||||||
int ClaimAllInterfaces(uint8 config_num);
|
int ClaimAllInterfaces(uint8 config_num);
|
||||||
|
@ -134,7 +143,3 @@ namespace nsyshid::backend::libusb
|
||||||
std::unique_ptr<HandleLock> AquireHandleLock();
|
std::unique_ptr<HandleLock> AquireHandleLock();
|
||||||
};
|
};
|
||||||
} // namespace nsyshid::backend::libusb
|
} // namespace nsyshid::backend::libusb
|
||||||
|
|
||||||
#endif // NSYSHID_ENABLE_BACKEND_LIBUSB
|
|
||||||
|
|
||||||
#endif // CEMU_NSYSHID_BACKEND_LIBUSB_H
|
|
||||||
|
|
|
@ -1,444 +0,0 @@
|
||||||
#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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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_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<uint32>(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
|
|
|
@ -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<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(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
|
|
|
@ -426,7 +426,7 @@ namespace nsyshid
|
||||||
|
|
||||||
bool DimensionsToypadDevice::GetDescriptor(uint8 descType,
|
bool DimensionsToypadDevice::GetDescriptor(uint8 descType,
|
||||||
uint8 descIndex,
|
uint8 descIndex,
|
||||||
uint8 lang,
|
uint16 lang,
|
||||||
uint8* output,
|
uint8* output,
|
||||||
uint32 outputMaxLength)
|
uint32 outputMaxLength)
|
||||||
{
|
{
|
||||||
|
@ -489,6 +489,13 @@ namespace nsyshid
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DimensionsToypadDevice::SetIdle(uint8 ifIndex,
|
||||||
|
uint8 reportId,
|
||||||
|
uint8 duration)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool DimensionsToypadDevice::SetProtocol(uint8 ifIndex, uint8 protocol)
|
bool DimensionsToypadDevice::SetProtocol(uint8 ifIndex, uint8 protocol)
|
||||||
{
|
{
|
||||||
cemuLog_log(LogType::Force, "Toypad Protocol");
|
cemuLog_log(LogType::Force, "Toypad Protocol");
|
||||||
|
|
|
@ -25,10 +25,14 @@ namespace nsyshid
|
||||||
|
|
||||||
bool GetDescriptor(uint8 descType,
|
bool GetDescriptor(uint8 descType,
|
||||||
uint8 descIndex,
|
uint8 descIndex,
|
||||||
uint8 lang,
|
uint16 lang,
|
||||||
uint8* output,
|
uint8* output,
|
||||||
uint32 outputMaxLength) override;
|
uint32 outputMaxLength) override;
|
||||||
|
|
||||||
|
bool SetIdle(uint8 ifIndex,
|
||||||
|
uint8 reportId,
|
||||||
|
uint8 duration) override;
|
||||||
|
|
||||||
bool SetProtocol(uint8 ifIndex, uint8 protocol) override;
|
bool SetProtocol(uint8 ifIndex, uint8 protocol) override;
|
||||||
|
|
||||||
bool SetReport(ReportMessage* message) override;
|
bool SetReport(ReportMessage* message) override;
|
||||||
|
|
|
@ -387,7 +387,7 @@ namespace nsyshid
|
||||||
|
|
||||||
bool InfinityBaseDevice::GetDescriptor(uint8 descType,
|
bool InfinityBaseDevice::GetDescriptor(uint8 descType,
|
||||||
uint8 descIndex,
|
uint8 descIndex,
|
||||||
uint8 lang,
|
uint16 lang,
|
||||||
uint8* output,
|
uint8* output,
|
||||||
uint32 outputMaxLength)
|
uint32 outputMaxLength)
|
||||||
{
|
{
|
||||||
|
@ -450,6 +450,13 @@ namespace nsyshid
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool InfinityBaseDevice::SetIdle(uint8 ifIndex,
|
||||||
|
uint8 reportId,
|
||||||
|
uint8 duration)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool InfinityBaseDevice::SetProtocol(uint8 ifIndex, uint8 protocol)
|
bool InfinityBaseDevice::SetProtocol(uint8 ifIndex, uint8 protocol)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
@ -492,7 +499,7 @@ namespace nsyshid
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InfinityUSB::SendCommand(uint8* buf, sint32 originalLength)
|
void InfinityUSB::SendCommand(uint8* buf, uint32 length)
|
||||||
{
|
{
|
||||||
const uint8 command = buf[2];
|
const uint8 command = buf[2];
|
||||||
const uint8 sequence = buf[3];
|
const uint8 sequence = buf[3];
|
||||||
|
|
|
@ -26,10 +26,14 @@ namespace nsyshid
|
||||||
|
|
||||||
bool GetDescriptor(uint8 descType,
|
bool GetDescriptor(uint8 descType,
|
||||||
uint8 descIndex,
|
uint8 descIndex,
|
||||||
uint8 lang,
|
uint16 lang,
|
||||||
uint8* output,
|
uint8* output,
|
||||||
uint32 outputMaxLength) override;
|
uint32 outputMaxLength) override;
|
||||||
|
|
||||||
|
bool SetIdle(uint8 ifIndex,
|
||||||
|
uint8 reportId,
|
||||||
|
uint8 duration) override;
|
||||||
|
|
||||||
bool SetProtocol(uint8 ifIndex, uint8 protocol) override;
|
bool SetProtocol(uint8 ifIndex, uint8 protocol) override;
|
||||||
|
|
||||||
bool SetReport(ReportMessage* message) override;
|
bool SetReport(ReportMessage* message) override;
|
||||||
|
@ -53,7 +57,7 @@ namespace nsyshid
|
||||||
void Save();
|
void Save();
|
||||||
};
|
};
|
||||||
|
|
||||||
void SendCommand(uint8* buf, sint32 originalLength);
|
void SendCommand(uint8* buf, uint32 length);
|
||||||
std::array<uint8, 32> GetStatus();
|
std::array<uint8, 32> GetStatus();
|
||||||
|
|
||||||
void GetBlankResponse(uint8 sequence, std::array<uint8, 32>& replyBuf);
|
void GetBlankResponse(uint8 sequence, std::array<uint8, 32>& replyBuf);
|
||||||
|
|
|
@ -564,7 +564,7 @@ namespace nsyshid
|
||||||
|
|
||||||
bool SkylanderPortalDevice::GetDescriptor(uint8 descType,
|
bool SkylanderPortalDevice::GetDescriptor(uint8 descType,
|
||||||
uint8 descIndex,
|
uint8 descIndex,
|
||||||
uint8 lang,
|
uint16 lang,
|
||||||
uint8* output,
|
uint8* output,
|
||||||
uint32 outputMaxLength)
|
uint32 outputMaxLength)
|
||||||
{
|
{
|
||||||
|
@ -583,7 +583,7 @@ namespace nsyshid
|
||||||
*(uint8*)(currentWritePtr + 7) = 0x80; // bmAttributes
|
*(uint8*)(currentWritePtr + 7) = 0x80; // bmAttributes
|
||||||
*(uint8*)(currentWritePtr + 8) = 0xFA; // MaxPower
|
*(uint8*)(currentWritePtr + 8) = 0xFA; // MaxPower
|
||||||
currentWritePtr = currentWritePtr + 9;
|
currentWritePtr = currentWritePtr + 9;
|
||||||
// configuration descriptor
|
// interface descriptor
|
||||||
*(uint8*)(currentWritePtr + 0) = 9; // bLength
|
*(uint8*)(currentWritePtr + 0) = 9; // bLength
|
||||||
*(uint8*)(currentWritePtr + 1) = 0x04; // bDescriptorType
|
*(uint8*)(currentWritePtr + 1) = 0x04; // bDescriptorType
|
||||||
*(uint8*)(currentWritePtr + 2) = 0; // bInterfaceNumber
|
*(uint8*)(currentWritePtr + 2) = 0; // bInterfaceNumber
|
||||||
|
@ -594,7 +594,7 @@ namespace nsyshid
|
||||||
*(uint8*)(currentWritePtr + 7) = 0; // bInterfaceProtocol
|
*(uint8*)(currentWritePtr + 7) = 0; // bInterfaceProtocol
|
||||||
*(uint8*)(currentWritePtr + 8) = 0; // iInterface
|
*(uint8*)(currentWritePtr + 8) = 0; // iInterface
|
||||||
currentWritePtr = currentWritePtr + 9;
|
currentWritePtr = currentWritePtr + 9;
|
||||||
// configuration descriptor
|
// HID descriptor
|
||||||
*(uint8*)(currentWritePtr + 0) = 9; // bLength
|
*(uint8*)(currentWritePtr + 0) = 9; // bLength
|
||||||
*(uint8*)(currentWritePtr + 1) = 0x21; // bDescriptorType
|
*(uint8*)(currentWritePtr + 1) = 0x21; // bDescriptorType
|
||||||
*(uint16be*)(currentWritePtr + 2) = 0x0111; // bcdHID
|
*(uint16be*)(currentWritePtr + 2) = 0x0111; // bcdHID
|
||||||
|
@ -608,7 +608,7 @@ namespace nsyshid
|
||||||
*(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType
|
*(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType
|
||||||
*(uint8*)(currentWritePtr + 2) = 0x81; // bEndpointAddress
|
*(uint8*)(currentWritePtr + 2) = 0x81; // bEndpointAddress
|
||||||
*(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes
|
*(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes
|
||||||
*(uint16be*)(currentWritePtr + 4) = 0x40; // wMaxPacketSize
|
*(uint16be*)(currentWritePtr + 4) = 0x0040; // wMaxPacketSize
|
||||||
*(uint8*)(currentWritePtr + 6) = 0x01; // bInterval
|
*(uint8*)(currentWritePtr + 6) = 0x01; // bInterval
|
||||||
currentWritePtr = currentWritePtr + 7;
|
currentWritePtr = currentWritePtr + 7;
|
||||||
// endpoint descriptor 2
|
// endpoint descriptor 2
|
||||||
|
@ -616,7 +616,7 @@ namespace nsyshid
|
||||||
*(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType
|
*(uint8*)(currentWritePtr + 1) = 0x05; // bDescriptorType
|
||||||
*(uint8*)(currentWritePtr + 2) = 0x02; // bEndpointAddress
|
*(uint8*)(currentWritePtr + 2) = 0x02; // bEndpointAddress
|
||||||
*(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes
|
*(uint8*)(currentWritePtr + 3) = 0x03; // bmAttributes
|
||||||
*(uint16be*)(currentWritePtr + 4) = 0x40; // wMaxPacketSize
|
*(uint16be*)(currentWritePtr + 4) = 0x0040; // wMaxPacketSize
|
||||||
*(uint8*)(currentWritePtr + 6) = 0x01; // bInterval
|
*(uint8*)(currentWritePtr + 6) = 0x01; // bInterval
|
||||||
currentWritePtr = currentWritePtr + 7;
|
currentWritePtr = currentWritePtr + 7;
|
||||||
|
|
||||||
|
@ -627,6 +627,13 @@ namespace nsyshid
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SkylanderPortalDevice::SetIdle(uint8 ifIndex,
|
||||||
|
uint8 reportId,
|
||||||
|
uint8 duration)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool SkylanderPortalDevice::SetProtocol(uint8 ifIndex, uint8 protocol)
|
bool SkylanderPortalDevice::SetProtocol(uint8 ifIndex, uint8 protocol)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
@ -634,12 +641,12 @@ namespace nsyshid
|
||||||
|
|
||||||
bool SkylanderPortalDevice::SetReport(ReportMessage* message)
|
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));
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkylanderUSB::ControlTransfer(uint8* buf, sint32 originalLength)
|
void SkylanderUSB::ControlTransfer(uint8* buf, uint32 length)
|
||||||
{
|
{
|
||||||
std::array<uint8, 64> interruptResponse = {};
|
std::array<uint8, 64> interruptResponse = {};
|
||||||
switch (buf[0])
|
switch (buf[0])
|
||||||
|
|
|
@ -26,10 +26,14 @@ namespace nsyshid
|
||||||
|
|
||||||
bool GetDescriptor(uint8 descType,
|
bool GetDescriptor(uint8 descType,
|
||||||
uint8 descIndex,
|
uint8 descIndex,
|
||||||
uint8 lang,
|
uint16 lang,
|
||||||
uint8* output,
|
uint8* output,
|
||||||
uint32 outputMaxLength) override;
|
uint32 outputMaxLength) override;
|
||||||
|
|
||||||
|
bool SetIdle(uint8 ifIndex,
|
||||||
|
uint8 reportId,
|
||||||
|
uint8 duration) override;
|
||||||
|
|
||||||
bool SetProtocol(uint8 ifIndex, uint8 protocol) override;
|
bool SetProtocol(uint8 ifIndex, uint8 protocol) override;
|
||||||
|
|
||||||
bool SetReport(ReportMessage* message) override;
|
bool SetReport(ReportMessage* message) override;
|
||||||
|
@ -70,7 +74,7 @@ namespace nsyshid
|
||||||
uint8 blue = 0;
|
uint8 blue = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
void ControlTransfer(uint8* buf, sint32 originalLength);
|
void ControlTransfer(uint8* buf, uint32 length);
|
||||||
|
|
||||||
void Activate();
|
void Activate();
|
||||||
void Deactivate();
|
void Deactivate();
|
||||||
|
|
|
@ -305,47 +305,37 @@ namespace nsyshid
|
||||||
osLib_returnFromFunction(hCPU, 0);
|
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
|
constexpr size_t BYTES_PER_LINE = 16;
|
||||||
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
|
|
||||||
|
|
||||||
int returnValue = -1;
|
std::string out;
|
||||||
std::shared_ptr<Device> device = GetDeviceByHandle(hidHandle, true);
|
for (size_t row_start = 0; row_start < size; row_start += BYTES_PER_LINE)
|
||||||
if (device)
|
|
||||||
{
|
{
|
||||||
memset(output, 0, outputMaxLength);
|
out += fmt::format("{:06x}: ", row_start);
|
||||||
if (device->GetDescriptor(descType, descIndex, lang, output, outputMaxLength))
|
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<char>(data[row_start + i]);
|
||||||
|
out += std::isprint(c, std::locale::classic()) ? c : '.';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
out += "\n";
|
||||||
}
|
}
|
||||||
else
|
cemuLog_logDebug(LogType::Force, "[{}] Data: \n{}", prefix, out);
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoHIDTransferCallback(MPTR callbackFuncMPTR, MPTR callbackParamMPTR, uint32 hidHandle, uint32 errorCode,
|
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);
|
coreinitAsyncCallback_add(callbackFuncMPTR, 5, hidHandle, errorCode, buffer, length, callbackParamMPTR);
|
||||||
}
|
}
|
||||||
|
|
||||||
void export_HIDSetIdle(PPCInterpreter_t* hCPU)
|
void _hidGetDescriptorAsync(std::shared_ptr<Device> device, uint8 descType, uint8 descIndex, uint16 lang, uint8* output, uint32 outputMaxLength, MPTR callbackFuncMPTR, MPTR callbackParamMPTR)
|
||||||
{
|
{
|
||||||
ppcDefineParamU32(hidHandle, 0); // r3
|
if (device->GetDescriptor(descType, descIndex, lang, output, outputMaxLength))
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
DoHIDTransferCallback(callbackFuncMPTR, callbackParamMPTR, hidHandle, 0, MPTR_NULL, 0);
|
DoHIDTransferCallback(callbackFuncMPTR,
|
||||||
|
callbackParamMPTR,
|
||||||
|
device->m_hid->handle,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0);
|
||||||
}
|
}
|
||||||
else
|
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> 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> 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> 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> 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)
|
void export_HIDSetProtocol(PPCInterpreter_t* hCPU)
|
||||||
|
@ -383,51 +499,51 @@ namespace nsyshid
|
||||||
ppcDefineParamU8(protocol, 2); // r5
|
ppcDefineParamU8(protocol, 2); // r5
|
||||||
ppcDefineParamMPTR(callbackFuncMPTR, 3); // r6
|
ppcDefineParamMPTR(callbackFuncMPTR, 3); // r6
|
||||||
ppcDefineParamMPTR(callbackParamMPTR, 4); // r7
|
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> device = GetDeviceByHandle(hidHandle, true);
|
std::shared_ptr<Device> device = GetDeviceByHandle(hidHandle, true);
|
||||||
sint32 returnCode = -1;
|
if (device == nullptr)
|
||||||
if (device)
|
|
||||||
{
|
{
|
||||||
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");
|
returnCode = 0;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (device->SetProtocol(ifIndex, protocol))
|
|
||||||
{
|
|
||||||
returnCode = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cemu_assert_suspicious();
|
// asynchronous
|
||||||
}
|
std::thread(&_hidSetProtocolAsync, device, ifIndex, protocol, callbackFuncMPTR, callbackParamMPTR)
|
||||||
|
.detach();
|
||||||
if (callbackFuncMPTR)
|
returnCode = 0;
|
||||||
{
|
|
||||||
DoHIDTransferCallback(callbackFuncMPTR, callbackParamMPTR, hidHandle, 0, MPTR_NULL, 0);
|
|
||||||
}
|
}
|
||||||
osLib_returnFromFunction(hCPU, returnCode);
|
osLib_returnFromFunction(hCPU, returnCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// handler for async HIDSetReport transfers
|
// handler for async HIDSetReport transfers
|
||||||
void _hidSetReportAsync(std::shared_ptr<Device> device, uint8* reportData, sint32 length,
|
void _hidSetReportAsync(std::shared_ptr<Device> device, uint8 reportType, uint8 reportId, uint8* data, uint32 length,
|
||||||
uint8* originalData,
|
MPTR callbackFuncMPTR, MPTR callbackParamMPTR)
|
||||||
sint32 originalLength, MPTR callbackFuncMPTR, MPTR callbackParamMPTR)
|
|
||||||
{
|
{
|
||||||
cemuLog_logDebug(LogType::Force, "_hidSetReportAsync begin");
|
cemuLog_logDebug(LogType::Force, "_hidSetReportAsync begin");
|
||||||
ReportMessage message(reportData, length, originalData, originalLength);
|
ReportMessage message(reportType, reportId, data, length);
|
||||||
if (device->SetReport(&message))
|
if (device->SetReport(&message))
|
||||||
{
|
{
|
||||||
DoHIDTransferCallback(callbackFuncMPTR,
|
DoHIDTransferCallback(callbackFuncMPTR,
|
||||||
callbackParamMPTR,
|
callbackParamMPTR,
|
||||||
device->m_hid->handle,
|
device->m_hid->handle,
|
||||||
0,
|
0,
|
||||||
memory_getVirtualOffsetFromPointer(originalData),
|
memory_getVirtualOffsetFromPointer(data),
|
||||||
originalLength);
|
length);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -435,24 +551,22 @@ namespace nsyshid
|
||||||
callbackParamMPTR,
|
callbackParamMPTR,
|
||||||
device->m_hid->handle,
|
device->m_hid->handle,
|
||||||
-1,
|
-1,
|
||||||
memory_getVirtualOffsetFromPointer(originalData),
|
memory_getVirtualOffsetFromPointer(data),
|
||||||
0);
|
length);
|
||||||
}
|
}
|
||||||
free(reportData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// handler for synchronous HIDSetReport transfers
|
// handler for synchronous HIDSetReport transfers
|
||||||
sint32 _hidSetReportSync(std::shared_ptr<Device> device, uint8* reportData, sint32 length,
|
sint32 _hidSetReportSync(std::shared_ptr<Device> device, uint8 reportType, uint8 reportId,
|
||||||
uint8* originalData, sint32 originalLength, coreinit::OSEvent* event)
|
uint8* data, uint32 length, coreinit::OSEvent* event)
|
||||||
{
|
{
|
||||||
_debugPrintHex("_hidSetReportSync Begin", reportData, length);
|
_debugPrintHex("_hidSetReportSync Begin", data, length);
|
||||||
sint32 returnCode = 0;
|
sint32 returnCode = 0;
|
||||||
ReportMessage message(reportData, length, originalData, originalLength);
|
ReportMessage message(reportType, reportId, data, length);
|
||||||
if (device->SetReport(&message))
|
if (device->SetReport(&message))
|
||||||
{
|
{
|
||||||
returnCode = originalLength;
|
returnCode = length;
|
||||||
}
|
}
|
||||||
free(reportData);
|
|
||||||
cemuLog_logDebug(LogType::Force, "_hidSetReportSync end. returnCode: {}", returnCode);
|
cemuLog_logDebug(LogType::Force, "_hidSetReportSync end. returnCode: {}", returnCode);
|
||||||
coreinit::OSSignalEvent(event);
|
coreinit::OSSignalEvent(event);
|
||||||
return returnCode;
|
return returnCode;
|
||||||
|
@ -461,19 +575,19 @@ namespace nsyshid
|
||||||
void export_HIDSetReport(PPCInterpreter_t* hCPU)
|
void export_HIDSetReport(PPCInterpreter_t* hCPU)
|
||||||
{
|
{
|
||||||
ppcDefineParamU32(hidHandle, 0); // r3
|
ppcDefineParamU32(hidHandle, 0); // r3
|
||||||
ppcDefineParamU32(reportRelatedUkn, 1); // r4
|
ppcDefineParamU8(reportType, 1); // r4
|
||||||
ppcDefineParamU32(reportId, 2); // r5
|
ppcDefineParamU8(reportId, 2); // r5
|
||||||
ppcDefineParamUStr(data, 3); // r6
|
ppcDefineParamUStr(data, 3); // r6
|
||||||
ppcDefineParamU32(dataLength, 4); // r7
|
ppcDefineParamU32(dataLength, 4); // r7
|
||||||
ppcDefineParamMPTR(callbackFuncMPTR, 5); // r8
|
ppcDefineParamMPTR(callbackFuncMPTR, 5); // r8
|
||||||
ppcDefineParamMPTR(callbackParamMPTR, 6); // r9
|
ppcDefineParamMPTR(callbackParamMPTR, 6); // r9
|
||||||
cemuLog_logDebug(LogType::Force, "nsyshid.HIDSetReport({},0x{:02x},0x{:02x},...)", hidHandle, reportRelatedUkn,
|
cemuLog_logDebug(LogType::Force, "nsyshid.HIDSetReport(0x{:08x}, 0x{:02x}, 0x{:02x}, 0x{:08x}, 0x{:08x}, 0x{:08x}, 0x{:08x})", hCPU->gpr[3],
|
||||||
reportId);
|
hCPU->gpr[4], hCPU->gpr[5], hCPU->gpr[6], hCPU->gpr[7], hCPU->gpr[8], hCPU->gpr[9]);
|
||||||
|
|
||||||
_debugPrintHex("HIDSetReport", data, dataLength);
|
_debugPrintHex("HIDSetReport", data, dataLength);
|
||||||
|
|
||||||
#ifdef CEMU_DEBUG_ASSERT
|
#ifdef CEMU_DEBUG_ASSERT
|
||||||
if (reportRelatedUkn != 2 || reportId != 0)
|
if (reportType != 2 || reportId != 0)
|
||||||
assert_dbg();
|
assert_dbg();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -485,15 +599,6 @@ namespace nsyshid
|
||||||
return;
|
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)
|
// issue request (synchronous or asynchronous)
|
||||||
sint32 returnCode = 0;
|
sint32 returnCode = 0;
|
||||||
if (callbackFuncMPTR == MPTR_NULL)
|
if (callbackFuncMPTR == MPTR_NULL)
|
||||||
|
@ -501,15 +606,14 @@ namespace nsyshid
|
||||||
// synchronous
|
// synchronous
|
||||||
StackAllocator<coreinit::OSEvent> event;
|
StackAllocator<coreinit::OSEvent> event;
|
||||||
coreinit::OSInitEvent(&event, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_AUTO);
|
coreinit::OSInitEvent(&event, coreinit::OSEvent::EVENT_STATE::STATE_NOT_SIGNALED, coreinit::OSEvent::EVENT_MODE::MODE_AUTO);
|
||||||
std::future<sint32> res = std::async(std::launch::async, &_hidSetReportSync, device, reportData,
|
std::future<sint32> res = std::async(std::launch::async, &_hidSetReportSync, device, reportType, reportId, data, dataLength, &event);
|
||||||
paddedLength + 1, data, dataLength, &event);
|
|
||||||
coreinit::OSWaitEvent(&event);
|
coreinit::OSWaitEvent(&event);
|
||||||
returnCode = res.get();
|
returnCode = res.get();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// asynchronous
|
// asynchronous
|
||||||
std::thread(&_hidSetReportAsync, device, reportData, paddedLength + 1, data, dataLength,
|
std::thread(&_hidSetReportAsync, device, reportType, reportId, data, dataLength,
|
||||||
callbackFuncMPTR, callbackParamMPTR)
|
callbackFuncMPTR, callbackParamMPTR)
|
||||||
.detach();
|
.detach();
|
||||||
returnCode = 0;
|
returnCode = 0;
|
||||||
|
@ -586,7 +690,7 @@ namespace nsyshid
|
||||||
ppcDefineParamMPTR(callbackFuncMPTR, 3); // r6
|
ppcDefineParamMPTR(callbackFuncMPTR, 3); // r6
|
||||||
ppcDefineParamMPTR(callbackParamMPTR, 4); // r7
|
ppcDefineParamMPTR(callbackParamMPTR, 4); // r7
|
||||||
cemuLog_logDebug(LogType::Force, "nsyshid.HIDRead(0x{:x},0x{:08x},0x{:08x},0x{:08x},0x{:08x})", hCPU->gpr[3],
|
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> device = GetDeviceByHandle(hidHandle, true);
|
std::shared_ptr<Device> device = GetDeviceByHandle(hidHandle, true);
|
||||||
if (device == nullptr)
|
if (device == nullptr)
|
||||||
|
@ -683,7 +787,7 @@ namespace nsyshid
|
||||||
ppcDefineParamMPTR(callbackFuncMPTR, 3); // r6
|
ppcDefineParamMPTR(callbackFuncMPTR, 3); // r6
|
||||||
ppcDefineParamMPTR(callbackParamMPTR, 4); // r7
|
ppcDefineParamMPTR(callbackParamMPTR, 4); // r7
|
||||||
cemuLog_logDebug(LogType::Force, "nsyshid.HIDWrite(0x{:x},0x{:08x},0x{:08x},0x{:08x},0x{:08x})", hCPU->gpr[3],
|
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> device = GetDeviceByHandle(hidHandle, true);
|
std::shared_ptr<Device> device = GetDeviceByHandle(hidHandle, true);
|
||||||
if (device == nullptr)
|
if (device == nullptr)
|
||||||
|
@ -718,7 +822,7 @@ namespace nsyshid
|
||||||
ppcDefineParamTypePtr(ukn0, uint32be, 1);
|
ppcDefineParamTypePtr(ukn0, uint32be, 1);
|
||||||
ppcDefineParamTypePtr(ukn1, uint32be, 2);
|
ppcDefineParamTypePtr(ukn1, uint32be, 2);
|
||||||
cemuLog_logDebug(LogType::Force, "nsyshid.HIDDecodeError(0x{:08x},0x{:08x},0x{:08x})", hCPU->gpr[3],
|
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
|
// todo
|
||||||
*ukn0 = 0x3FF;
|
*ukn0 = 0x3FF;
|
||||||
|
|
Loading…
Reference in New Issue