Skip to content
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

Merged
merged 3 commits into from Sep 18, 2023

Conversation

ssievert42
Copy link
Contributor

@ssievert42 ssievert42 commented Aug 26, 2023

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 and setReport, 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:

# allow applications to talk to the lego dimensions portal
# place this file in '/etc/udev/rules.d/'
# after that you need to reboot or run 'sudo udevadm control --reload' and replug the device for the rules to apply
SUBSYSTEM=="usb", ATTRS{idVendor}=="0e6f", ATTRS{idProduct}=="0241", MODE="0666"

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 enabled
  • ENABLE_NSYSHID_WINDOWS_HID - enable Windows HID backend; default "on" on Windows

So 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!):

  • test with more devices (do we need to add more to the whitelist?)
  • test on Windows with the Windows HID backend (there shouldn't be a noticable difference to before)
  • test on Windows with the libusb backend (may require the zadig drivers as mentioned here)
  • test on macOS

@ssievert42 ssievert42 marked this pull request as draft August 26, 2023 18:39
@ssievert42 ssievert42 marked this pull request as ready for review August 26, 2023 18:40
@deReeperJosh
Copy link
Contributor

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

@ssievert42 ssievert42 force-pushed the nsyshid_backends branch 5 times, most recently from 5307dce to d8219c3 Compare August 29, 2023 19:02
@ssievert42
Copy link
Contributor Author

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! 🎉
At least the Lego Dimensions portal lights up when using either of them, before Cemu crashes due to running in a VM with no proper OpenGL support.
The macOS build on the other hand... CI seems to fail somewhere in the process of vcpkg pulling in libusb and trying to build it, but without the logs that were written to some file on the runner (more specifically /Users/runner/work/Cemu/Cemu/dependencies/vcpkg/buildtrees/libusb/autoconf-x64-osx-err.log) it's really hard to tell where. I've disabled the libusb backend on macOS and made libusb only a dependency if we are not on osx for now.

Comment on lines +6 to +7
#pragma comment(lib, "Setupapi.lib")
#pragma comment(lib, "hid.lib")
Copy link
Contributor Author

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.

@ssievert42
Copy link
Contributor Author

Turns out to build libusb, you need to have autoconf automake libtool and m4 installed. Adding automake and libtool (which depend on autoconf and m4 respectively) allows the macOS build to succeed: https://github.com/ssievert42/Cemu/actions/runs/6035539753.

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.

@ssievert42
Copy link
Contributor Author

ssievert42 commented Sep 2, 2023

Just tried to build the Flatpak and it fails :(
Since the Flatpak doesn't use vcpkg there is no libusb package that CMake can find - replacing:

if (ENABLE_NSYSHID_LIBUSB)
find_package(libusb CONFIG REQUIRED)
target_include_directories(CemuCafe PRIVATE ${LIBUSB_INCLUDE_DIRS})
target_link_libraries(CemuCafe PRIVATE ${LIBUSB_LIBRARIES})
endif ()

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?

@capitalistspz
Copy link
Contributor

capitalistspz commented Sep 4, 2023

Just tried to build the Flatpak and it fails :( Since the Flatpak doesn't use vcpkg there is no libusb package that CMake can find - replacing:

I suggest using a cmake module to support building without vcpkg. To build without vcpkg on my machine, I created Findlibusb.cmake in the cmake folder

# 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

if (ENABLE_NSYSHID_LIBUSB)
find_package(libusb CONFIG REQUIRED)
target_include_directories(CemuCafe PRIVATE ${LIBUSB_INCLUDE_DIRS})
target_link_libraries(CemuCafe PRIVATE ${LIBUSB_LIBRARIES})
endif ()
to

if (ENABLE_NSYSHID_LIBUSB)
	find_package(libusb MODULE REQUIRED)
	target_link_libraries(CemuCafe PRIVATE libusb::libusb)
endif ()

@ssievert42
Copy link
Contributor Author

Thanks!
That looks a lot cleaner, and I like that it fails early at the cmake stage if libusb is not installed.
But I had to keep the if (ENABLE_VCPKG) ..., because otherwise building with vcpkg doesn't work on my computer.

Just pushed that cmake change along with some threading fixes (you could call me paranoid).

@ssievert42
Copy link
Contributor Author

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 😅
I redid that locking stuff, and tried to keep the sections where a mutex is held as short and simple as possible.

@ssievert42 ssievert42 changed the title nsyshid: add backends for cross platform support nsyshid: add backends for cross platform USB passthrough support Sep 8, 2023
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.
@ssievert42
Copy link
Contributor Author

Just rebased on main, ran ClangFormat and tried to stick to the newly added coding style guidelines.

@Exzap
Copy link
Contributor

Exzap commented Sep 18, 2023

Looks good to me. Thanks!
If you need help fixing the Windows compilation error let me know. After resolving it we can merge this.

This adds a backend for nsyshid, that uses the Windows HID API to provide passthrough access to real usb devices.
@ssievert42
Copy link
Contributor Author

Cool, thanks!
The Windows build should be fixed now :)

@Exzap Exzap merged commit 98b5a87 into cemu-project:main Sep 18, 2023
5 checks passed
@deReeperJosh
Copy link
Contributor

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

@deReeperJosh
Copy link
Contributor

deReeperJosh commented Sep 19, 2023

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

@ssievert42
Copy link
Contributor Author

Welp, that's unfortunate :(
Maybe a clean rebuild could do the trick in your case then?
So with the release version, the Skylanders portal now works on macOS?
On Windows there shouldn't be a noticable difference to before these changes and after, but I guess I must have messed something up along the way when copy pasting the Windows HID code to the BackendWindowsHID class.

@deReeperJosh
Copy link
Contributor

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

@ssievert42
Copy link
Contributor Author

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 libusb_bulk_transfer for libusb_interrupt_transfer in the Read and Write methods, it still functions correctly.
Maybe something like the code below could be used for SetReport?

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 ¯\_(ツ)_/¯

@deReeperJosh
Copy link
Contributor

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 :~)

@andygrillo
Copy link

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?

@ssievert42
Copy link
Contributor Author

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.
Probably something like this for Skylanders:
SUBSYSTEM=="usb", ATTRS{idVendor}=="1430", ATTRS{idProduct}=="0150", MODE="0666"

The implementation is quite noisy when it comes to logging, especially if errors occur. Definitely have a look at lines containing "nsyshid" in log.txt.

This is a shot in the dark, but maybe the code I posted above for SetReport is enough to make the portal work (if it currently doesn't). If you don't want to compile yourself, you could use the AppImage generated here. (And check the commits that I added to my main branch beforehand, to make sure that I'm not including anything shady. (I obviously didn't include any shady stuff (as far as I know), but I just don't want to normalize people suggesting to download and run random executables.))

@andygrillo
Copy link

andygrillo commented Sep 22, 2023

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:

Bus 001 Device 006: ID 1430:0150 RedOctane wireless receiver for skylanders wii

so I created the udev rule 51-gcadapter.rules as follows:

SUBSYSTEM=="usb", ATTRS{idVendor}=="1430", ATTRS{idProduct}=="0150", MODE="0666"

I ran sudo udevadm control --reload then to enable the rule.

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:

image
cemu recognizes when I unplug it as it then says no portal connected.

What do you suggest I can try next?

@andygrillo
Copy link

andygrillo commented Sep 22, 2023

ok in the logs i see this

[22:13:22.690] nsyshid::BackendLibusb: device not on whitelist: 1d6b:0003
[22:13:22.691] nsyshid::BackendLibusb: device not on whitelist: 1d6b:0002
[22:13:22.691] nsyshid::BackendLibusb: device not on whitelist: 1d6b:0003
[22:13:22.691] nsyshid::BackendLibusb: device not on whitelist: 8087:0aa7
[22:13:22.691] nsyshid::BackendLibusb: device not on whitelist: 048d:5702
[22:13:22.691] nsyshid::BackendLibusb: device not on whitelist: 046d:c52b
[22:13:22.691] nsyshid::BackendLibusb: device not on whitelist: 1d6b:0002

am I doing the udev rules incorrectly ? seems that the portal is missing from the list.

@deReeperJosh
Copy link
Contributor

@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!

@ssievert42
Copy link
Contributor Author

@andygrillo thanks for trying that out!

The udev stuff should be correct - the device not on whitelist bit in the logs is just telling you about other devices that are connected to your computer, but not whitelisted by Cemu. If the game sees a device, that means that it is on the whitelist 👍
I guess you have unplugged and replugged the portal, since you added the udev rule, a bunch of times by now, but for the udev rule to apply the device needs to be plugged in after the rule was created. I lost at least an hour to that when hacking on this PR.

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 lsusb -v -d 1430:0150 while the skylanders portal is plugged in? That should list all descriptors of the device. My suspicion is, that my logic for figuring out from which endpoint can be read, and to which endpoint can be written, might be wrong.

What might actually work is applying this change on top. Cemu pads the data that is passed to SetReport and then should be sent to the portal; that change switches to using the original, unpadded data. Build here.

@deReeperJosh I'm actually cooking something that uses control transfers in SetReport on my main branch (seemed like the easiest way to trigger actions runs), but I'm not sure at all that the way I'm doing that is correct 😅

@deReeperJosh
Copy link
Contributor

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

@andygrillo
Copy link

here is the output from lsusb -v -d 1430:

[gamer@chimeraos ~]$ lsusb -v -d 1430:0150

Bus 001 Device 006: ID 1430:0150 RedOctane wireless receiver for skylanders wii
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0        64
  idVendor           0x1430 RedOctane
  idProduct          0x0150 wireless receiver for skylanders wii
  bcdDevice            1.00
  iManufacturer           1 Activision
  iProduct                2 Spyro Porta
  iSerial                 0 
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength       0x0029
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              500mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0 
      bInterfaceProtocol      0 
      iInterface              0 
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.11
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      29
         Report Descriptors: 
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x02  EP 2 OUT
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1
Device Status:     0x0000
  (Bus Powered)

@andygrillo
Copy link

I tried your new build @ssievert42 but same issue, cemu says incompatible portal.

@andygrillo
Copy link

here is the whole log.txt from the recent cemu attempt:

[12:08:19.915] ------- Init Cemu 2.0-cee96af (experimental) -------
[12:08:19.916] Init Wii U memory space (base: 0x00007f66b8000000)
[12:08:19.918] mlc01 path: /home/gamer/.local/share/Cemu/mlc01
[12:08:19.918] CPU: AMD Ryzen 5 5600X 6-Core Processor             
[12:08:19.918] RAM: 15913MB
[12:08:19.918] Platform: Linux (AppImage)
[12:08:19.918] Used CPU extensions: SSSE3, SSE4.1, AVX, AVX2, LZCNT, MOVBE, BMI2, AES-NI, INVARIANT-TSC
[12:08:19.921] IOSU_CRYPTO: No otp.bin found. Online mode cannot be used
[12:08:19.921] IOSU_CRYPTO: No Seeprom.bin found. Online mode cannot be used
[12:08:19.932] Failed to read valid PlayDiary header
[12:08:21.284] Mounting title 000500001017c600
[12:08:21.284] Base: /run/media/sda1/ROMS/Wii U/Skylanders Trap Team [Game] [000500001017c600] [Folder]
[12:08:21.284] Update: Not present
[12:08:21.284] DLC: Not present
[12:08:21.323] COS: System fonts found. Generated shareddata (26609KB)
[12:08:21.329] Recompiler initialized
[12:08:21.509] ------- Init Vulkan graphics backend -------
[12:08:21.528] Vulkan instance version: 1.3
[12:08:21.533] Using GPU: AMD Radeon RX 6600 (RADV NAVI23)
[12:08:21.533] Driver version: Mesa 23.1.3
[12:08:21.533] VulkanLimits: UBAlignment 4 nonCoherentAtomSize 64
[12:08:21.540] Using available debug function: vkCreateDebugUtilsMessengerEXT()
[12:08:21.540] Vulkan device memory info:
[12:08:21.540] Heap 0 - Size 7936MB Flags 0x00000001
[12:08:21.540] Heap 1 - Size 7956MB Flags 0x00000000
[12:08:21.540] Heap 2 - Size 256MB Flags 0x00000001
[12:08:21.540] Memory 0 - HeapIndex 0 Flags 0x00000001
[12:08:21.540] Memory 1 - HeapIndex 0 Flags 0x00000001
[12:08:21.540] Memory 2 - HeapIndex 1 Flags 0x00000006
[12:08:21.540] Memory 3 - HeapIndex 2 Flags 0x00000007
[12:08:21.540] Memory 4 - HeapIndex 2 Flags 0x00000007
[12:08:21.540] Memory 5 - HeapIndex 1 Flags 0x0000000e
[12:08:21.540] Memory 6 - HeapIndex 1 Flags 0x0000000e
[12:08:21.540] Memory 7 - HeapIndex 0 Flags 0x000000c1
[12:08:21.540] Memory 8 - HeapIndex 1 Flags 0x000000c6
[12:08:21.540] Memory 9 - HeapIndex 2 Flags 0x000000c7
[12:08:21.540] Memory 10 - HeapIndex 1 Flags 0x000000ce
[12:08:21.540] VK_FORMAT_D24_UNORM_S8_UINT not supported
[12:08:21.540] VK_FORMAT_R4G4_UNORM_PACK8 not supported
[12:08:22.569] ------- Loaded title -------
[12:08:22.569] TitleId: 00050000-1017c600
[12:08:22.569] TitleVersion: v1
[12:08:22.569] TitleRegion: US
[12:08:22.569] Save path:   /home/gamer/.local/share/Cemu/mlc01/usr/save/00050000/1017C600/user/ (not present)
[12:08:22.569] Shader cache file: shaderCache/transferable/000500001017c600.bin
[12:08:22.569] gameprofile path: gameProfiles/000500001017c600.ini
[12:08:22.569] RPX hash (updated): 24e28517
[12:08:22.569] RPX hash (base): 24e28517
[12:08:22.576] nsyshid::BackendLibusb: device not on whitelist: 1d6b:0003
[12:08:22.576] nsyshid::BackendLibusb: device not on whitelist: 1d6b:0002
[12:08:22.577] nsyshid::BackendLibusb: device not on whitelist: 1d6b:0003
[12:08:22.577] nsyshid::BackendLibusb: device not on whitelist: 8087:0aa7
[12:08:22.577] nsyshid::BackendLibusb: device not on whitelist: 048d:5702
[12:08:22.577] nsyshid::BackendLibusb: device not on whitelist: 046d:c52b
[12:08:22.577] nsyshid::BackendLibusb: device not on whitelist: 1d6b:0002
[12:08:22.616] Loaded module 'tfbgame_cafe' with checksum 0x321d97f0
[12:08:22.616] RPL link time: 39ms
[12:08:22.638] HLE scan time: 22ms
[12:08:22.638] ------- Active settings -------
[12:08:22.638] CPU-Mode: Multi-core recompiler (gameprofile)
[12:08:22.638] Load shared libraries: true (gameprofile)
[12:08:22.638] Use precompiled shaders: auto (gameprofile)
[12:08:22.638] Full sync at GX2DrawDone: true
[12:08:22.638] Strict shader mul: true
[12:08:22.638] Async compile: true
[12:08:22.638] Console language: English
[12:08:22.720] ------- Activate graphic packs -------
[12:08:22.720] Set vsync frequency to 120 (graphic pack Skylanders Trap Team/Mods/FPS)
[12:08:22.720] Activate graphic pack: Skylanders Trap Team/Mods/FPS [Presets: 60 FPS]
[12:08:22.722] Activate graphic pack: Skylanders Trap Team/Graphics [Presets: 1920x1080,Medium (100%, Default)]
[12:08:22.722] ------- Init Audio backend -------
[12:08:22.722] DirectSound: not supported
[12:08:22.722] XAudio 2.8: not supported
[12:08:22.722] XAudio 2.7: not supported
[12:08:22.722] Cubeb: available
[12:08:22.722] ------- Init Audio input backend -------
[12:08:22.722] Cubeb: available
[12:08:22.722] ------- Run title -------
[12:08:22.852] IOSU_ACT: using account default in first slot
[12:08:22.947] Game attempting to re-initialize existing thread
[12:08:22.947] Calling OSCreateThread() on thread which is still active (Thread exited without detached flag). Forcing OSDetachThread()...
[12:09:07.022] nsyshid.HIDRead(): Unable to find device with hid handle 2```

@andygrillo
Copy link

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 sudo udevadm control --reload, then unplugged and replugged before starting the game.

I also recently rebooted the device.

@ssievert42
Copy link
Contributor Author

The lsusb output looks pretty similar to the Lego Dimensions portal, and since the device only has two endpoints and one configuration, my default endpoint discovery logic shouldn't be at fault here.

I had the portal plugged in when creating the rule, then when enabling the rule with sudo udevadm control --reload, then unplugged and replugged before starting the game.

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)

[12:09:07.022] nsyshid.HIDRead(): Unable to find device with hid handle 2

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.
Weirdly enough, I could only get the Unable to find device with hid handle ... message with Lego Dimensions, when I removed my udev rule file /etc/udev/rules.d/99-lego-dimensions-portal.rules.

@andygrillo
Copy link

andygrillo commented Sep 23, 2023

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.
log.txt

@andygrillo
Copy link

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...

@ssievert42
Copy link
Contributor Author

Hi @andygrillo the log definitely helped!
That makes two of us :)
Reading data from the portal seems to work, but writing does not:
nsyshid::DeviceLibusb::SetReport(): control transfer failed with error code: 2
Since error code 2 means LIBUSB_ERROR_INVALID_PARAM I must have supplied an invalid parameter to libusb_control_transfer. I'm honestly starting to run out of ideas, and trying to develop for a device without having access to such a device is just no fun.
RPCS3 and Dolphin seem to use libusb's async api, but from a quick glance I can't figure out what the equivalent arguments for the synchronous api would be.
There is however a project that manages to talk to a Skylanders portal only using libusb_interrupt_transfer like this.
If this change (build here) doesn't work, then I guess it's up to someone other than me to get the Skylanders portal to work.

@andygrillo
Copy link

thanks very much, unfortuantely this doesn't work. Thanks for the many attempts!

@deReeperJosh
Copy link
Contributor

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.

@deReeperJosh
Copy link
Contributor

@andygrillo if you want to help test, I have opened #1027 which should hopefully fix Skylanders on linux

ssievert42 added a commit to ssievert42/Cemu that referenced this pull request Mar 6, 2024
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Linux] [Skylanders] USB Support for Portal of Power
5 participants