Skip to content

Commit

Permalink
nsyshid: Add SetProtocol and SetReport support for libusb backend (ce…
Browse files Browse the repository at this point in the history
  • Loading branch information
deReeperJosh authored Jul 2, 2024
1 parent 64b0b85 commit 5209677
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 54 deletions.
2 changes: 1 addition & 1 deletion src/Cafe/OS/libs/nsyshid/Backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ namespace nsyshid
uint8* output,
uint32 outputMaxLength) = 0;

virtual bool SetProtocol(uint32 ifIndef, uint32 protocol) = 0;
virtual bool SetProtocol(uint8 ifIndex, uint8 protocol) = 0;

virtual bool SetReport(ReportMessage* message) = 0;
};
Expand Down
163 changes: 118 additions & 45 deletions src/Cafe/OS/libs/nsyshid/BackendLibusb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,17 @@ namespace nsyshid::backend::libusb
return nullptr;
}

std::pair<int, ConfigDescriptor> MakeConfigDescriptor(libusb_device* device, uint8 config_num)
{
libusb_config_descriptor* descriptor = nullptr;
const int ret = libusb_get_config_descriptor(device, config_num, &descriptor);
if (ret == LIBUSB_SUCCESS)
return {ret, ConfigDescriptor{descriptor, libusb_free_config_descriptor}};

return {ret, ConfigDescriptor{nullptr, [](auto) {
}}};
}

