Skip to content

Commit 98b5a87

Browse files
authored
nsyshid: Add backends for cross platform USB passthrough support (#950)
1 parent 2a735f1 commit 98b5a87

17 files changed

+2285
-511
lines changed

.github/workflows/build.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ jobs:
232232
- name: "Install system dependencies"
233233
run: |
234234
brew update
235-
brew install llvm@15 ninja nasm molten-vk
235+
brew install llvm@15 ninja nasm molten-vk automake libtool
236236
237237
- name: "Bootstrap vcpkg"
238238
run: |

BUILD.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ You can skip this section if you have an Intel Mac. Every time you compile, you
8686

8787
### Installing dependencies
8888

89-
`brew install boost git cmake llvm ninja nasm molten-vk`
89+
`brew install boost git cmake llvm ninja nasm molten-vk automake libtool`
9090

9191
### Build Cemu using cmake and clang
9292
1. `git clone --recursive https://github.com/cemu-project/Cemu`

CMakeLists.txt

+17
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,23 @@ if (WIN32)
102102
endif()
103103
option(ENABLE_CUBEB "Enabled cubeb backend" ON)
104104

105+
# usb hid backends
106+
if (WIN32)
107+
option(ENABLE_NSYSHID_WINDOWS_HID "Enables the native Windows HID backend for nsyshid" ON)
108+
endif ()
109+
# libusb and windows hid backends shouldn't be active at the same time; otherwise we'd see all devices twice!
110+
if (NOT ENABLE_NSYSHID_WINDOWS_HID)
111+
option(ENABLE_NSYSHID_LIBUSB "Enables the libusb backend for nsyshid" ON)
112+
else ()
113+
set(ENABLE_NSYSHID_LIBUSB OFF CACHE BOOL "" FORCE)
114+
endif ()
115+
if (ENABLE_NSYSHID_WINDOWS_HID)
116+
add_compile_definitions(NSYSHID_ENABLE_BACKEND_WINDOWS_HID)
117+
endif ()
118+
if (ENABLE_NSYSHID_LIBUSB)
119+
add_compile_definitions(NSYSHID_ENABLE_BACKEND_LIBUSB)
120+
endif ()
121+
105122
option(ENABLE_WXWIDGETS "Build with wxWidgets UI (Currently required)" ON)
106123

107124
set(THREADS_PREFER_PTHREAD_FLAG true)

cmake/Findlibusb.cmake

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# SPDX-FileCopyrightText: 2022 Andrea Pappacoda <[email protected]>
2+
# SPDX-License-Identifier: ISC
3+
4+
find_package(libusb CONFIG)
5+
if (NOT libusb_FOUND)
6+
find_package(PkgConfig)
7+
if (PKG_CONFIG_FOUND)
8+
pkg_search_module(libusb IMPORTED_TARGET GLOBAL libusb-1.0 libusb)
9+
if (libusb_FOUND)
10+
add_library(libusb::libusb ALIAS PkgConfig::libusb)
11+
endif ()
12+
endif ()
13+
endif ()
14+
15+
find_package_handle_standard_args(libusb
16+
REQUIRED_VARS
17+
libusb_LINK_LIBRARIES
18+
libusb_FOUND
19+
VERSION_VAR libusb_VERSION
20+
)

src/Cafe/CMakeLists.txt

+19
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,14 @@ add_library(CemuCafe
434434
OS/libs/nn_uds/nn_uds.h
435435
OS/libs/nsyshid/nsyshid.cpp
436436
OS/libs/nsyshid/nsyshid.h
437+
OS/libs/nsyshid/Backend.h
438+
OS/libs/nsyshid/AttachDefaultBackends.cpp
439+
OS/libs/nsyshid/Whitelist.cpp
440+
OS/libs/nsyshid/Whitelist.h
441+
OS/libs/nsyshid/BackendLibusb.cpp
442+
OS/libs/nsyshid/BackendLibusb.h
443+
OS/libs/nsyshid/BackendWindowsHID.cpp
444+
OS/libs/nsyshid/BackendWindowsHID.h
437445
OS/libs/nsyskbd/nsyskbd.cpp
438446
OS/libs/nsyskbd/nsyskbd.h
439447
OS/libs/nsysnet/nsysnet.cpp
@@ -524,6 +532,17 @@ if (ENABLE_WAYLAND)
524532
target_link_libraries(CemuCafe PUBLIC Wayland::Client)
525533
endif()
526534

535+
if (ENABLE_NSYSHID_LIBUSB)
536+
if (ENABLE_VCPKG)
537+
find_package(libusb CONFIG REQUIRED)
538+
target_include_directories(CemuCafe PRIVATE ${LIBUSB_INCLUDE_DIRS})
539+
target_link_libraries(CemuCafe PRIVATE ${LIBUSB_LIBRARIES})
540+
else ()
541+
find_package(libusb MODULE REQUIRED)
542+
target_link_libraries(CemuCafe PRIVATE libusb::libusb)
543+
endif ()
544+
endif ()
545+
527546
if (ENABLE_WXWIDGETS)
528547
target_link_libraries(CemuCafe PRIVATE wx::base wx::core)
529548
endif()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#include "nsyshid.h"
2+
#include "Backend.h"
3+
4+
#if NSYSHID_ENABLE_BACKEND_LIBUSB
5+
6+
#include "BackendLibusb.h"
7+
8+
#endif
9+
10+
#if NSYSHID_ENABLE_BACKEND_WINDOWS_HID
11+
12+
#include "BackendWindowsHID.h"
13+
14+
#endif
15+
16+
namespace nsyshid::backend
17+
{
18+
void AttachDefaultBackends()
19+
{
20+
#if NSYSHID_ENABLE_BACKEND_LIBUSB
21+
// add libusb backend
22+
{
23+
auto backendLibusb = std::make_shared<backend::libusb::BackendLibusb>();
24+
if (backendLibusb->IsInitialisedOk())
25+
{
26+
AttachBackend(backendLibusb);
27+
}
28+
}
29+
#endif // NSYSHID_ENABLE_BACKEND_LIBUSB
30+
#if NSYSHID_ENABLE_BACKEND_WINDOWS_HID
31+
// add windows hid backend
32+
{
33+
auto backendWindowsHID = std::make_shared<backend::windows::BackendWindowsHID>();
34+
if (backendWindowsHID->IsInitialisedOk())
35+
{
36+
AttachBackend(backendWindowsHID);
37+
}
38+
}
39+
#endif // NSYSHID_ENABLE_BACKEND_WINDOWS_HID
40+
}
41+
} // namespace nsyshid::backend

src/Cafe/OS/libs/nsyshid/Backend.h

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#ifndef CEMU_NSYSHID_BACKEND_H
2+
#define CEMU_NSYSHID_BACKEND_H
3+
4+
#include <list>
5+
#include <memory>
6+
#include <mutex>
7+
8+
#include "Common/precompiled.h"
9+
10+
namespace nsyshid
11+
{
12+
typedef struct
13+
{
14+
/* +0x00 */ uint32be handle;
15+
/* +0x04 */ uint32 ukn04;
16+
/* +0x08 */ uint16 vendorId; // little-endian ?
17+
/* +0x0A */ uint16 productId; // little-endian ?
18+
/* +0x0C */ uint8 ifIndex;
19+
/* +0x0D */ uint8 subClass;
20+
/* +0x0E */ uint8 protocol;
21+
/* +0x0F */ uint8 paddingGuessed0F;
22+
/* +0x10 */ uint16be maxPacketSizeRX;
23+
/* +0x12 */ uint16be maxPacketSizeTX;
24+
} HID_t;
25+
26+
static_assert(offsetof(HID_t, vendorId) == 0x8, "");
27+
static_assert(offsetof(HID_t, productId) == 0xA, "");
28+
static_assert(offsetof(HID_t, ifIndex) == 0xC, "");
29+
static_assert(offsetof(HID_t, protocol) == 0xE, "");
30+
31+
class Device {
32+
public:
33+
Device() = delete;
34+
35+
Device(uint16 vendorId,
36+
uint16 productId,
37+
uint8 interfaceIndex,
38+
uint8 interfaceSubClass,
39+
uint8 protocol);
40+
41+
Device(const Device& device) = delete;
42+
43+
Device& operator=(const Device& device) = delete;
44+
45+
virtual ~Device() = default;
46+
47+
HID_t* m_hid; // this info is passed to applications and must remain intact
48+
49+
uint16 m_vendorId;
50+
uint16 m_productId;
51+
uint8 m_interfaceIndex;
52+
uint8 m_interfaceSubClass;
53+
uint8 m_protocol;
54+
uint16 m_maxPacketSizeRX;
55+
uint16 m_maxPacketSizeTX;
56+
57+
virtual void AssignHID(HID_t* hid);
58+
59+
virtual bool Open() = 0;
60+
61+
virtual void Close() = 0;
62+
63+
virtual bool IsOpened() = 0;
64+
65+
enum class ReadResult
66+
{
67+
Success,
68+
Error,
69+
ErrorTimeout,
70+
};
71+
72+
virtual ReadResult Read(uint8* data, sint32 length, sint32& bytesRead) = 0;
73+
74+
enum class WriteResult
75+
{
76+
Success,
77+
Error,
78+
ErrorTimeout,
79+
};
80+
81+
virtual WriteResult Write(uint8* data, sint32 length, sint32& bytesWritten) = 0;
82+
83+
virtual bool GetDescriptor(uint8 descType,
84+
uint8 descIndex,
85+
uint8 lang,
86+
uint8* output,
87+
uint32 outputMaxLength) = 0;
88+
89+
virtual bool SetProtocol(uint32 ifIndef, uint32 protocol) = 0;
90+
91+
virtual bool SetReport(uint8* reportData, sint32 length, uint8* originalData, sint32 originalLength) = 0;
92+
};
93+
94+
class Backend {
95+
public:
96+
Backend();
97+
98+
Backend(const Backend& backend) = delete;
99+
100+
Backend& operator=(const Backend& backend) = delete;
101+
102+
virtual ~Backend() = default;
103+
104+
void DetachAllDevices();
105+
106+
// called from nsyshid when this backend is attached - do not call this yourself!
107+
void OnAttach();
108+
109+
// called from nsyshid when this backend is detached - do not call this yourself!
110+
void OnDetach();
111+
112+
bool IsBackendAttached();
113+
114+
virtual bool IsInitialisedOk() = 0;
115+
116+
protected:
117+
// try to attach a device - only works if this backend is attached
118+
bool AttachDevice(const std::shared_ptr<Device>& device);
119+
120+
void DetachDevice(const std::shared_ptr<Device>& device);
121+
122+
std::shared_ptr<Device> FindDevice(std::function<bool(const std::shared_ptr<Device>&)> isWantedDevice);
123+
124+
bool IsDeviceWhitelisted(uint16 vendorId, uint16 productId);
125+
126+
// called from OnAttach() - attach devices that your backend can see here
127+
virtual void AttachVisibleDevices() = 0;
128+
129+
private:
130+
std::list<std::shared_ptr<Device>> m_devices;
131+
std::recursive_mutex m_devicesMutex;
132+
bool m_isAttached;
133+
};
134+
135+
namespace backend
136+
{
137+
void AttachDefaultBackends();
138+
}
139+
} // namespace nsyshid
140+
141+
#endif // CEMU_NSYSHID_BACKEND_H

0 commit comments

Comments
 (0)