-
Notifications
You must be signed in to change notification settings - Fork 616
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
nsyshid: add backends for cross platform USB passthrough support #950
Conversation
I think this should definitely be enabled for MacOS as well, libUSB works there as well (provided the device you want to use has a driver, otherwise the MacOS kernel claims it unless run with sudo) I can help test on MacOS, and can eventually test the Skylanders Portal and the Disney Infinity Base |
5307dce
to
d8219c3
Compare
The Windows build should be fixed now, at least Cemu builds and starts in my VM. On Windows, both the native Windows HID API backend and the libusb backend seem to work! 🎉 |
#pragma comment(lib, "Setupapi.lib") | ||
#pragma comment(lib, "hid.lib") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Otherwise the linker complains about undefined symbols, like for example HidD_GetAttributes
.
Weirdly enough if I put those #pragma
statements back in nsyshid.cpp
everything links fine, but having them here seems like a cleaner solution.
d8219c3
to
f616c72
Compare
Turns out to build libusb, you need to have The AppImage that was generated by CI works nicely on Steam Deck with the Lego Dimensions portal, provided the udev rules I mentioned above are present :) Something that puzzles me is that the Windows build doesn't need the zadig drivers for the libusb backend to work - at least in my VM. |
f616c72
to
5554c98
Compare
Just tried to build the Flatpak and it fails :( Lines 533 to 537 in dff33b3
with: if (ENABLE_NSYSHID_LIBUSB)
if (ENABLE_VCPKG)
find_package(libusb CONFIG REQUIRED)
target_include_directories(CemuCafe PRIVATE ${LIBUSB_INCLUDE_DIRS})
target_link_libraries(CemuCafe PRIVATE ${LIBUSB_LIBRARIES})
else ()
target_link_libraries(CemuCafe PRIVATE usb-1.0)
endif ()
endif () produces a working Flaptak; although hotplug seems to not work. Would that change be enough to properly support building without vcpkg? |
I suggest using a cmake module to support building without vcpkg. To build without vcpkg on my machine, I created # SPDX-FileCopyrightText: 2022 Andrea Pappacoda <andrea@pappacoda.it>
# SPDX-License-Identifier: ISC
find_package(libusb CONFIG)
if (NOT libusb_FOUND)
find_package(PkgConfig)
if (PKG_CONFIG_FOUND)
pkg_search_module(libusb IMPORTED_TARGET GLOBAL libusb-1.0 libusb)
if (libusb_FOUND)
add_library(libusb::libusb ALIAS PkgConfig::libusb)
endif()
endif()
endif()
find_package_handle_standard_args(libusb
REQUIRED_VARS
libusb_LINK_LIBRARIES
libusb_FOUND
VERSION_VAR libusb_VERSION
) and changed Lines 533 to 537 in dff33b3
if (ENABLE_NSYSHID_LIBUSB)
find_package(libusb MODULE REQUIRED)
target_link_libraries(CemuCafe PRIVATE libusb::libusb)
endif () |
5554c98
to
e38e7ed
Compare
Thanks! Just pushed that cmake change along with some threading fixes (you could call me paranoid). |
e38e7ed
to
6e2b14a
Compare
On the topic of threading: Turns out I was being a bit eager when it comes to locking in nsyshid.cpp, which could in theory result in an application locking up 😅 |
This introduces the concept of backends that implement usb functionality. Backends can be attached / detached at runtime. It is possible for more than one backend to be active at the same time, for example one backend could provide real usb devices, while another provides emulated devices.
This adds a backend for nsyshid, that uses libusb to provide passthrough access to real usb devices.
6e2b14a
to
e79cfe7
Compare
Just rebased on main, ran ClangFormat and tried to stick to the newly added coding style guidelines. |
Looks good to me. Thanks! |
This adds a backend for nsyshid, that uses the Windows HID API to provide passthrough access to real usb devices.
e79cfe7
to
7483c86
Compare
Cool, thanks! |
I think this PR may have broken the Skylanders games on Windows and Mac, maybe linux too. When launching on my Mac, the game instantly crashes, and I am not sure if the compile definitions for libusb have worked because I get no log messages being printed out before the crash. Had a friend test on Windows and that also instantly crashed too |
Ignore me, looks like the bug is only present when compiling from the repo rather than in the release version itself. Looks like the games still crash on windows though |
Welp, that's unfortunate :( |
I haven't been able to test the Skylanders games out (still have no portal with me), but it won't work without an implementation of HIDRead and HIDSetReport. HIDRead needs to use the libusb interrupt transfer, and set report needs to use libusb control transfers |
For what it's worth: The Lego Dimensions portal doesn't seem to care whether bulk transfers or interrupt transfers are used to talk to it; after swapping bool DeviceLibusb::SetReport(uint8* reportData, sint32 length, uint8* originalData, sint32 originalLength)
{
auto handleLock = AquireHandleLock();
if (!handleLock->IsValid())
{
cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::SetReport(): device is not opened");
return false;
}
int ret = 0;
sint32 retryCount = 0;
do
{
ret = libusb_control_transfer(handleLock->GetHandle(),
LIBUSB_RECIPIENT_INTERFACE | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_ENDPOINT_OUT,
LIBUSB_REQUEST_SET_CONFIGURATION,
(LIBUSB_DT_CONFIG << 8) | reportData[0],
0,
reportData,
length,
50);
if (ret != 0)
{
retryCount++;
}
}
while (ret != 0 && retryCount < 20);
if (ret != 0)
{
cemuLog_logDebug(LogType::Force, "nsyshid::DeviceLibusb::SetReport(): control transfer failed with error code: {}", ret);
return false;
}
return true;
} Again, there's no way for me to test that, as I don't own any skylanders games. On a side note, the flatpak built by flathub seems to have working hotplug support, while the one I built myself does not. No idea why that is the case, but if it works, it works ¯\_(ツ)_/¯ |
Yeah, once I get my Portal back I'll try it out with bulk transfers, and try to confirm if that control transfer method works too. Side note, your changes have made it heaps easier for me to add an emulated Skylander Portal and I've got that working, just need to test a few more games before I make a PR :~) |
Hi can i help test this? I have a portal and skylanders games. I tried with the latest appimage but the usb portal isnt detected. Is there something else I need to do? |
Hi @andygrillo, that'd be cool! If you haven't already, you need to create a udev rule to allow Cemu to talk to the portal, as described in the first comment. The implementation is quite noisy when it comes to logging, especially if errors occur. Definitely have a look at lines containing "nsyshid" in This is a shot in the dark, but maybe the code I posted above for |
Hi @ssievert42 thanks for the guidance. I confirm the portal I have, which works with windows cemu, according to lsusb does indeed as you suggest require udev rules with these params:
so I created the udev rule
I ran I took the appimage from the link you provided and booted up skylanders trap team, a game that works with my portal on windows cemu, and for the first time I got confirmation that a portal was connected, just not the right one:
What do you suggest I can try next? |
ok in the logs i see this
am I doing the udev rules incorrectly ? seems that the portal is missing from the list. |
@andygrillo that looks like it worked, but Cemu hasn't completely added support for the Skylander portal yet. The game recognises it as a portal which is the important part! @ssievert42 I believe once the Report methods use control transfers that should fix skylanders. Will look in to getting a portal as soon as I can! |
@andygrillo thanks for trying that out! The udev stuff should be correct - the There should be some more lines in the log that contain "nsyshid". If not, the game doesn't even try to talk to the portal, which would be really strange. Could you please post the output of What might actually work is applying this change on top. Cemu pads the data that is passed to @deReeperJosh I'm actually cooking something that uses control transfers in |
Oh awesome! The only examples off the top of my head that use libusb that you could compare with are RPCS3 and Dolphin, so maybe check there to see if there's anything you can apply here too |
here is the output from
|
I tried your new build @ssievert42 but same issue, cemu says incompatible portal. |
here is the whole log.txt from the recent cemu attempt:
|
just checking I understood this comment @ssievert42 - "for the udev rule to apply the device needs to be plugged in after the rule was created"... I had the portal plugged in when creating the rule, then when enabling the rule with I also recently rebooted the device. |
The
Everything should be fine in that regard, then 👍 I totally forgot that the excessive logging only happens in debug builds. This change also logs a lot of stuff in a release build and might provide some more insight into what is going wrong. (Build here)
This could be one of two things: either the device left, and the game tried to read from it using the now invalid hid handle, or it is impossible to open the device. The build with more logging should help to differentiate between the two. |
here is the full log using the debugging build. please note once I got to the incompatible portal screen I tried disconnecting and reconnecting the portal. |
Hi @ssievert42 , did my log help in anyway ? I wish I could be more help but I don't know how to interact with libusb... |
Hi @andygrillo the log definitely helped! |
thanks very much, unfortuantely this doesn't work. Thanks for the many attempts! |
I think I have made some progress here (in terms of getting the Skylander portal working) - the set protocol method is quite important, because it tells us what interface and configuration to claim via libusb, so what I have done locally is use the SetProtocol method to release all current interfaces, then set the configuration to the one provided in the setprotocol method, then detach kernel drivers and claim all interfaces for that config. When the SetReport method is called, I use the libusb_control_transfer method, and I am now in game with giants! I will keep trying out things, but I am currently hardcoding values to ones I have seen in Dolphin (bmRequestType, bmRequest, wIndex and wValue), but this seems to be working okay for now. |
@andygrillo if you want to help test, I have opened #1027 which should hopefully fix Skylanders on linux |
this should fix a nasty regression, that I introduced as part of 98b5a87 (nsyshid: Add backends for cross platform USB passthrough support (cemu-project#950), 2023-09-19), which could lead to crashes
This introduces the concept of backends for nsyshid that implement USB functionality.
Backends can be attached / detached at runtime. It is possible for more than one backend to be active at the same time, for example one backend could provide real USB devices, while another provides emulated devices.
There are two backends included already: one using libusb and one using the native Windows HID API.
Note that I am not experienced at all when it comes to working with USB devices or libusb. Feedback and suggestions how to improve this are very welcome :)
This also adds a whitelist feature, to only expose devices that the emulated Wii U needs to know about. That whitelist stuff could absolutely be expanded to be user configurable, but for now the whitelist is just populated with some known devices.
The libusb backend works nicely on Linux with the Lego Dimensions portal :)
That is the only configuration I can test though.
Libusb is fetched with vcpkg and statically linked into the final binary - hopefully that isn't an issue, but it seems like mentioning libusb in the about dialog (where it automagically appears), should suffice.
On macOS the libusb backend is compiled in by default and may or may not work.
Currently there are still unimplemented methods in the libusb backend:
setProtocol
andsetReport
, but the Lego Dimensions portal seems to work fine without them. And I have no idea how they could be implemented.One neat thing that is included in the libusb backend as well is hotplug support - having to restart the game because of a flaky USB connection drove me mad enough to add this.
For hotplug support there needs to be someone that regularily calls libusb_handle_events_completed. I did this with a separate thread. Sadly it seems like libusb doesn't support hotplug on Windows.
On Linux, to be able to use a device, one needs to add some udev rules. Running
lsusb
spits out all connected devices as well as their vendorId and productId. For example to use the Lego Dimensions portal, you'd need to create a file /etc/udev/rules.d/99-lego-dimensions-portal.rules with the following contents:The Windows HID backend is entirely untested, but should ideally work 🤞
Those backends can be enabled / disabled with the CMake flags:
ENABLE_NSYSHID_LIBUSB
- enable libusb backend; default "on" and not specifiable if the Windows HID backend is enabledENABLE_NSYSHID_WINDOWS_HID
- enable Windows HID backend; default "on" on WindowsSo to use the libusb backend on Windows you'd need to pass
-DENABLE_NSYSHID_WINDOWS_HID=OFF -DENABLE_NSYSHID_LIBUSB=ON
to cmake. (Only disabling the Windows HID backend is enough as well.)Ideally closes #275, but, as I said above, I can only test this myself with the Lego Dimensions portal on Linux.
Supersedes #946
@deReeperJosh what do you think, could you use this as a base for emulated devices? And should the libusb backend even be enabled on macOS?
Things that should be done before this can be merged, that I can't do myself (any help is greatly appreciated!):