Cemu/src/Cafe/OS/libs/nsyshid/nsyshid.cpp
Exzap c5913fe8cb refactor/rpl: Treat HLE libs more like RPL modules
HLE modules now have a unified interface via which they can get notified when mapped into the process or when loaded/unloaded.

We also always call the unload functions when stopping emulation to give the module implementations a chance to reset all state (although many of them are still missing proper cleanup code for now)
2026-01-07 02:46:49 +01:00

990 lines
28 KiB
C++

#include "Cafe/OS/common/OSCommon.h"
#include "Cafe/HW/Espresso/PPCCallback.h"
#include <bitset>
#include <mutex>
#include "nsyshid.h"
#include "Cafe/OS/libs/coreinit/coreinit_Thread.h"
#include "Backend.h"
#include "Whitelist.h"
namespace nsyshid
{
std::list<std::shared_ptr<Backend>> backendList;
std::list<std::shared_ptr<Device>> deviceList;
typedef struct _HIDClient_t
{
uint32be callbackFunc; // attach/detach callback
} HIDClient_t;
std::list<HIDClient_t*> HIDClientList;
std::recursive_mutex hidMutex;
void AttachClientToList(HIDClient_t* hidClient)
{
std::lock_guard<std::recursive_mutex> lock(hidMutex);
// todo - append at the beginning or end of the list? List order matters because it also controls the order in which attach callbacks are called
HIDClientList.push_front(hidClient);
}
void DetachClientFromList(HIDClient_t* hidClient)
{
std::lock_guard<std::recursive_mutex> lock(hidMutex);
HIDClientList.remove(hidClient);
}
std::shared_ptr<Device> GetDeviceByHandle(uint32 handle, bool openIfClosed = false)
{
std::shared_ptr<Device> device;
{
std::lock_guard<std::recursive_mutex> lock(hidMutex);
for (const auto& d : deviceList)
{
if (d->m_hid->handle == handle)
{
device = d;
break;
}
}
}
if (device != nullptr)
{
if (openIfClosed && !device->IsOpened())
{
if (!device->Open())
{
return nullptr;
}
}
return device;
}
return nullptr;
}
uint32 _lastGeneratedHidHandle = 1;
uint32 GenerateHIDHandle()
{
std::lock_guard<std::recursive_mutex> lock(hidMutex);
_lastGeneratedHidHandle++;
return _lastGeneratedHidHandle;
}
const int HID_MAX_NUM_DEVICES = 128;
SysAllocator<HID_t, HID_MAX_NUM_DEVICES> HIDPool;
std::queue<size_t> HIDPoolIndexQueue;
void InitHIDPoolIndexQueue()
{
static bool HIDPoolIndexQueueInitialized = false;
std::lock_guard<std::recursive_mutex> lock(hidMutex);
if (HIDPoolIndexQueueInitialized)
{
return;
}
HIDPoolIndexQueueInitialized = true;
for (size_t i = 0; i < HID_MAX_NUM_DEVICES; i++)
{
HIDPoolIndexQueue.push(i);
}
}
HID_t* GetFreeHID()
{
std::lock_guard<std::recursive_mutex> lock(hidMutex);
InitHIDPoolIndexQueue();
if (HIDPoolIndexQueue.empty())
{
return nullptr;
}
size_t index = HIDPoolIndexQueue.front();
HIDPoolIndexQueue.pop();
return HIDPool.GetPtr() + index;
}
void ReleaseHID(HID_t* device)
{
// this should never happen, but having a safeguard can't hurt
if (device == nullptr)
{
cemu_assert_error();
}
std::lock_guard<std::recursive_mutex> lock(hidMutex);
InitHIDPoolIndexQueue();
size_t index = device - HIDPool.GetPtr();
HIDPoolIndexQueue.push(index);
}
const int HID_CALLBACK_DETACH = 0;
const int HID_CALLBACK_ATTACH = 1;
uint32 DoAttachCallback(HIDClient_t* hidClient, const std::shared_ptr<Device>& device)
{
return PPCCoreCallback(hidClient->callbackFunc, memory_getVirtualOffsetFromPointer(hidClient),
memory_getVirtualOffsetFromPointer(device->m_hid), HID_CALLBACK_ATTACH);
}
void DoAttachCallbackAsync(HIDClient_t* hidClient, const std::shared_ptr<Device>& device)
{
coreinitAsyncCallback_add(hidClient->callbackFunc, 3, memory_getVirtualOffsetFromPointer(hidClient),
memory_getVirtualOffsetFromPointer(device->m_hid), HID_CALLBACK_ATTACH);
}
void DoDetachCallback(HIDClient_t* hidClient, const std::shared_ptr<Device>& device)
{
PPCCoreCallback(hidClient->callbackFunc, memory_getVirtualOffsetFromPointer(hidClient),
memory_getVirtualOffsetFromPointer(device->m_hid), HID_CALLBACK_DETACH);
}
void DoDetachCallbackAsync(HIDClient_t* hidClient, const std::shared_ptr<Device>& device)
{
coreinitAsyncCallback_add(hidClient->callbackFunc, 3, memory_getVirtualOffsetFromPointer(hidClient),
memory_getVirtualOffsetFromPointer(device->m_hid), HID_CALLBACK_DETACH);
}
void AttachBackend(const std::shared_ptr<Backend>& backend)
{
{
std::lock_guard<std::recursive_mutex> lock(hidMutex);
backendList.push_back(backend);
}
backend->OnAttach();
}
void DetachBackend(const std::shared_ptr<Backend>& backend)
{
{
std::lock_guard<std::recursive_mutex> lock(hidMutex);
backendList.remove(backend);
}
backend->OnDetach();
}
void DetachAllBackends()
{
std::list<std::shared_ptr<Backend>> backendListCopy;
{
std::lock_guard<std::recursive_mutex> lock(hidMutex);
backendListCopy = backendList;
backendList.clear();
}
for (const auto& backend : backendListCopy)
{
backend->OnDetach();
}
}
void AttachDefaultBackends()
{
backend::AttachDefaultBackends();
}
bool AttachDevice(const std::shared_ptr<Device>& device)
{
std::lock_guard<std::recursive_mutex> lock(hidMutex);
// is the device already attached?
{
auto it = std::find(deviceList.begin(), deviceList.end(), device);
if (it != deviceList.end())
{
cemuLog_logDebug(LogType::Force,
"nsyshid.AttachDevice(): failed to attach device: {:04x}:{:04x}: already attached",
device->m_vendorId,
device->m_productId);
return false;
}
}
HID_t* hidDevice = GetFreeHID();
if (hidDevice == nullptr)
{
cemuLog_logDebug(LogType::Force,
"nsyshid.AttachDevice(): failed to attach device: {:04x}:{:04x}: no free device slots left",
device->m_vendorId,
device->m_productId);
return false;
}
hidDevice->handle = GenerateHIDHandle();
device->AssignHID(hidDevice);
deviceList.push_back(device);
// do attach callbacks
for (auto client : HIDClientList)
{
DoAttachCallbackAsync(client, device);
}
cemuLog_logDebug(LogType::Force, "nsyshid.AttachDevice(): device attached: {:04x}:{:04x}",
device->m_vendorId,
device->m_productId);
return true;
}
void DetachDevice(const std::shared_ptr<Device>& device)
{
{
std::lock_guard<std::recursive_mutex> lock(hidMutex);
// remove from list
auto it = std::find(deviceList.begin(), deviceList.end(), device);
if (it == deviceList.end())
{
cemuLog_logDebug(LogType::Force, "nsyshid.DetachDevice(): device not found: {:04x}:{:04x}",
device->m_vendorId,
device->m_productId);
return;
}
deviceList.erase(it);
// do detach callbacks
for (auto client : HIDClientList)
{
DoDetachCallbackAsync(client, device);
}
ReleaseHID(device->m_hid);
}
device->Close();
cemuLog_logDebug(LogType::Force, "nsyshid.DetachDevice(): device removed: {:04x}:{:04x}",
device->m_vendorId,
device->m_productId);
}
std::shared_ptr<Device> FindDeviceById(uint16 vendorId, uint16 productId)
{
std::lock_guard<std::recursive_mutex> lock(hidMutex);
for (const auto& device : deviceList)
{
if (device->m_vendorId == vendorId && device->m_productId == productId)
{
return device;
}
}
return nullptr;
}
void export_HIDAddClient(PPCInterpreter_t* hCPU)
{
ppcDefineParamTypePtr(hidClient, HIDClient_t, 0);
ppcDefineParamMPTR(callbackFuncMPTR, 1);
cemuLog_logDebug(LogType::Force, "nsyshid.HIDAddClient(0x{:08x},0x{:08x})", hCPU->gpr[3], hCPU->gpr[4]);
hidClient->callbackFunc = callbackFuncMPTR;
std::lock_guard<std::recursive_mutex> lock(hidMutex);
AttachClientToList(hidClient);
// do attach callbacks
for (const auto& device : deviceList)
{
DoAttachCallback(hidClient, device);
}
osLib_returnFromFunction(hCPU, 0);
}
void export_HIDDelClient(PPCInterpreter_t* hCPU)
{
ppcDefineParamTypePtr(hidClient, HIDClient_t, 0);
cemuLog_logDebug(LogType::Force, "nsyshid.HIDDelClient(0x{:08x})", hCPU->gpr[3]);
std::lock_guard<std::recursive_mutex> lock(hidMutex);
DetachClientFromList(hidClient);
// do detach callbacks
for (const auto& device : deviceList)
{
DoDetachCallback(hidClient, device);
}
osLib_returnFromFunction(hCPU, 0);
}
void _debugPrintHex(const std::string prefix, const uint8* data, size_t size)
{
constexpr size_t BYTES_PER_LINE = 16;
std::string out;
for (size_t row_start = 0; row_start < size; row_start += BYTES_PER_LINE)
{
out += fmt::format("{:06x}: ", row_start);
for (size_t i = 0; i < BYTES_PER_LINE; ++i)
{
if (row_start + i < size)
{
out += fmt::format("{:02x} ", data[row_start + i]);
}
else
{
out += " ";
}
}
out += " ";
for (size_t i = 0; i < BYTES_PER_LINE; ++i)
{
if (row_start + i < size)
{
char c = static_cast<char>(data[row_start + i]);
out += std::isprint(c, std::locale::classic()) ? c : '.';
}
}
out += "\n";
}
cemuLog_logDebug(LogType::Force, "[{}] Data: \n{}", prefix, out);
}
void DoHIDTransferCallback(MPTR callbackFuncMPTR, MPTR callbackParamMPTR, uint32 hidHandle, uint32 errorCode,
MPTR buffer, sint32 length)
{
coreinitAsyncCallback_add(callbackFuncMPTR, 5, hidHandle, errorCode, buffer, length, callbackParamMPTR);
}
void _hidGetDescriptorAsync(std::shared_ptr<Device> device, uint8 descType, uint8 descIndex, uint16 lang, uint8* output, uint32 outputMaxLength, MPTR callbackFuncMPTR, MPTR callbackParamMPTR)
{
if (device->GetDescriptor(descType, descIndex, lang, output, outputMaxLength))
{
DoHIDTransferCallback(callbackFuncMPTR,
callbackParamMPTR,
device->m_hid->handle,
0,
0,
0);
}
else
{
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);
}
}
void export_HIDSetProtocol(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(hidHandle, 0); // r3
ppcDefineParamU8(ifIndex, 1); // r4
ppcDefineParamU8(protocol, 2); // r5
ppcDefineParamMPTR(callbackFuncMPTR, 3); // r6
ppcDefineParamMPTR(callbackParamMPTR, 4); // r7
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);
if (device == nullptr)
{
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))
{
returnCode = 0;
}
}
else
{
// 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> device, uint8 reportType, uint8 reportId, uint8* data, uint32 length,
MPTR callbackFuncMPTR, MPTR callbackParamMPTR)
{
cemuLog_logDebug(LogType::Force, "_hidSetReportAsync begin");
ReportMessage message(reportType, reportId, data, length);
if (device->SetReport(&message))
{
DoHIDTransferCallback(callbackFuncMPTR,
callbackParamMPTR,
device->m_hid->handle,
0,
memory_getVirtualOffsetFromPointer(data),
length);
}
else
{
DoHIDTransferCallback(callbackFuncMPTR,
callbackParamMPTR,
device->m_hid->handle,
-1,
memory_getVirtualOffsetFromPointer(data),
length);
}
}
// handler for synchronous HIDSetReport transfers
sint32 _hidSetReportSync(std::shared_ptr<Device> device, uint8 reportType, uint8 reportId,
uint8* data, uint32 length, coreinit::OSEvent* event)
{
_debugPrintHex("_hidSetReportSync Begin", data, length);
sint32 returnCode = 0;
ReportMessage message(reportType, reportId, data, length);
if (device->SetReport(&message))
{
returnCode = length;
}
cemuLog_logDebug(LogType::Force, "_hidSetReportSync end. returnCode: {}", returnCode);
coreinit::OSSignalEvent(event);
return returnCode;
}
void export_HIDSetReport(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(hidHandle, 0); // r3
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{: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 (reportType != 2 || reportId != 0)
assert_dbg();
#endif
std::shared_ptr<Device> device = GetDeviceByHandle(hidHandle, true);
if (device == nullptr)
{
cemuLog_log(LogType::Force, "nsyshid.HIDSetReport(): 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
StackAllocator<coreinit::OSEvent> event;
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, reportType, reportId, data, dataLength, &event);
coreinit::OSWaitEvent(&event);
returnCode = res.get();
}
else
{
// asynchronous
std::thread(&_hidSetReportAsync, device, reportType, reportId, data, dataLength,
callbackFuncMPTR, callbackParamMPTR)
.detach();
returnCode = 0;
}
osLib_returnFromFunction(hCPU, returnCode);
}
sint32 _hidReadInternalSync(std::shared_ptr<Device> device, uint8* data, sint32 maxLength)
{
cemuLog_logDebug(LogType::Force, "HidRead Begin (Length 0x{:08x})", maxLength);
if (!device->IsOpened())
{
cemuLog_logDebug(LogType::Force, "nsyshid.hidReadInternalSync(): cannot read from a non-opened device");
return -1;
}
memset(data, 0, maxLength);
ReadMessage message(data, maxLength, 0);
Device::ReadResult readResult = device->Read(&message);
switch (readResult)
{
case Device::ReadResult::Success:
{
cemuLog_logDebug(LogType::Force, "nsyshid.hidReadInternalSync(): read {} of {} bytes",
message.bytesRead,
maxLength);
return message.bytesRead;
}
break;
case Device::ReadResult::Error:
{
cemuLog_logDebug(LogType::Force, "nsyshid.hidReadInternalSync(): read error");
return -1;
}
break;
case Device::ReadResult::ErrorTimeout:
{
cemuLog_logDebug(LogType::Force, "nsyshid.hidReadInternalSync(): read error: timeout");
return -108;
}
break;
}
cemuLog_logDebug(LogType::Force, "nsyshid.hidReadInternalSync(): read error: unknown");
return -1;
}
void _hidReadAsync(std::shared_ptr<Device> device,
uint8* data, sint32 maxLength,
MPTR callbackFuncMPTR,
MPTR callbackParamMPTR)
{
sint32 returnCode = _hidReadInternalSync(device, data, maxLength);
sint32 errorCode = 0;
if (returnCode < 0)
errorCode = returnCode; // don't return number of bytes in error code
DoHIDTransferCallback(callbackFuncMPTR, callbackParamMPTR, device->m_hid->handle, errorCode,
memory_getVirtualOffsetFromPointer(data), (returnCode > 0) ? returnCode : 0);
}
sint32 _hidReadSync(std::shared_ptr<Device> device,
uint8* data,
sint32 maxLength,
coreinit::OSEvent* event)
{
sint32 returnCode = _hidReadInternalSync(device, data, maxLength);
coreinit::OSSignalEvent(event);
return returnCode;
}
void export_HIDRead(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(hidHandle, 0); // r3
ppcDefineParamUStr(data, 1); // r4
ppcDefineParamU32(maxLength, 2); // r5
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]);
std::shared_ptr<Device> device = GetDeviceByHandle(hidHandle, true);
if (device == nullptr)
{
cemuLog_log(LogType::Force, "nsyshid.HIDRead(): Unable to find device with hid handle {}", hidHandle);
osLib_returnFromFunction(hCPU, -1);
return;
}
sint32 returnCode = 0;
if (callbackFuncMPTR != MPTR_NULL)
{
// asynchronous transfer
std::thread(&_hidReadAsync, device, data, maxLength, callbackFuncMPTR, callbackParamMPTR).detach();
returnCode = 0;
}
else
{
// synchronous transfer
StackAllocator<coreinit::OSEvent> event;
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, &_hidReadSync, device, data, maxLength, &event);
coreinit::OSWaitEvent(&event);
returnCode = res.get();
}
osLib_returnFromFunction(hCPU, returnCode);
}
sint32 _hidWriteInternalSync(std::shared_ptr<Device> device, uint8* data, sint32 maxLength)
{
cemuLog_logDebug(LogType::Force, "HidWrite Begin (Length 0x{:08x})", maxLength);
if (!device->IsOpened())
{
cemuLog_logDebug(LogType::Force, "nsyshid.hidWriteInternalSync(): cannot write to a non-opened device");
return -1;
}
WriteMessage message(data, maxLength, 0);
Device::WriteResult writeResult = device->Write(&message);
switch (writeResult)
{
case Device::WriteResult::Success:
{
cemuLog_logDebug(LogType::Force, "nsyshid.hidWriteInternalSync(): wrote {} of {} bytes", message.bytesWritten,
maxLength);
return message.bytesWritten;
}
break;
case Device::WriteResult::Error:
{
cemuLog_logDebug(LogType::Force, "nsyshid.hidWriteInternalSync(): write error");
return -1;
}
break;
case Device::WriteResult::ErrorTimeout:
{
cemuLog_logDebug(LogType::Force, "nsyshid.hidWriteInternalSync(): write error: timeout");
return -108;
}
break;
}
cemuLog_logDebug(LogType::Force, "nsyshid.hidWriteInternalSync(): write error: unknown");
return -1;
}
void _hidWriteAsync(std::shared_ptr<Device> device,
uint8* data,
sint32 maxLength,
MPTR callbackFuncMPTR,
MPTR callbackParamMPTR)
{
sint32 returnCode = _hidWriteInternalSync(device, data, maxLength);
sint32 errorCode = 0;
if (returnCode < 0)
errorCode = returnCode; // don't return number of bytes in error code
DoHIDTransferCallback(callbackFuncMPTR, callbackParamMPTR, device->m_hid->handle, errorCode,
memory_getVirtualOffsetFromPointer(data), (returnCode > 0) ? returnCode : 0);
}
sint32 _hidWriteSync(std::shared_ptr<Device> device,
uint8* data,
sint32 maxLength,
coreinit::OSEvent* event)
{
sint32 returnCode = _hidWriteInternalSync(device, data, maxLength);
coreinit::OSSignalEvent(event);
return returnCode;
}
void export_HIDWrite(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(hidHandle, 0); // r3
ppcDefineParamUStr(data, 1); // r4
ppcDefineParamU32(maxLength, 2); // r5
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]);
std::shared_ptr<Device> device = GetDeviceByHandle(hidHandle, true);
if (device == nullptr)
{
cemuLog_log(LogType::Force, "nsyshid.HIDWrite(): Unable to find device with hid handle {}", hidHandle);
osLib_returnFromFunction(hCPU, -1);
return;
}
sint32 returnCode = 0;
if (callbackFuncMPTR != MPTR_NULL)
{
// asynchronous transfer
std::thread(&_hidWriteAsync, device, data, maxLength, callbackFuncMPTR, callbackParamMPTR).detach();
returnCode = 0;
}
else
{
// synchronous transfer
StackAllocator<coreinit::OSEvent> event;
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, &_hidWriteSync, device, data, maxLength, &event);
coreinit::OSWaitEvent(&event);
returnCode = res.get();
}
osLib_returnFromFunction(hCPU, returnCode);
}
void export_HIDDecodeError(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(errorCode, 0);
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]);
// todo
*ukn0 = 0x3FF;
*ukn1 = (uint32)-0x7FFF;
osLib_returnFromFunction(hCPU, 0);
}
void Backend::DetachAllDevices()
{
std::lock_guard<std::recursive_mutex> lock(this->m_devicesMutex);
if (m_isAttached)
{
for (const auto& device : this->m_devices)
{
nsyshid::DetachDevice(device);
}
this->m_devices.clear();
}
}
bool Backend::AttachDevice(const std::shared_ptr<Device>& device)
{
std::lock_guard<std::recursive_mutex> lock(this->m_devicesMutex);
if (m_isAttached && nsyshid::AttachDevice(device))
{
this->m_devices.push_back(device);
return true;
}
return false;
}
void Backend::DetachDevice(const std::shared_ptr<Device>& device)
{
std::lock_guard<std::recursive_mutex> lock(this->m_devicesMutex);
if (m_isAttached)
{
nsyshid::DetachDevice(device);
this->m_devices.remove(device);
}
}
std::shared_ptr<Device> Backend::FindDevice(std::function<bool(const std::shared_ptr<Device>&)> isWantedDevice)
{
std::lock_guard<std::recursive_mutex> lock(this->m_devicesMutex);
auto it = std::find_if(this->m_devices.begin(), this->m_devices.end(), std::move(isWantedDevice));
if (it != this->m_devices.end())
{
return *it;
}
return nullptr;
}
std::shared_ptr<Device> Backend::FindDeviceById(uint16 vendorId, uint16 productId)
{
return nsyshid::FindDeviceById(vendorId, productId);
}
bool Backend::IsDeviceWhitelisted(uint16 vendorId, uint16 productId)
{
return Whitelist::GetInstance().IsDeviceWhitelisted(vendorId, productId);
}
Backend::Backend()
: m_isAttached(false)
{
}
void Backend::OnAttach()
{
std::lock_guard<std::recursive_mutex> lock(this->m_devicesMutex);
m_isAttached = true;
AttachVisibleDevices();
}
void Backend::OnDetach()
{
std::lock_guard<std::recursive_mutex> lock(this->m_devicesMutex);
DetachAllDevices();
m_isAttached = false;
}
bool Backend::IsBackendAttached()
{
std::lock_guard<std::recursive_mutex> lock(this->m_devicesMutex);
return m_isAttached;
}
Device::Device(uint16 vendorId,
uint16 productId,
uint8 interfaceIndex,
uint8 interfaceSubClass,
uint8 protocol)
: m_hid(nullptr),
m_vendorId(vendorId),
m_productId(productId),
m_interfaceIndex(interfaceIndex),
m_interfaceSubClass(interfaceSubClass),
m_protocol(protocol),
m_maxPacketSizeRX(0x20),
m_maxPacketSizeTX(0x20)
{
}
void Device::AssignHID(HID_t* hid)
{
if (hid != nullptr)
{
hid->vendorId = this->m_vendorId;
hid->productId = this->m_productId;
hid->ifIndex = this->m_interfaceIndex;
hid->subClass = this->m_interfaceSubClass;
hid->protocol = this->m_protocol;
hid->ukn04 = 0x11223344;
hid->paddingGuessed0F = 0;
hid->maxPacketSizeRX = this->m_maxPacketSizeRX;
hid->maxPacketSizeTX = this->m_maxPacketSizeTX;
}
this->m_hid = hid;
}
class : public COSModule
{
public:
std::string_view GetName() override
{
return "nsyshid";
}
void RPLMapped() override
{
osLib_addFunction("nsyshid", "HIDAddClient", export_HIDAddClient);
osLib_addFunction("nsyshid", "HIDDelClient", export_HIDDelClient);
osLib_addFunction("nsyshid", "HIDGetDescriptor", export_HIDGetDescriptor);
osLib_addFunction("nsyshid", "HIDSetIdle", export_HIDSetIdle);
osLib_addFunction("nsyshid", "HIDSetProtocol", export_HIDSetProtocol);
osLib_addFunction("nsyshid", "HIDSetReport", export_HIDSetReport);
osLib_addFunction("nsyshid", "HIDRead", export_HIDRead);
osLib_addFunction("nsyshid", "HIDWrite", export_HIDWrite);
osLib_addFunction("nsyshid", "HIDDecodeError", export_HIDDecodeError);
};
void rpl_entry(uint32 moduleHandle, coreinit::RplEntryReason reason) override
{
if (reason == coreinit::RplEntryReason::Loaded)
{
// initialise whitelist
Whitelist::GetInstance();
AttachDefaultBackends();
}
else if (reason == coreinit::RplEntryReason::Unloaded)
{
}
}
}s_COSnsyshidModule;
COSModule* GetModule()
{
return &s_COSnsyshidModule;
}
} // namespace nsyshid