std::shared_ptr<Device> BackendLibusb::CheckAndCreateDevice(libusb_device* dev)
{
struct libusb_device_descriptor desc;
Expand All @@ -241,14 +252,34 @@ namespace nsyshid::backend::libusb
ret);
return nullptr;
}
std::vector<ConfigDescriptor> config_descriptors{};
for (uint8 i = 0; i < desc.bNumConfigurations; ++i)
{
auto [ret, config_descriptor] = MakeConfigDescriptor(dev, i);
if (ret != LIBUSB_SUCCESS || !config_descriptor)
{
cemuLog_log(LogType::Force, "Failed to make config descriptor {} for {:04x}:{:04x}: {}",
i, desc.idVendor, desc.idProduct, libusb_error_name(ret));
}
else
{
config_descriptors.emplace_back(std::move(config_descriptor));
}
}
if (desc.idVendor == 0x0e6f && desc.idProduct == 0x0241)
{
cemuLog_logDebug(LogType::Force,
"nsyshid::BackendLibusb::CheckAndCreateDevice(): lego dimensions portal detected");
}
auto device = std::make_shared<DeviceLibusb>(m_ctx,
desc.idVendor,
desc.idProduct,
1,
2,
0,
libusb_get_bus_number(dev),
libusb_get_device_address(dev));
libusb_get_device_address(dev),
std::move(config_descriptors));
// figure out device endpoints
if (!FindDefaultDeviceEndpoints(dev,
device->m_libusbHasEndpointIn,
Expand Down Expand Up @@ -330,7 +361,8 @@ namespace nsyshid::backend::libusb
uint8 interfaceSubClass,
uint8 protocol,
uint8 libusbBusNumber,
uint8 libusbDeviceAddress)
uint8 libusbDeviceAddress,
std::vector<ConfigDescriptor> configs)
: Device(vendorId,
productId,
interfaceIndex,
Expand All @@ -346,6 +378,7 @@ namespace nsyshid::backend::libusb
m_libusbHasEndpointOut(false),
m_libusbEndpointOut(0)
{
m_config_descriptors = std::move(configs);
}

DeviceLibusb::~DeviceLibusb()
Expand Down Expand Up @@ -413,20 +446,8 @@ namespace nsyshid::backend::libusb
}
this->m_handleInUseCounter = 0;
}
if (libusb_kernel_driver_active(this->m_libusbHandle, 0) == 1)
{
cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::open(): kernel driver active");
if (libusb_detach_kernel_driver(this->m_libusbHandle, 0) == 0)
{
cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::open(): kernel driver detached");
}
else
{
cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::open(): failed to detach kernel driver");
}
}
{
int ret = libusb_claim_interface(this->m_libusbHandle, 0);
int ret = ClaimAllInterfaces(0);
if (ret != 0)
{
cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::open(): cannot claim interface");
Expand Down Expand Up @@ -680,32 +701,84 @@ namespace nsyshid::backend::libusb
return false;
}

bool DeviceLibusb::SetProtocol(uint32 ifIndex, uint32 protocol)
template<typename Configs, typename Function>
static int DoForEachInterface(const Configs& configs, uint8 config_num, Function action)
{
int ret = LIBUSB_ERROR_NOT_FOUND;
if (configs.size() <= config_num || !configs[config_num])
return ret;
for (uint8 i = 0; i < configs[config_num]->bNumInterfaces; ++i)
{
ret = action(i);
if (ret < LIBUSB_SUCCESS)
break;
}
return ret;
}

int DeviceLibusb::ClaimAllInterfaces(uint8 config_num)
{
const int ret = DoForEachInterface(m_config_descriptors, config_num, [this](uint8 i) {
if (libusb_kernel_driver_active(this->m_libusbHandle, i))
{
const int ret2 = libusb_detach_kernel_driver(this->m_libusbHandle, i);
if (ret2 < LIBUSB_SUCCESS && ret2 != LIBUSB_ERROR_NOT_FOUND &&
ret2 != LIBUSB_ERROR_NOT_SUPPORTED)
{
cemuLog_log(LogType::Force, "Failed to detach kernel driver {}", libusb_error_name(ret2));
return ret2;
}
}
return libusb_claim_interface(this->m_libusbHandle, i);
});
if (ret < LIBUSB_SUCCESS)
{
cemuLog_log(LogType::Force, "Failed to release all interfaces for config {}", config_num);
}
return ret;
}

int DeviceLibusb::ReleaseAllInterfaces(uint8 config_num)
{
const int ret = DoForEachInterface(m_config_descriptors, config_num, [this](uint8 i) {
return libusb_release_interface(AquireHandleLock()->GetHandle(), i);
});
if (ret < LIBUSB_SUCCESS && ret != LIBUSB_ERROR_NO_DEVICE && ret != LIBUSB_ERROR_NOT_FOUND)
{
cemuLog_log(LogType::Force, "Failed to release all interfaces for config {}", config_num);
}
return ret;
}

int DeviceLibusb::ReleaseAllInterfacesForCurrentConfig()
{
int config_num;
const int get_config_ret = libusb_get_configuration(AquireHandleLock()->GetHandle(), &config_num);
if (get_config_ret < LIBUSB_SUCCESS)
return get_config_ret;
return ReleaseAllInterfaces(config_num);
}

bool DeviceLibusb::SetProtocol(uint8 ifIndex, uint8 protocol)
{
auto handleLock = AquireHandleLock();
if (!handleLock->IsValid())
{
cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::SetProtocol(): device is not opened");
return false;
}
if (m_interfaceIndex != ifIndex)
m_interfaceIndex = ifIndex;

// ToDo: implement this
#if 0
// is this correct? Discarding "ifIndex" seems like a bad idea
int ret = libusb_set_configuration(handleLock->getHandle(), protocol);
if (ret == 0) {
cemuLog_logDebug(LogType::Force,
"nsyshid::DeviceLibusb::setProtocol(): success");
ReleaseAllInterfacesForCurrentConfig();
int ret = libusb_set_configuration(AquireHandleLock()->GetHandle(), protocol);
if (ret == LIBUSB_SUCCESS)
ret = ClaimAllInterfaces(protocol);

if (ret == LIBUSB_SUCCESS)
return true;
}
cemuLog_logDebug(LogType::Force,
"nsyshid::DeviceLibusb::setProtocol(): failed with error code: {}",
ret);
return false;
#endif

// pretend that everything is fine
return true;
return false;
}

bool DeviceLibusb::SetReport(ReportMessage* message)
Expand All @@ -717,20 +790,20 @@ namespace nsyshid::backend::libusb
return false;
}

// ToDo: implement this
#if 0
// not sure if libusb_control_transfer() is the right candidate for this
int ret = libusb_control_transfer(handleLock->getHandle(),
bmRequestType,
bRequest,
wValue,
wIndex,
message->reportData,
message->length,
timeout);
#endif

// pretend that everything is fine
int ret = libusb_control_transfer(handleLock->GetHandle(),
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT,
LIBUSB_REQUEST_SET_CONFIGURATION,
512,
0,
message->originalData,
message->originalLength,
0);

if (ret != message->originalLength)
{
cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::SetReport(): Control Transfer Failed: {}", libusb_error_name(ret));
return false;
}
return true;
}

