summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/core/CMakeLists.txt10
-rw-r--r--src/core/hle/kernel/kernel.cpp18
-rw-r--r--src/core/hle/kernel/kernel.h6
-rw-r--r--src/core/hle/service/hid/hid.cpp27
-rw-r--r--src/core/hle/service/hid/hidbus.cpp529
-rw-r--r--src/core/hle/service/hid/hidbus.h131
-rw-r--r--src/core/hle/service/hid/hidbus/hidbus_base.cpp72
-rw-r--r--src/core/hle/service/hid/hidbus/hidbus_base.h178
-rw-r--r--src/core/hle/service/hid/hidbus/ringcon.cpp306
-rw-r--r--src/core/hle/service/hid/hidbus/ringcon.h247
-rw-r--r--src/core/hle/service/hid/hidbus/starlink.cpp51
-rw-r--r--src/core/hle/service/hid/hidbus/starlink.h39
-rw-r--r--src/core/hle/service/hid/hidbus/stubbed.cpp52
-rw-r--r--src/core/hle/service/hid/hidbus/stubbed.h39
14 files changed, 1679 insertions, 26 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index b681d21a7..62230bae0 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -434,6 +434,8 @@ add_library(core STATIC
434 hle/service/grc/grc.h 434 hle/service/grc/grc.h
435 hle/service/hid/hid.cpp 435 hle/service/hid/hid.cpp
436 hle/service/hid/hid.h 436 hle/service/hid/hid.h
437 hle/service/hid/hidbus.cpp
438 hle/service/hid/hidbus.h
437 hle/service/hid/irs.cpp 439 hle/service/hid/irs.cpp
438 hle/service/hid/irs.h 440 hle/service/hid/irs.h
439 hle/service/hid/ring_lifo.h 441 hle/service/hid/ring_lifo.h
@@ -460,6 +462,14 @@ add_library(core STATIC
460 hle/service/hid/controllers/touchscreen.h 462 hle/service/hid/controllers/touchscreen.h
461 hle/service/hid/controllers/xpad.cpp 463 hle/service/hid/controllers/xpad.cpp
462 hle/service/hid/controllers/xpad.h 464 hle/service/hid/controllers/xpad.h
465 hle/service/hid/hidbus/hidbus_base.cpp
466 hle/service/hid/hidbus/hidbus_base.h
467 hle/service/hid/hidbus/ringcon.cpp
468 hle/service/hid/hidbus/ringcon.h
469 hle/service/hid/hidbus/starlink.cpp
470 hle/service/hid/hidbus/starlink.h
471 hle/service/hid/hidbus/stubbed.cpp
472 hle/service/hid/hidbus/stubbed.h
463 hle/service/jit/jit_context.cpp 473 hle/service/jit/jit_context.cpp
464 hle/service/jit/jit_context.h 474 hle/service/jit/jit_context.h
465 hle/service/jit/jit.cpp 475 hle/service/jit/jit.cpp
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index d840d44e6..5984afd7e 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -140,6 +140,7 @@ struct KernelCore::Impl {
140 CleanupObject(font_shared_mem); 140 CleanupObject(font_shared_mem);
141 CleanupObject(irs_shared_mem); 141 CleanupObject(irs_shared_mem);
142 CleanupObject(time_shared_mem); 142 CleanupObject(time_shared_mem);
143 CleanupObject(hidbus_shared_mem);
143 CleanupObject(system_resource_limit); 144 CleanupObject(system_resource_limit);
144 145
145 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { 146 for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
@@ -622,16 +623,20 @@ struct KernelCore::Impl {
622 constexpr std::size_t font_size{0x1100000}; 623 constexpr std::size_t font_size{0x1100000};
623 constexpr std::size_t irs_size{0x8000}; 624 constexpr std::size_t irs_size{0x8000};
624 constexpr std::size_t time_size{0x1000}; 625 constexpr std::size_t time_size{0x1000};
626 constexpr std::size_t hidbus_size{0x1000};
625 627
626 const PAddr hid_phys_addr{system_pool.GetAddress()}; 628 const PAddr hid_phys_addr{system_pool.GetAddress()};
627 const PAddr font_phys_addr{system_pool.GetAddress() + hid_size}; 629 const PAddr font_phys_addr{system_pool.GetAddress() + hid_size};
628 const PAddr irs_phys_addr{system_pool.GetAddress() + hid_size + font_size}; 630 const PAddr irs_phys_addr{system_pool.GetAddress() + hid_size + font_size};
629 const PAddr time_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size}; 631 const PAddr time_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size};
632 const PAddr hidbus_phys_addr{system_pool.GetAddress() + hid_size + font_size + irs_size +
633 time_size};
630 634
631 hid_shared_mem = KSharedMemory::Create(system.Kernel()); 635 hid_shared_mem = KSharedMemory::Create(system.Kernel());
632 font_shared_mem = KSharedMemory::Create(system.Kernel()); 636 font_shared_mem = KSharedMemory::Create(system.Kernel());
633 irs_shared_mem = KSharedMemory::Create(system.Kernel()); 637 irs_shared_mem = KSharedMemory::Create(system.Kernel());
634 time_shared_mem = KSharedMemory::Create(system.Kernel()); 638 time_shared_mem = KSharedMemory::Create(system.Kernel());
639 hidbus_shared_mem = KSharedMemory::Create(system.Kernel());
635 640
636 hid_shared_mem->Initialize(system.DeviceMemory(), nullptr, 641 hid_shared_mem->Initialize(system.DeviceMemory(), nullptr,
637 {hid_phys_addr, hid_size / PageSize}, 642 {hid_phys_addr, hid_size / PageSize},
@@ -649,6 +654,10 @@ struct KernelCore::Impl {
649 {time_phys_addr, time_size / PageSize}, 654 {time_phys_addr, time_size / PageSize},
650 Svc::MemoryPermission::None, Svc::MemoryPermission::Read, 655 Svc::MemoryPermission::None, Svc::MemoryPermission::Read,
651 time_phys_addr, time_size, "Time:SharedMemory"); 656 time_phys_addr, time_size, "Time:SharedMemory");
657 hidbus_shared_mem->Initialize(system.DeviceMemory(), nullptr,
658 {hidbus_phys_addr, hidbus_size / PageSize},
659 Svc::MemoryPermission::None, Svc::MemoryPermission::Read,
660 hidbus_phys_addr, hidbus_size, "HidBus:SharedMemory");
652 } 661 }
653 662
654 KClientPort* CreateNamedServicePort(std::string name) { 663 KClientPort* CreateNamedServicePort(std::string name) {
@@ -748,6 +757,7 @@ struct KernelCore::Impl {
748 Kernel::KSharedMemory* font_shared_mem{}; 757 Kernel::KSharedMemory* font_shared_mem{};
749 Kernel::KSharedMemory* irs_shared_mem{}; 758 Kernel::KSharedMemory* irs_shared_mem{};
750 Kernel::KSharedMemory* time_shared_mem{}; 759 Kernel::KSharedMemory* time_shared_mem{};
760 Kernel::KSharedMemory* hidbus_shared_mem{};
751 761
752 // Memory layout 762 // Memory layout
753 std::unique_ptr<KMemoryLayout> memory_layout; 763 std::unique_ptr<KMemoryLayout> memory_layout;
@@ -1047,6 +1057,14 @@ const Kernel::KSharedMemory& KernelCore::GetTimeSharedMem() const {
1047 return *impl->time_shared_mem; 1057 return *impl->time_shared_mem;
1048} 1058}
1049 1059
1060Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() {
1061 return *impl->hidbus_shared_mem;
1062}
1063
1064const Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() const {
1065 return *impl->hidbus_shared_mem;
1066}
1067
1050void KernelCore::Suspend(bool in_suspention) { 1068void KernelCore::Suspend(bool in_suspention) {
1051 const bool should_suspend = exception_exited || in_suspention; 1069 const bool should_suspend = exception_exited || in_suspention;
1052 { 1070 {
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index d709c368b..12e44b8a5 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -264,6 +264,12 @@ public:
264 /// Gets the shared memory object for Time services. 264 /// Gets the shared memory object for Time services.
265 const Kernel::KSharedMemory& GetTimeSharedMem() const; 265 const Kernel::KSharedMemory& GetTimeSharedMem() const;
266 266
267 /// Gets the shared memory object for HIDBus services.
268 Kernel::KSharedMemory& GetHidBusSharedMem();
269
270 /// Gets the shared memory object for HIDBus services.
271 const Kernel::KSharedMemory& GetHidBusSharedMem() const;
272
267 /// Suspend/unsuspend the OS. 273 /// Suspend/unsuspend the OS.
268 void Suspend(bool in_suspention); 274 void Suspend(bool in_suspention);
269 275
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index b2cec2253..9d3e0a658 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -16,6 +16,7 @@
16#include "core/hle/kernel/kernel.h" 16#include "core/hle/kernel/kernel.h"
17#include "core/hle/service/hid/errors.h" 17#include "core/hle/service/hid/errors.h"
18#include "core/hle/service/hid/hid.h" 18#include "core/hle/service/hid/hid.h"
19#include "core/hle/service/hid/hidbus.h"
19#include "core/hle/service/hid/irs.h" 20#include "core/hle/service/hid/irs.h"
20#include "core/hle/service/hid/xcd.h" 21#include "core/hle/service/hid/xcd.h"
21#include "core/memory.h" 22#include "core/memory.h"
@@ -2128,32 +2129,6 @@ public:
2128 } 2129 }
2129}; 2130};
2130 2131
2131class HidBus final : public ServiceFramework<HidBus> {
2132public:
2133 explicit HidBus(Core::System& system_) : ServiceFramework{system_, "hidbus"} {
2134 // clang-format off
2135 static const FunctionInfo functions[] = {
2136 {1, nullptr, "GetBusHandle"},
2137 {2, nullptr, "IsExternalDeviceConnected"},
2138 {3, nullptr, "Initialize"},
2139 {4, nullptr, "Finalize"},
2140 {5, nullptr, "EnableExternalDevice"},
2141 {6, nullptr, "GetExternalDeviceId"},
2142 {7, nullptr, "SendCommandAsync"},
2143 {8, nullptr, "GetSendCommandAsynceResult"},
2144 {9, nullptr, "SetEventForSendCommandAsycResult"},
2145 {10, nullptr, "GetSharedMemoryHandle"},
2146 {11, nullptr, "EnableJoyPollingReceiveMode"},
2147 {12, nullptr, "DisableJoyPollingReceiveMode"},
2148 {13, nullptr, "GetPollingData"},
2149 {14, nullptr, "SetStatusManagerType"},
2150 };
2151 // clang-format on
2152
2153 RegisterHandlers(functions);
2154 }
2155};
2156
2157void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) { 2132void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
2158 std::make_shared<Hid>(system)->InstallAsService(service_manager); 2133 std::make_shared<Hid>(system)->InstallAsService(service_manager);
2159 std::make_shared<HidBus>(system)->InstallAsService(service_manager); 2134 std::make_shared<HidBus>(system)->InstallAsService(service_manager);
diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp
new file mode 100644
index 000000000..db2864277
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus.cpp
@@ -0,0 +1,529 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/logging/log.h"
6#include "core/core.h"
7#include "core/core_timing.h"
8#include "core/core_timing_util.h"
9#include "core/hid/hid_types.h"
10#include "core/hle/ipc_helpers.h"
11#include "core/hle/kernel/k_event.h"
12#include "core/hle/kernel/k_readable_event.h"
13#include "core/hle/kernel/k_shared_memory.h"
14#include "core/hle/kernel/k_transfer_memory.h"
15#include "core/hle/service/hid/hidbus.h"
16#include "core/hle/service/hid/hidbus/ringcon.h"
17#include "core/hle/service/hid/hidbus/starlink.h"
18#include "core/hle/service/hid/hidbus/stubbed.h"
19#include "core/hle/service/service.h"
20#include "core/memory.h"
21
22namespace Service::HID {
23// (15ms, 66Hz)
24constexpr auto hidbus_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000};
25
26HidBus::HidBus(Core::System& system_)
27 : ServiceFramework{system_, "hidbus"}, service_context{system_, service_name} {
28
29 // clang-format off
30 static const FunctionInfo functions[] = {
31 {1, &HidBus::GetBusHandle, "GetBusHandle"},
32 {2, &HidBus::IsExternalDeviceConnected, "IsExternalDeviceConnected"},
33 {3, &HidBus::Initialize, "Initialize"},
34 {4, &HidBus::Finalize, "Finalize"},
35 {5, &HidBus::EnableExternalDevice, "EnableExternalDevice"},
36 {6, &HidBus::GetExternalDeviceId, "GetExternalDeviceId"},
37 {7, &HidBus::SendCommandAsync, "SendCommandAsync"},
38 {8, &HidBus::GetSendCommandAsynceResult, "GetSendCommandAsynceResult"},
39 {9, &HidBus::SetEventForSendCommandAsycResult, "SetEventForSendCommandAsycResult"},
40 {10, &HidBus::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
41 {11, &HidBus::EnableJoyPollingReceiveMode, "EnableJoyPollingReceiveMode"},
42 {12, &HidBus::DisableJoyPollingReceiveMode, "DisableJoyPollingReceiveMode"},
43 {13, nullptr, "GetPollingData"},
44 {14, &HidBus::SetStatusManagerType, "SetStatusManagerType"},
45 };
46 // clang-format on
47
48 RegisterHandlers(functions);
49
50 // Register update callbacks
51 hidbus_update_event = Core::Timing::CreateEvent(
52 "Hidbus::UpdateCallback",
53 [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
54 const auto guard = LockService();
55 UpdateHidbus(user_data, ns_late);
56 });
57
58 system_.CoreTiming().ScheduleEvent(hidbus_update_ns, hidbus_update_event);
59}
60
61HidBus::~HidBus() {
62 system.CoreTiming().UnscheduleEvent(hidbus_update_event, 0);
63}
64
65void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
66 auto& core_timing = system.CoreTiming();
67
68 if (is_hidbus_enabled) {
69 for (std::size_t i = 0; i < devices.size(); ++i) {
70 if (!devices[i].is_device_initializated) {
71 continue;
72 }
73 auto& device = devices[i].device;
74 device->OnUpdate();
75 auto& cur_entry = hidbus_status.entries[devices[i].handle.internal_index];
76 cur_entry.is_polling_mode = device->IsPollingMode();
77 cur_entry.polling_mode = device->GetPollingMode();
78 cur_entry.is_enabled = device->IsEnabled();
79
80 u8* shared_memory = system.Kernel().GetHidBusSharedMem().GetPointer();
81 std::memcpy(shared_memory + (i * sizeof(HidbusStatusManagerEntry)), &hidbus_status,
82 sizeof(HidbusStatusManagerEntry));
83 }
84 }
85
86 // If ns_late is higher than the update rate ignore the delay
87 if (ns_late > hidbus_update_ns) {
88 ns_late = {};
89 }
90
91 core_timing.ScheduleEvent(hidbus_update_ns - ns_late, hidbus_update_event);
92}
93
94std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const {
95 for (std::size_t i = 0; i < devices.size(); ++i) {
96 const auto& device_handle = devices[i].handle;
97 if (handle.abstracted_pad_id == device_handle.abstracted_pad_id &&
98 handle.internal_index == device_handle.internal_index &&
99 handle.player_number == device_handle.player_number &&
100 handle.bus_type == device_handle.bus_type &&
101 handle.is_valid == device_handle.is_valid) {
102 return i;
103 }
104 }
105 return std::nullopt;
106}
107
108void HidBus::GetBusHandle(Kernel::HLERequestContext& ctx) {
109 IPC::RequestParser rp{ctx};
110 struct Parameters {
111 Core::HID::NpadIdType npad_id;
112 INSERT_PADDING_WORDS_NOINIT(1);
113 BusType bus_type;
114 u64 applet_resource_user_id;
115 };
116 static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
117
118 const auto parameters{rp.PopRaw<Parameters>()};
119
120 LOG_INFO(Service_HID, "called, npad_id={}, bus_type={}, applet_resource_user_id={}",
121 parameters.npad_id, parameters.bus_type, parameters.applet_resource_user_id);
122
123 bool is_handle_found = 0;
124 std::size_t handle_index = 0;
125
126 for (std::size_t i = 0; i < devices.size(); i++) {
127 const auto& handle = devices[i].handle;
128 if (!handle.is_valid) {
129 continue;
130 }
131 if (static_cast<Core::HID::NpadIdType>(handle.player_number) == parameters.npad_id &&
132 handle.bus_type == parameters.bus_type) {
133 is_handle_found = true;
134 handle_index = i;
135 break;
136 }
137 }
138
139 // Handle not found. Create a new one
140 if (!is_handle_found) {
141 for (std::size_t i = 0; i < devices.size(); i++) {
142 if (devices[i].handle.is_valid) {
143 continue;
144 }
145 devices[i].handle = {
146 .abstracted_pad_id = static_cast<u8>(i),
147 .internal_index = static_cast<u8>(i),
148 .player_number = static_cast<u8>(parameters.npad_id),
149 .bus_type = parameters.bus_type,
150 .is_valid = true,
151 };
152 handle_index = i;
153 break;
154 }
155 }
156
157 struct OutData {
158 bool is_valid;
159 INSERT_PADDING_BYTES(7);
160 BusHandle handle;
161 };
162 static_assert(sizeof(OutData) == 0x10, "OutData has incorrect size.");
163
164 const OutData out_data{
165 .is_valid = true,
166 .handle = devices[handle_index].handle,
167 };
168
169 IPC::ResponseBuilder rb{ctx, 6};
170 rb.Push(ResultSuccess);
171 rb.PushRaw(out_data);
172}
173
174void HidBus::IsExternalDeviceConnected(Kernel::HLERequestContext& ctx) {
175 IPC::RequestParser rp{ctx};
176 const auto bus_handle_{rp.PopRaw<BusHandle>()};
177
178 LOG_INFO(Service_HID,
179 "Called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
180 "player_number={}, is_valid={}",
181 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
182 bus_handle_.player_number, bus_handle_.is_valid);
183
184 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
185
186 if (device_index) {
187 const auto& device = devices[device_index.value()].device;
188 const bool is_attached = device->IsDeviceActivated();
189
190 IPC::ResponseBuilder rb{ctx, 3};
191 rb.Push(ResultSuccess);
192 rb.Push(is_attached);
193 }
194
195 LOG_ERROR(Service_HID, "Invalid handle");
196 IPC::ResponseBuilder rb{ctx, 2};
197 rb.Push(ResultUnknown);
198 return;
199}
200
201void HidBus::Initialize(Kernel::HLERequestContext& ctx) {
202 IPC::RequestParser rp{ctx};
203 const auto bus_handle_{rp.PopRaw<BusHandle>()};
204 const auto applet_resource_user_id{rp.Pop<u64>()};
205
206 LOG_INFO(Service_HID,
207 "called, abstracted_pad_id={} bus_type={} internal_index={} "
208 "player_number={} is_valid={}, applet_resource_user_id={}",
209 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
210 bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id);
211
212 is_hidbus_enabled = true;
213
214 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
215
216 if (device_index) {
217 const auto entry_index = devices[device_index.value()].handle.internal_index;
218 auto& cur_entry = hidbus_status.entries[entry_index];
219
220 if (bus_handle_.internal_index == 0) {
221 MakeDevice<RingController>(bus_handle_);
222 devices[device_index.value()].is_device_initializated = true;
223 devices[device_index.value()].device->ActivateDevice();
224 cur_entry.is_in_focus = true;
225 cur_entry.is_connected = true;
226 cur_entry.is_connected_result = ResultSuccess;
227 cur_entry.is_enabled = false;
228 cur_entry.is_polling_mode = false;
229 } else {
230 MakeDevice<HidbusStubbed>(bus_handle_);
231 devices[device_index.value()].is_device_initializated = true;
232 cur_entry.is_in_focus = true;
233 cur_entry.is_connected = false;
234 cur_entry.is_connected_result = ResultSuccess;
235 cur_entry.is_enabled = false;
236 cur_entry.is_polling_mode = false;
237 }
238
239 std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
240 sizeof(hidbus_status));
241
242 IPC::ResponseBuilder rb{ctx, 2};
243 rb.Push(ResultSuccess);
244 return;
245 }
246
247 LOG_ERROR(Service_HID, "Invalid handle");
248 IPC::ResponseBuilder rb{ctx, 2};
249 rb.Push(ResultUnknown);
250 return;
251}
252
253void HidBus::Finalize(Kernel::HLERequestContext& ctx) {
254 IPC::RequestParser rp{ctx};
255 const auto bus_handle_{rp.PopRaw<BusHandle>()};
256 const auto applet_resource_user_id{rp.Pop<u64>()};
257
258 LOG_INFO(Service_HID,
259 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, "
260 "player_number={}, is_valid={}, applet_resource_user_id={}",
261 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
262 bus_handle_.player_number, bus_handle_.is_valid, applet_resource_user_id);
263
264 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
265
266 if (device_index) {
267 const auto entry_index = devices[device_index.value()].handle.internal_index;
268 auto& cur_entry = hidbus_status.entries[entry_index];
269 auto& device = devices[device_index.value()].device;
270 devices[device_index.value()].is_device_initializated = false;
271 device->DeactivateDevice();
272
273 cur_entry.is_in_focus = true;
274 cur_entry.is_connected = false;
275 cur_entry.is_connected_result = ResultSuccess;
276 cur_entry.is_enabled = false;
277 cur_entry.is_polling_mode = false;
278 std::memcpy(system.Kernel().GetHidBusSharedMem().GetPointer(), &hidbus_status,
279 sizeof(hidbus_status));
280
281 IPC::ResponseBuilder rb{ctx, 2};
282 rb.Push(ResultSuccess);
283 return;
284 }
285
286 LOG_ERROR(Service_HID, "Invalid handle");
287 IPC::ResponseBuilder rb{ctx, 2};
288 rb.Push(ResultUnknown);
289 return;
290}
291
292void HidBus::EnableExternalDevice(Kernel::HLERequestContext& ctx) {
293 IPC::RequestParser rp{ctx};
294 struct Parameters {
295 bool enable;
296 INSERT_PADDING_BYTES_NOINIT(7);
297 BusHandle bus_handle;
298 u64 inval;
299 u64 applet_resource_user_id;
300 };
301 static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size.");
302
303 const auto parameters{rp.PopRaw<Parameters>()};
304
305 LOG_INFO(Service_HID,
306 "called, enable={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
307 "player_number={}, is_valid={}, inval={}, applet_resource_user_id{}",
308 parameters.enable, parameters.bus_handle.abstracted_pad_id,
309 parameters.bus_handle.bus_type, parameters.bus_handle.internal_index,
310 parameters.bus_handle.player_number, parameters.bus_handle.is_valid, parameters.inval,
311 parameters.applet_resource_user_id);
312
313 const auto device_index = GetDeviceIndexFromHandle(parameters.bus_handle);
314
315 if (device_index) {
316 auto& device = devices[device_index.value()].device;
317 device->Enable(parameters.enable);
318
319 IPC::ResponseBuilder rb{ctx, 2};
320 rb.Push(ResultSuccess);
321 return;
322 }
323
324 LOG_ERROR(Service_HID, "Invalid handle");
325 IPC::ResponseBuilder rb{ctx, 2};
326 rb.Push(ResultUnknown);
327 return;
328}
329
330void HidBus::GetExternalDeviceId(Kernel::HLERequestContext& ctx) {
331 IPC::RequestParser rp{ctx};
332 const auto bus_handle_{rp.PopRaw<BusHandle>()};
333
334 LOG_INFO(Service_HID,
335 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
336 "is_valid={}",
337 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
338 bus_handle_.player_number, bus_handle_.is_valid);
339
340 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
341
342 if (device_index) {
343 const auto& device = devices[device_index.value()].device;
344 u32 device_id = device->GetDeviceId();
345 IPC::ResponseBuilder rb{ctx, 3};
346 rb.Push(ResultSuccess);
347 rb.Push<u32>(device_id);
348 return;
349 }
350
351 LOG_ERROR(Service_HID, "Invalid handle");
352 IPC::ResponseBuilder rb{ctx, 2};
353 rb.Push(ResultUnknown);
354 return;
355}
356
357void HidBus::SendCommandAsync(Kernel::HLERequestContext& ctx) {
358 IPC::RequestParser rp{ctx};
359 const auto data = ctx.ReadBuffer();
360 const auto bus_handle_{rp.PopRaw<BusHandle>()};
361
362 LOG_DEBUG(Service_HID,
363 "called, data_size={}, abstracted_pad_id={}, bus_type={}, internal_index={}, "
364 "player_number={}, is_valid={}",
365 data.size(), bus_handle_.abstracted_pad_id, bus_handle_.bus_type,
366 bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid);
367
368 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
369
370 if (device_index) {
371 auto& device = devices[device_index.value()].device;
372 device->SetCommand(data);
373
374 IPC::ResponseBuilder rb{ctx, 2};
375 rb.Push(ResultSuccess);
376 return;
377 }
378
379 LOG_ERROR(Service_HID, "Invalid handle");
380 IPC::ResponseBuilder rb{ctx, 2};
381 rb.Push(ResultUnknown);
382 return;
383};
384
385void HidBus::GetSendCommandAsynceResult(Kernel::HLERequestContext& ctx) {
386 IPC::RequestParser rp{ctx};
387 const auto bus_handle_{rp.PopRaw<BusHandle>()};
388
389 LOG_DEBUG(Service_HID,
390 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
391 "is_valid={}",
392 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
393 bus_handle_.player_number, bus_handle_.is_valid);
394
395 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
396
397 if (device_index) {
398 const auto& device = devices[device_index.value()].device;
399 const std::vector<u8> data = device->GetReply();
400 const u64 data_size = ctx.WriteBuffer(data);
401
402 IPC::ResponseBuilder rb{ctx, 4};
403 rb.Push(ResultSuccess);
404 rb.Push<u64>(data_size);
405 return;
406 }
407
408 LOG_ERROR(Service_HID, "Invalid handle");
409 IPC::ResponseBuilder rb{ctx, 2};
410 rb.Push(ResultUnknown);
411 return;
412};
413
414void HidBus::SetEventForSendCommandAsycResult(Kernel::HLERequestContext& ctx) {
415 IPC::RequestParser rp{ctx};
416 const auto bus_handle_{rp.PopRaw<BusHandle>()};
417
418 LOG_INFO(Service_HID,
419 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
420 "is_valid={}",
421 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
422 bus_handle_.player_number, bus_handle_.is_valid);
423
424 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
425
426 if (device_index) {
427 const auto& device = devices[device_index.value()].device;
428 IPC::ResponseBuilder rb{ctx, 2, 1};
429 rb.Push(ResultSuccess);
430 rb.PushCopyObjects(device->GetSendCommandAsycEvent());
431 return;
432 }
433
434 LOG_ERROR(Service_HID, "Invalid handle");
435 IPC::ResponseBuilder rb{ctx, 2};
436 rb.Push(ResultUnknown);
437 return;
438};
439
440void HidBus::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
441 LOG_DEBUG(Service_HID, "called");
442
443 IPC::ResponseBuilder rb{ctx, 2, 1};
444 rb.Push(ResultSuccess);
445 rb.PushCopyObjects(&system.Kernel().GetHidBusSharedMem());
446}
447
448void HidBus::EnableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx) {
449 IPC::RequestParser rp{ctx};
450 const auto t_mem_size{rp.Pop<u32>()};
451 const auto t_mem_handle{ctx.GetCopyHandle(0)};
452 const auto polling_mode_{rp.PopEnum<JoyPollingMode>()};
453 const auto bus_handle_{rp.PopRaw<BusHandle>()};
454
455 ASSERT_MSG(t_mem_size == 0x1000, "t_mem_size is not 0x1000 bytes");
456
457 auto t_mem =
458 system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
459
460 if (t_mem.IsNull()) {
461 LOG_ERROR(Service_HID, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
462 IPC::ResponseBuilder rb{ctx, 2};
463 rb.Push(ResultUnknown);
464 return;
465 }
466
467 ASSERT_MSG(t_mem->GetSize() == 0x1000, "t_mem has incorrect size");
468
469 LOG_INFO(Service_HID,
470 "called, t_mem_handle=0x{:08X}, polling_mode={}, abstracted_pad_id={}, bus_type={}, "
471 "internal_index={}, player_number={}, is_valid={}",
472 t_mem_handle, polling_mode_, bus_handle_.abstracted_pad_id, bus_handle_.bus_type,
473 bus_handle_.internal_index, bus_handle_.player_number, bus_handle_.is_valid);
474
475 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
476
477 if (device_index) {
478 auto& device = devices[device_index.value()].device;
479 device->SetPollingMode(polling_mode_);
480 device->SetTransferMemoryPointer(system.Memory().GetPointer(t_mem->GetSourceAddress()));
481
482 IPC::ResponseBuilder rb{ctx, 2};
483 rb.Push(ResultSuccess);
484 return;
485 }
486
487 LOG_ERROR(Service_HID, "Invalid handle");
488 IPC::ResponseBuilder rb{ctx, 2};
489 rb.Push(ResultUnknown);
490 return;
491}
492
493void HidBus::DisableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx) {
494 IPC::RequestParser rp{ctx};
495 const auto bus_handle_{rp.PopRaw<BusHandle>()};
496
497 LOG_INFO(Service_HID,
498 "called, abstracted_pad_id={}, bus_type={}, internal_index={}, player_number={}, "
499 "is_valid={}",
500 bus_handle_.abstracted_pad_id, bus_handle_.bus_type, bus_handle_.internal_index,
501 bus_handle_.player_number, bus_handle_.is_valid);
502
503 const auto device_index = GetDeviceIndexFromHandle(bus_handle_);
504
505 if (device_index) {
506 auto& device = devices[device_index.value()].device;
507 device->DisablePollingMode();
508
509 IPC::ResponseBuilder rb{ctx, 2};
510 rb.Push(ResultSuccess);
511 return;
512 }
513
514 LOG_ERROR(Service_HID, "Invalid handle");
515 IPC::ResponseBuilder rb{ctx, 2};
516 rb.Push(ResultUnknown);
517 return;
518}
519
520void HidBus::SetStatusManagerType(Kernel::HLERequestContext& ctx) {
521 IPC::RequestParser rp{ctx};
522 const auto manager_type{rp.PopEnum<StatusManagerType>()};
523
524 LOG_WARNING(Service_HID, "(STUBBED) called, manager_type={}", manager_type);
525
526 IPC::ResponseBuilder rb{ctx, 2};
527 rb.Push(ResultSuccess);
528};
529} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus.h b/src/core/hle/service/hid/hidbus.h
new file mode 100644
index 000000000..b10d5156a
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus.h
@@ -0,0 +1,131 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8
9#include "core/hle/service/hid/hidbus/hidbus_base.h"
10#include "core/hle/service/kernel_helpers.h"
11#include "core/hle/service/service.h"
12
13namespace Core::Timing {
14struct EventType;
15} // namespace Core::Timing
16
17namespace Core {
18class System;
19} // namespace Core
20
21namespace Service::HID {
22
23class HidBus final : public ServiceFramework<HidBus> {
24public:
25 explicit HidBus(Core::System& system_);
26 ~HidBus() override;
27
28private:
29 static const std::size_t max_number_of_handles = 0x13;
30
31 enum class HidBusDeviceId : std::size_t {
32 RingController = 0x20,
33 FamicomRight = 0x21,
34 Starlink = 0x28,
35 };
36
37 // This is nn::hidbus::detail::StatusManagerType
38 enum class StatusManagerType : u32 {
39 None,
40 Type16,
41 Type32,
42 };
43
44 // This is nn::hidbus::BusType
45 enum class BusType : u8 {
46 LeftJoyRail,
47 RightJoyRail,
48 InternalBus, // Lark microphone
49
50 MaxBusType,
51 };
52
53 // This is nn::hidbus::BusHandle
54 struct BusHandle {
55 u32 abstracted_pad_id;
56 u8 internal_index;
57 u8 player_number;
58 BusType bus_type;
59 bool is_valid;
60 };
61 static_assert(sizeof(BusHandle) == 0x8, "BusHandle is an invalid size");
62
63 // This is nn::hidbus::JoyPollingReceivedData
64 struct JoyPollingReceivedData {
65 std::array<u8, 0x30> data;
66 u64 out_size;
67 u64 sampling_number;
68 };
69 static_assert(sizeof(JoyPollingReceivedData) == 0x40,
70 "JoyPollingReceivedData is an invalid size");
71
72 struct HidbusStatusManagerEntry {
73 u8 is_connected{};
74 INSERT_PADDING_BYTES(0x3);
75 ResultCode is_connected_result{0};
76 u8 is_enabled{};
77 u8 is_in_focus{};
78 u8 is_polling_mode{};
79 u8 reserved{};
80 JoyPollingMode polling_mode{};
81 INSERT_PADDING_BYTES(0x70); // Unknown
82 };
83 static_assert(sizeof(HidbusStatusManagerEntry) == 0x80,
84 "HidbusStatusManagerEntry is an invalid size");
85
86 struct HidbusStatusManager {
87 std::array<HidbusStatusManagerEntry, max_number_of_handles> entries{};
88 INSERT_PADDING_BYTES(0x680); // Unused
89 };
90 static_assert(sizeof(HidbusStatusManager) <= 0x1000, "HidbusStatusManager is an invalid size");
91
92 struct HidbusDevice {
93 bool is_device_initializated{};
94 BusHandle handle{};
95 std::unique_ptr<HidbusBase> device{nullptr};
96 };
97
98 void GetBusHandle(Kernel::HLERequestContext& ctx);
99 void IsExternalDeviceConnected(Kernel::HLERequestContext& ctx);
100 void Initialize(Kernel::HLERequestContext& ctx);
101 void Finalize(Kernel::HLERequestContext& ctx);
102 void EnableExternalDevice(Kernel::HLERequestContext& ctx);
103 void GetExternalDeviceId(Kernel::HLERequestContext& ctx);
104 void SendCommandAsync(Kernel::HLERequestContext& ctx);
105 void GetSendCommandAsynceResult(Kernel::HLERequestContext& ctx);
106 void SetEventForSendCommandAsycResult(Kernel::HLERequestContext& ctx);
107 void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
108 void EnableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx);
109 void DisableJoyPollingReceiveMode(Kernel::HLERequestContext& ctx);
110 void SetStatusManagerType(Kernel::HLERequestContext& ctx);
111
112 void UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
113 std::optional<std::size_t> GetDeviceIndexFromHandle(BusHandle handle) const;
114
115 template <typename T>
116 void MakeDevice(BusHandle handle) {
117 const auto device_index = GetDeviceIndexFromHandle(handle);
118 if (device_index) {
119 devices[device_index.value()].device =
120 std::make_unique<T>(system.HIDCore(), service_context);
121 }
122 }
123
124 bool is_hidbus_enabled{false};
125 HidbusStatusManager hidbus_status{};
126 std::array<HidbusDevice, max_number_of_handles> devices{};
127 std::shared_ptr<Core::Timing::EventType> hidbus_update_event;
128 KernelHelpers::ServiceContext service_context;
129};
130
131} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/hidbus_base.cpp b/src/core/hle/service/hid/hidbus/hidbus_base.cpp
new file mode 100644
index 000000000..9cac0be80
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/hidbus_base.cpp
@@ -0,0 +1,72 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hid/hid_core.h"
6#include "core/hle/kernel/k_event.h"
7#include "core/hle/kernel/k_readable_event.h"
8#include "core/hle/service/hid/hidbus/hidbus_base.h"
9#include "core/hle/service/kernel_helpers.h"
10
11namespace Service::HID {
12
13HidbusBase::HidbusBase(KernelHelpers::ServiceContext& service_context_)
14 : service_context(service_context_) {
15 send_command_asyc_event = service_context.CreateEvent("hidbus:SendCommandAsycEvent");
16}
17HidbusBase::~HidbusBase() = default;
18
19void HidbusBase::ActivateDevice() {
20 if (is_activated) {
21 return;
22 }
23 is_activated = true;
24 OnInit();
25}
26
27void HidbusBase::DeactivateDevice() {
28 if (is_activated) {
29 OnRelease();
30 }
31 is_activated = false;
32}
33
34bool HidbusBase::IsDeviceActivated() const {
35 return is_activated;
36}
37
38void HidbusBase::Enable(bool enable) {
39 device_enabled = enable;
40}
41
42bool HidbusBase::IsEnabled() const {
43 return device_enabled;
44}
45
46bool HidbusBase::IsPollingMode() const {
47 return polling_mode_enabled;
48}
49
50JoyPollingMode HidbusBase::GetPollingMode() const {
51 return polling_mode;
52}
53
54void HidbusBase::SetPollingMode(JoyPollingMode mode) {
55 polling_mode = mode;
56 polling_mode_enabled = true;
57}
58
59void HidbusBase::DisablePollingMode() {
60 polling_mode_enabled = false;
61}
62
63void HidbusBase::SetTransferMemoryPointer(u8* t_mem) {
64 is_transfer_memory_set = true;
65 transfer_memory = t_mem;
66}
67
68Kernel::KReadableEvent& HidbusBase::GetSendCommandAsycEvent() const {
69 return send_command_asyc_event->GetReadableEvent();
70}
71
72} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/hidbus_base.h b/src/core/hle/service/hid/hidbus/hidbus_base.h
new file mode 100644
index 000000000..41e571998
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/hidbus_base.h
@@ -0,0 +1,178 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include "common/common_types.h"
9#include "core/hle/result.h"
10
11namespace Kernel {
12class KEvent;
13class KReadableEvent;
14} // namespace Kernel
15
16namespace Service::KernelHelpers {
17class ServiceContext;
18}
19
20namespace Service::HID {
21
22// This is nn::hidbus::JoyPollingMode
23enum class JoyPollingMode : u32 {
24 SixAxisSensorDisable,
25 SixAxisSensorEnable,
26 ButtonOnly,
27};
28
29struct DataAccessorHeader {
30 ResultCode result{ResultUnknown};
31 INSERT_PADDING_WORDS(0x1);
32 std::array<u8, 0x18> unused{};
33 u64 latest_entry{};
34 u64 total_entries{};
35};
36static_assert(sizeof(DataAccessorHeader) == 0x30, "DataAccessorHeader is an invalid size");
37
38struct JoyDisableSixAxisPollingData {
39 std::array<u8, 0x26> data;
40 u8 out_size;
41 INSERT_PADDING_BYTES(0x1);
42 u64 sampling_number;
43};
44static_assert(sizeof(JoyDisableSixAxisPollingData) == 0x30,
45 "JoyDisableSixAxisPollingData is an invalid size");
46
47struct JoyEnableSixAxisPollingData {
48 std::array<u8, 0x8> data;
49 u8 out_size;
50 INSERT_PADDING_BYTES(0x7);
51 u64 sampling_number;
52};
53static_assert(sizeof(JoyEnableSixAxisPollingData) == 0x18,
54 "JoyEnableSixAxisPollingData is an invalid size");
55
56struct JoyButtonOnlyPollingData {
57 std::array<u8, 0x2c> data;
58 u8 out_size;
59 INSERT_PADDING_BYTES(0x3);
60 u64 sampling_number;
61};
62static_assert(sizeof(JoyButtonOnlyPollingData) == 0x38,
63 "JoyButtonOnlyPollingData is an invalid size");
64
65struct JoyDisableSixAxisPollingEntry {
66 u64 sampling_number;
67 JoyDisableSixAxisPollingData polling_data;
68};
69static_assert(sizeof(JoyDisableSixAxisPollingEntry) == 0x38,
70 "JoyDisableSixAxisPollingEntry is an invalid size");
71
72struct JoyEnableSixAxisPollingEntry {
73 u64 sampling_number;
74 JoyEnableSixAxisPollingData polling_data;
75};
76static_assert(sizeof(JoyEnableSixAxisPollingEntry) == 0x20,
77 "JoyEnableSixAxisPollingEntry is an invalid size");
78
79struct JoyButtonOnlyPollingEntry {
80 u64 sampling_number;
81 JoyButtonOnlyPollingData polling_data;
82};
83static_assert(sizeof(JoyButtonOnlyPollingEntry) == 0x40,
84 "JoyButtonOnlyPollingEntry is an invalid size");
85
86struct JoyDisableSixAxisDataAccessor {
87 DataAccessorHeader header{};
88 std::array<JoyDisableSixAxisPollingEntry, 0xb> entries{};
89};
90static_assert(sizeof(JoyDisableSixAxisDataAccessor) == 0x298,
91 "JoyDisableSixAxisDataAccessor is an invalid size");
92
93struct JoyEnableSixAxisDataAccessor {
94 DataAccessorHeader header{};
95 std::array<JoyEnableSixAxisPollingEntry, 0xb> entries{};
96};
97static_assert(sizeof(JoyEnableSixAxisDataAccessor) == 0x190,
98 "JoyEnableSixAxisDataAccessor is an invalid size");
99
100struct ButtonOnlyPollingDataAccessor {
101 DataAccessorHeader header;
102 std::array<JoyButtonOnlyPollingEntry, 0xb> entries;
103};
104static_assert(sizeof(ButtonOnlyPollingDataAccessor) == 0x2F0,
105 "ButtonOnlyPollingDataAccessor is an invalid size");
106
107class HidbusBase {
108public:
109 explicit HidbusBase(KernelHelpers::ServiceContext& service_context_);
110 virtual ~HidbusBase();
111
112 void ActivateDevice();
113
114 void DeactivateDevice();
115
116 bool IsDeviceActivated() const;
117
118 // Enables/disables the device
119 void Enable(bool enable);
120
121 // returns true if device is enabled
122 bool IsEnabled() const;
123
124 // returns true if polling mode is enabled
125 bool IsPollingMode() const;
126
127 // returns polling mode
128 JoyPollingMode GetPollingMode() const;
129
130 // Sets and enables JoyPollingMode
131 void SetPollingMode(JoyPollingMode mode);
132
133 // Disables JoyPollingMode
134 void DisablePollingMode();
135
136 // Called on EnableJoyPollingReceiveMode
137 void SetTransferMemoryPointer(u8* t_mem);
138
139 Kernel::KReadableEvent& GetSendCommandAsycEvent() const;
140
141 virtual void OnInit() {}
142
143 virtual void OnRelease() {}
144
145 // Updates device transfer memory
146 virtual void OnUpdate() {}
147
148 // Returns the device ID of the joycon
149 virtual u8 GetDeviceId() const {
150 return {};
151 }
152
153 // Assigns a command from data
154 virtual bool SetCommand(const std::vector<u8>& data) {
155 return {};
156 }
157
158 // Returns a reply from a command
159 virtual std::vector<u8> GetReply() const {
160 return {};
161 }
162
163protected:
164 bool is_activated{};
165 bool device_enabled{};
166 bool polling_mode_enabled{};
167 JoyPollingMode polling_mode = {};
168 JoyDisableSixAxisDataAccessor disable_sixaxis_data{};
169 JoyEnableSixAxisDataAccessor enable_sixaxis_data{};
170 ButtonOnlyPollingDataAccessor button_only_data{};
171
172 u8* transfer_memory{nullptr};
173 bool is_transfer_memory_set{};
174
175 Kernel::KEvent* send_command_asyc_event;
176 KernelHelpers::ServiceContext& service_context;
177};
178} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/ringcon.cpp b/src/core/hle/service/hid/hidbus/ringcon.cpp
new file mode 100644
index 000000000..6fe68081f
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/ringcon.cpp
@@ -0,0 +1,306 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hid/emulated_controller.h"
6#include "core/hid/hid_core.h"
7#include "core/hle/kernel/k_event.h"
8#include "core/hle/kernel/k_readable_event.h"
9#include "core/hle/service/hid/hidbus/ringcon.h"
10
11namespace Service::HID {
12
13RingController::RingController(Core::HID::HIDCore& hid_core_,
14 KernelHelpers::ServiceContext& service_context_)
15 : HidbusBase(service_context_) {
16 // Use the horizontal axis of left stick for emulating input
17 // There is no point on adding a frontend implementation since Ring Fit Adventure doesn't work
18 input = hid_core_.GetEmulatedController(Core::HID::NpadIdType::Player1);
19}
20
21RingController::~RingController() = default;
22
23void RingController::OnInit() {
24 return;
25}
26
27void RingController::OnRelease() {
28 return;
29};
30
31void RingController::OnUpdate() {
32 if (!is_activated) {
33 return;
34 }
35
36 if (!device_enabled) {
37 return;
38 }
39
40 if (!polling_mode_enabled || !is_transfer_memory_set) {
41 return;
42 }
43
44 switch (polling_mode) {
45 case JoyPollingMode::SixAxisSensorEnable: {
46 enable_sixaxis_data.header.total_entries = 10;
47 enable_sixaxis_data.header.result = ResultSuccess;
48 const auto& last_entry =
49 enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry];
50
51 enable_sixaxis_data.header.latest_entry =
52 (enable_sixaxis_data.header.latest_entry + 1) % 10;
53 auto& curr_entry = enable_sixaxis_data.entries[enable_sixaxis_data.header.latest_entry];
54
55 curr_entry.sampling_number = last_entry.sampling_number + 1;
56 curr_entry.polling_data.sampling_number = curr_entry.sampling_number;
57
58 const RingConData ringcon_value = GetSensorValue();
59 curr_entry.polling_data.out_size = sizeof(ringcon_value);
60 std::memcpy(curr_entry.polling_data.data.data(), &ringcon_value, sizeof(ringcon_value));
61
62 std::memcpy(transfer_memory, &enable_sixaxis_data, sizeof(enable_sixaxis_data));
63 break;
64 }
65 default:
66 LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
67 break;
68 }
69}
70
71RingController::RingConData RingController::GetSensorValue() const {
72 RingConData ringcon_sensor_value{
73 .status = DataValid::Valid,
74 .data = 0,
75 };
76
77 const f32 stick_value = static_cast<f32>(input->GetSticks().left.x) / 32767.0f;
78
79 ringcon_sensor_value.data = static_cast<s16>(stick_value * range) + idle_value;
80
81 return ringcon_sensor_value;
82}
83
84u8 RingController::GetDeviceId() const {
85 return device_id;
86}
87
88std::vector<u8> RingController::GetReply() const {
89 const RingConCommands current_command = command;
90
91 switch (current_command) {
92 case RingConCommands::GetFirmwareVersion:
93 return GetFirmwareVersionReply();
94 case RingConCommands::ReadId:
95 return GetReadIdReply();
96 case RingConCommands::c20105:
97 return GetC020105Reply();
98 case RingConCommands::ReadUnkCal:
99 return GetReadUnkCalReply();
100 case RingConCommands::ReadFactoryCal:
101 return GetReadFactoryCalReply();
102 case RingConCommands::ReadUserCal:
103 return GetReadUserCalReply();
104 case RingConCommands::ReadRepCount:
105 return GetReadRepCountReply();
106 case RingConCommands::ReadTotalPushCount:
107 return GetReadTotalPushCountReply();
108 case RingConCommands::SaveCalData:
109 return GetSaveDataReply();
110 default:
111 return GetErrorReply();
112 }
113}
114
115bool RingController::SetCommand(const std::vector<u8>& data) {
116 if (data.size() < 4) {
117 LOG_ERROR(Service_HID, "Command size not supported {}", data.size());
118 command = RingConCommands::Error;
119 return false;
120 }
121
122 // There must be a better way to do this
123 const u32 command_id =
124 u32{data[0]} + (u32{data[1]} << 8) + (u32{data[2]} << 16) + (u32{data[3]} << 24);
125 static constexpr std::array supported_commands = {
126 RingConCommands::GetFirmwareVersion,
127 RingConCommands::ReadId,
128 RingConCommands::c20105,
129 RingConCommands::ReadUnkCal,
130 RingConCommands::ReadFactoryCal,
131 RingConCommands::ReadUserCal,
132 RingConCommands::ReadRepCount,
133 RingConCommands::ReadTotalPushCount,
134 RingConCommands::SaveCalData,
135 };
136
137 for (RingConCommands cmd : supported_commands) {
138 if (command_id == static_cast<u32>(cmd)) {
139 return ExcecuteCommand(cmd, data);
140 }
141 }
142
143 LOG_ERROR(Service_HID, "Command not implemented {}", command_id);
144 command = RingConCommands::Error;
145 // Signal a reply to avoid softlocking
146 send_command_asyc_event->GetWritableEvent().Signal();
147 return false;
148}
149
150bool RingController::ExcecuteCommand(RingConCommands cmd, const std::vector<u8>& data) {
151 switch (cmd) {
152 case RingConCommands::GetFirmwareVersion:
153 case RingConCommands::ReadId:
154 case RingConCommands::c20105:
155 case RingConCommands::ReadUnkCal:
156 case RingConCommands::ReadFactoryCal:
157 case RingConCommands::ReadUserCal:
158 case RingConCommands::ReadRepCount:
159 case RingConCommands::ReadTotalPushCount:
160 ASSERT_MSG(data.size() == 0x4, "data.size is not 0x4 bytes");
161 command = cmd;
162 send_command_asyc_event->GetWritableEvent().Signal();
163 return true;
164 case RingConCommands::SaveCalData: {
165 ASSERT_MSG(data.size() == 0x14, "data.size is not 0x14 bytes");
166
167 SaveCalData save_info{};
168 std::memcpy(&save_info, &data, sizeof(SaveCalData));
169 user_calibration = save_info.calibration;
170
171 command = cmd;
172 send_command_asyc_event->GetWritableEvent().Signal();
173 return true;
174 }
175 default:
176 LOG_ERROR(Service_HID, "Command not implemented {}", cmd);
177 command = RingConCommands::Error;
178 return false;
179 }
180}
181
182std::vector<u8> RingController::GetFirmwareVersionReply() const {
183 const FirmwareVersionReply reply{
184 .status = DataValid::Valid,
185 .firmware = version,
186 };
187
188 return GetDataVector(reply);
189}
190
191std::vector<u8> RingController::GetReadIdReply() const {
192 // The values are hardcoded from a real joycon
193 const ReadIdReply reply{
194 .status = DataValid::Valid,
195 .id_l_x0 = 8,
196 .id_l_x0_2 = 41,
197 .id_l_x4 = 22294,
198 .id_h_x0 = 19777,
199 .id_h_x0_2 = 13621,
200 .id_h_x4 = 8245,
201 };
202
203 return GetDataVector(reply);
204}
205
206std::vector<u8> RingController::GetC020105Reply() const {
207 const Cmd020105Reply reply{
208 .status = DataValid::Valid,
209 .data = 1,
210 };
211
212 return GetDataVector(reply);
213}
214
215std::vector<u8> RingController::GetReadUnkCalReply() const {
216 const ReadUnkCalReply reply{
217 .status = DataValid::Valid,
218 .data = 0,
219 };
220
221 return GetDataVector(reply);
222}
223
224std::vector<u8> RingController::GetReadFactoryCalReply() const {
225 const ReadFactoryCalReply reply{
226 .status = DataValid::Valid,
227 .calibration = factory_calibration,
228 };
229
230 return GetDataVector(reply);
231}
232
233std::vector<u8> RingController::GetReadUserCalReply() const {
234 const ReadUserCalReply reply{
235 .status = DataValid::Valid,
236 .calibration = user_calibration,
237 };
238
239 return GetDataVector(reply);
240}
241
242std::vector<u8> RingController::GetReadRepCountReply() const {
243 // The values are hardcoded from a real joycon
244 const GetThreeByteReply reply{
245 .status = DataValid::Valid,
246 .data = {30, 0, 0},
247 .crc = GetCrcValue({30, 0, 0, 0}),
248 };
249
250 return GetDataVector(reply);
251}
252
253std::vector<u8> RingController::GetReadTotalPushCountReply() const {
254 // The values are hardcoded from a real joycon
255 const GetThreeByteReply reply{
256 .status = DataValid::Valid,
257 .data = {30, 0, 0},
258 .crc = GetCrcValue({30, 0, 0, 0}),
259 };
260
261 return GetDataVector(reply);
262}
263
264std::vector<u8> RingController::GetSaveDataReply() const {
265 const StatusReply reply{
266 .status = DataValid::Valid,
267 };
268
269 return GetDataVector(reply);
270}
271
272std::vector<u8> RingController::GetErrorReply() const {
273 const ErrorReply reply{
274 .status = DataValid::BadCRC,
275 };
276
277 return GetDataVector(reply);
278}
279
280u8 RingController::GetCrcValue(const std::vector<u8>& data) const {
281 u8 crc = 0;
282 for (std::size_t index = 0; index < data.size(); index++) {
283 for (u8 i = 0x80; i > 0; i >>= 1) {
284 bool bit = (crc & 0x80) != 0;
285 if ((data[index] & i) != 0) {
286 bit = !bit;
287 }
288 crc <<= 1;
289 if (bit) {
290 crc ^= 0x8d;
291 }
292 }
293 }
294 return crc;
295}
296
297template <typename T>
298std::vector<u8> RingController::GetDataVector(const T& reply) const {
299 static_assert(std::is_trivially_copyable_v<T>);
300 std::vector<u8> data;
301 data.resize(sizeof(reply));
302 std::memcpy(data.data(), &reply, sizeof(reply));
303 return data;
304}
305
306} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/ringcon.h b/src/core/hle/service/hid/hidbus/ringcon.h
new file mode 100644
index 000000000..e8b3d8254
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/ringcon.h
@@ -0,0 +1,247 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8
9#include "common/common_types.h"
10#include "core/hle/service/hid/hidbus/hidbus_base.h"
11
12namespace Core::HID {
13class EmulatedController;
14} // namespace Core::HID
15
16namespace Service::HID {
17
18class RingController final : public HidbusBase {
19public:
20 explicit RingController(Core::HID::HIDCore& hid_core_,
21 KernelHelpers::ServiceContext& service_context_);
22 ~RingController() override;
23
24 void OnInit() override;
25
26 void OnRelease() override;
27
28 // Updates ringcon transfer memory
29 void OnUpdate() override;
30
31 // Returns the device ID of the joycon
32 u8 GetDeviceId() const override;
33
34 // Assigns a command from data
35 bool SetCommand(const std::vector<u8>& data) override;
36
37 // Returns a reply from a command
38 std::vector<u8> GetReply() const override;
39
40private:
41 // These values are obtained from a real ring controller
42 static constexpr s16 idle_value = 2280;
43 static constexpr s16 idle_deadzone = 120;
44 static constexpr s16 range = 2500;
45
46 enum class RingConCommands : u32 {
47 GetFirmwareVersion = 0x00020000,
48 ReadId = 0x00020100,
49 JoyPolling = 0x00020101,
50 Unknown1 = 0x00020104,
51 c20105 = 0x00020105,
52 Unknown2 = 0x00020204,
53 Unknown3 = 0x00020304,
54 Unknown4 = 0x00020404,
55 ReadUnkCal = 0x00020504,
56 ReadFactoryCal = 0x00020A04,
57 Unknown5 = 0x00021104,
58 Unknown6 = 0x00021204,
59 Unknown7 = 0x00021304,
60 ReadUserCal = 0x00021A04,
61 ReadRepCount = 0x00023104,
62 ReadTotalPushCount = 0x00023204,
63 Unknown9 = 0x04013104,
64 Unknown10 = 0x04011104,
65 Unknown11 = 0x04011204,
66 Unknown12 = 0x04011304,
67 SaveCalData = 0x10011A04,
68 Error = 0xFFFFFFFF,
69 };
70
71 enum class DataValid : u32 {
72 Valid,
73 BadCRC,
74 Cal,
75 };
76
77 struct FirmwareVersion {
78 u8 sub;
79 u8 main;
80 };
81 static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid size");
82
83 struct FactoryCalibration {
84 s32_le os_max;
85 s32_le hk_max;
86 s32_le zero_min;
87 s32_le zero_max;
88 };
89 static_assert(sizeof(FactoryCalibration) == 0x10, "FactoryCalibration is an invalid size");
90
91 struct CalibrationValue {
92 s16 value;
93 u16 crc;
94 };
95 static_assert(sizeof(CalibrationValue) == 0x4, "CalibrationValue is an invalid size");
96
97 struct UserCalibration {
98 CalibrationValue os_max;
99 CalibrationValue hk_max;
100 CalibrationValue zero;
101 };
102 static_assert(sizeof(UserCalibration) == 0xC, "UserCalibration is an invalid size");
103
104 struct SaveCalData {
105 RingConCommands command;
106 UserCalibration calibration;
107 INSERT_PADDING_BYTES_NOINIT(4);
108 };
109 static_assert(sizeof(SaveCalData) == 0x14, "SaveCalData is an invalid size");
110 static_assert(std::is_trivially_copyable_v<SaveCalData>,
111 "SaveCalData must be trivially copyable");
112
113 struct FirmwareVersionReply {
114 DataValid status;
115 FirmwareVersion firmware;
116 INSERT_PADDING_BYTES(0x2);
117 };
118 static_assert(sizeof(FirmwareVersionReply) == 0x8, "FirmwareVersionReply is an invalid size");
119
120 struct Cmd020105Reply {
121 DataValid status;
122 u8 data;
123 INSERT_PADDING_BYTES(0x3);
124 };
125 static_assert(sizeof(Cmd020105Reply) == 0x8, "Cmd020105Reply is an invalid size");
126
127 struct StatusReply {
128 DataValid status;
129 };
130 static_assert(sizeof(StatusReply) == 0x4, "StatusReply is an invalid size");
131
132 struct GetThreeByteReply {
133 DataValid status;
134 std::array<u8, 3> data;
135 u8 crc;
136 };
137 static_assert(sizeof(GetThreeByteReply) == 0x8, "GetThreeByteReply is an invalid size");
138
139 struct ReadUnkCalReply {
140 DataValid status;
141 u16 data;
142 INSERT_PADDING_BYTES(0x2);
143 };
144 static_assert(sizeof(ReadUnkCalReply) == 0x8, "ReadUnkCalReply is an invalid size");
145
146 struct ReadFactoryCalReply {
147 DataValid status;
148 FactoryCalibration calibration;
149 };
150 static_assert(sizeof(ReadFactoryCalReply) == 0x14, "ReadFactoryCalReply is an invalid size");
151
152 struct ReadUserCalReply {
153 DataValid status;
154 UserCalibration calibration;
155 INSERT_PADDING_BYTES(0x4);
156 };
157 static_assert(sizeof(ReadUserCalReply) == 0x14, "ReadUserCalReply is an invalid size");
158
159 struct ReadIdReply {
160 DataValid status;
161 u16 id_l_x0;
162 u16 id_l_x0_2;
163 u16 id_l_x4;
164 u16 id_h_x0;
165 u16 id_h_x0_2;
166 u16 id_h_x4;
167 };
168 static_assert(sizeof(ReadIdReply) == 0x10, "ReadIdReply is an invalid size");
169
170 struct ErrorReply {
171 DataValid status;
172 INSERT_PADDING_BYTES(0x3);
173 };
174 static_assert(sizeof(ErrorReply) == 0x8, "ErrorReply is an invalid size");
175
176 struct RingConData {
177 DataValid status;
178 s16_le data;
179 INSERT_PADDING_BYTES(0x2);
180 };
181 static_assert(sizeof(RingConData) == 0x8, "RingConData is an invalid size");
182
183 // Executes the command requested
184 bool ExcecuteCommand(RingConCommands cmd, const std::vector<u8>& data);
185
186 // Returns RingConData struct with pressure sensor values
187 RingConData GetSensorValue() const;
188
189 // Returns 8 byte reply with firmware version
190 std::vector<u8> GetFirmwareVersionReply() const;
191
192 // Returns 16 byte reply with ID values
193 std::vector<u8> GetReadIdReply() const;
194
195 // (STUBBED) Returns 8 byte reply
196 std::vector<u8> GetC020105Reply() const;
197
198 // (STUBBED) Returns 8 byte empty reply
199 std::vector<u8> GetReadUnkCalReply() const;
200
201 // Returns 20 byte reply with factory calibration values
202 std::vector<u8> GetReadFactoryCalReply() const;
203
204 // Returns 20 byte reply with user calibration values
205 std::vector<u8> GetReadUserCalReply() const;
206
207 // (STUBBED) Returns 8 byte reply
208 std::vector<u8> GetReadRepCountReply() const;
209
210 // (STUBBED) Returns 8 byte reply
211 std::vector<u8> GetReadTotalPushCountReply() const;
212
213 // Returns 4 byte save data reply
214 std::vector<u8> GetSaveDataReply() const;
215
216 // Returns 8 byte error reply
217 std::vector<u8> GetErrorReply() const;
218
219 // Returns 8 bit redundancy check from provided data
220 u8 GetCrcValue(const std::vector<u8>& data) const;
221
222 // Converts structs to an u8 vector equivalent
223 template <typename T>
224 std::vector<u8> GetDataVector(const T& reply) const;
225
226 RingConCommands command{RingConCommands::Error};
227
228 const u8 device_id = 0x20;
229 const FirmwareVersion version = {
230 .sub = 0x0,
231 .main = 0x2c,
232 };
233 const FactoryCalibration factory_calibration = {
234 .os_max = idle_value + range + idle_deadzone,
235 .hk_max = idle_value - range - idle_deadzone,
236 .zero_min = idle_value - idle_deadzone,
237 .zero_max = idle_value + idle_deadzone,
238 };
239 UserCalibration user_calibration = {
240 .os_max = {.value = range, .crc = 228},
241 .hk_max = {.value = -range, .crc = 239},
242 .zero = {.value = idle_value, .crc = 225},
243 };
244
245 Core::HID::EmulatedController* input;
246};
247} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/starlink.cpp b/src/core/hle/service/hid/hidbus/starlink.cpp
new file mode 100644
index 000000000..3175c48da
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/starlink.cpp
@@ -0,0 +1,51 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hid/emulated_controller.h"
6#include "core/hid/hid_core.h"
7#include "core/hle/service/hid/hidbus/starlink.h"
8
9namespace Service::HID {
10constexpr u8 DEVICE_ID = 0x28;
11
12Starlink::Starlink(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service_context_)
13 : HidbusBase(service_context_) {}
14Starlink::~Starlink() = default;
15
16void Starlink::OnInit() {
17 return;
18}
19
20void Starlink::OnRelease() {
21 return;
22};
23
24void Starlink::OnUpdate() {
25 if (!is_activated) {
26 return;
27 }
28 if (!device_enabled) {
29 return;
30 }
31 if (!polling_mode_enabled || !is_transfer_memory_set) {
32 return;
33 }
34
35 LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
36}
37
38u8 Starlink::GetDeviceId() const {
39 return DEVICE_ID;
40}
41
42std::vector<u8> Starlink::GetReply() const {
43 return {};
44}
45
46bool Starlink::SetCommand(const std::vector<u8>& data) {
47 LOG_ERROR(Service_HID, "Command not implemented");
48 return false;
49}
50
51} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/starlink.h b/src/core/hle/service/hid/hidbus/starlink.h
new file mode 100644
index 000000000..79770b68e
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/starlink.h
@@ -0,0 +1,39 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "core/hle/service/hid/hidbus/hidbus_base.h"
9
10namespace Core::HID {
11class EmulatedController;
12} // namespace Core::HID
13
14namespace Service::HID {
15
16class Starlink final : public HidbusBase {
17public:
18 explicit Starlink(Core::HID::HIDCore& hid_core_,
19 KernelHelpers::ServiceContext& service_context_);
20 ~Starlink() override;
21
22 void OnInit() override;
23
24 void OnRelease() override;
25
26 // Updates ringcon transfer memory
27 void OnUpdate() override;
28
29 // Returns the device ID of the joycon
30 u8 GetDeviceId() const override;
31
32 // Assigns a command from data
33 bool SetCommand(const std::vector<u8>& data) override;
34
35 // Returns a reply from a command
36 std::vector<u8> GetReply() const override;
37};
38
39} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/stubbed.cpp b/src/core/hle/service/hid/hidbus/stubbed.cpp
new file mode 100644
index 000000000..5474657be
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/stubbed.cpp
@@ -0,0 +1,52 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/hid/emulated_controller.h"
6#include "core/hid/hid_core.h"
7#include "core/hle/service/hid/hidbus/stubbed.h"
8
9namespace Service::HID {
10constexpr u8 DEVICE_ID = 0xFF;
11
12HidbusStubbed::HidbusStubbed(Core::HID::HIDCore& hid_core_,
13 KernelHelpers::ServiceContext& service_context_)
14 : HidbusBase(service_context_) {}
15HidbusStubbed::~HidbusStubbed() = default;
16
17void HidbusStubbed::OnInit() {
18 return;
19}
20
21void HidbusStubbed::OnRelease() {
22 return;
23};
24
25void HidbusStubbed::OnUpdate() {
26 if (!is_activated) {
27 return;
28 }
29 if (!device_enabled) {
30 return;
31 }
32 if (!polling_mode_enabled || !is_transfer_memory_set) {
33 return;
34 }
35
36 LOG_ERROR(Service_HID, "Polling mode not supported {}", polling_mode);
37}
38
39u8 HidbusStubbed::GetDeviceId() const {
40 return DEVICE_ID;
41}
42
43std::vector<u8> HidbusStubbed::GetReply() const {
44 return {};
45}
46
47bool HidbusStubbed::SetCommand(const std::vector<u8>& data) {
48 LOG_ERROR(Service_HID, "Command not implemented");
49 return false;
50}
51
52} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hidbus/stubbed.h b/src/core/hle/service/hid/hidbus/stubbed.h
new file mode 100644
index 000000000..40acdfe8f
--- /dev/null
+++ b/src/core/hle/service/hid/hidbus/stubbed.h
@@ -0,0 +1,39 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "core/hle/service/hid/hidbus/hidbus_base.h"
9
10namespace Core::HID {
11class EmulatedController;
12} // namespace Core::HID
13
14namespace Service::HID {
15
16class HidbusStubbed final : public HidbusBase {
17public:
18 explicit HidbusStubbed(Core::HID::HIDCore& hid_core_,
19 KernelHelpers::ServiceContext& service_context_);
20 ~HidbusStubbed() override;
21
22 void OnInit() override;
23
24 void OnRelease() override;
25
26 // Updates ringcon transfer memory
27 void OnUpdate() override;
28
29 // Returns the device ID of the joycon
30 u8 GetDeviceId() const override;
31
32 // Assigns a command from data
33 bool SetCommand(const std::vector<u8>& data) override;
34
35 // Returns a reply from a command
36 std::vector<u8> GetReply() const override;
37};
38
39} // namespace Service::HID