Expand Down
15 changes: 13 additions & 2 deletions src/Cafe/OS/libs/nsyshid/BackendLibusb.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ namespace nsyshid::backend::libusb
bool& endpointOutFound, uint8& endpointOut, uint16& endpointOutMaxPacketSize);
};

template<typename T>
using UniquePtr = std::unique_ptr<T, void (*)(T*)>;

using ConfigDescriptor = UniquePtr<libusb_config_descriptor>;

class DeviceLibusb : public nsyshid::Device {
public:
DeviceLibusb(libusb_context* ctx,
Expand All @@ -53,7 +58,8 @@ namespace nsyshid::backend::libusb
uint8 interfaceSubClass,
uint8 protocol,
uint8 libusbBusNumber,
uint8 libusbDeviceAddress);
uint8 libusbDeviceAddress,
std::vector<ConfigDescriptor> configs);

~DeviceLibusb() override;

Expand All @@ -73,7 +79,11 @@ namespace nsyshid::backend::libusb
uint8* output,
uint32 outputMaxLength) override;

bool SetProtocol(uint32 ifIndex, uint32 protocol) override;
bool SetProtocol(uint8 ifIndex, uint8 protocol) override;

int ClaimAllInterfaces(uint8 config_num);
int ReleaseAllInterfaces(uint8 config_num);
int ReleaseAllInterfacesForCurrentConfig();

bool SetReport(ReportMessage* message) override;

Expand All @@ -92,6 +102,7 @@ namespace nsyshid::backend::libusb
std::atomic<sint32> m_handleInUseCounter;
std::condition_variable m_handleInUseCounterDecremented;
libusb_device_handle* m_libusbHandle;
std::vector<ConfigDescriptor> m_config_descriptors;

class HandleLock {
public:
Expand Down
2 changes: 1 addition & 1 deletion src/Cafe/OS/libs/nsyshid/BackendWindowsHID.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ namespace nsyshid::backend::windows
return false;
}

bool DeviceWindowsHID::SetProtocol(uint32 ifIndef, uint32 protocol)
bool DeviceWindowsHID::SetProtocol(uint8 ifIndex, uint8 protocol)
{
// ToDo: implement this
// pretend that everything is fine
Expand Down
2 changes: 1 addition & 1 deletion src/Cafe/OS/libs/nsyshid/BackendWindowsHID.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ namespace nsyshid::backend::windows

bool GetDescriptor(uint8 descType, uint8 descIndex, uint8 lang, uint8* output, uint32 outputMaxLength) override;

bool SetProtocol(uint32 ifIndef, uint32 protocol) override;
bool SetProtocol(uint8 ifIndex, uint8 protocol) override;

bool SetReport(ReportMessage* message) override;

Expand Down
2 changes: 1 addition & 1 deletion src/Cafe/OS/libs/nsyshid/Skylander.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,7 @@ namespace nsyshid
return true;
}

bool SkylanderPortalDevice::SetProtocol(uint32 ifIndex, uint32 protocol)
bool SkylanderPortalDevice::SetProtocol(uint8 ifIndex, uint8 protocol)
{
return true;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Cafe/OS/libs/nsyshid/Skylander.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ namespace nsyshid
uint8* output,
uint32 outputMaxLength) override;

bool SetProtocol(uint32 ifIndex, uint32 protocol) override;
bool SetProtocol(uint8 ifIndex, uint8 protocol) override;

bool SetReport(ReportMessage* message) override;

Expand Down
4 changes: 2 additions & 2 deletions src/Cafe/OS/libs/nsyshid/nsyshid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -379,8 +379,8 @@ namespace nsyshid
void export_HIDSetProtocol(PPCInterpreter_t* hCPU)
{
ppcDefineParamU32(hidHandle, 0); // r3
ppcDefineParamU32(ifIndex, 1); // r4
ppcDefineParamU32(protocol, 2); // r5
ppcDefineParamU8(ifIndex, 1); // r4
ppcDefineParamU8(protocol, 2); // r5
ppcDefineParamMPTR(callbackFuncMPTR, 3); // r6
ppcDefineParamMPTR(callbackParamMPTR, 4); // r7
cemuLog_logDebug(LogType::Force, "nsyshid.HIDSetProtocol(...)");
Expand Down

0 comments on commit 5209677

Please sign in to comment.