summaryrefslogtreecommitdiff
path: root/src/input_common/gcadapter/gc_adapter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/input_common/gcadapter/gc_adapter.cpp')
-rw-r--r--src/input_common/gcadapter/gc_adapter.cpp398
1 files changed, 398 insertions, 0 deletions
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp
new file mode 100644
index 000000000..6d9f4d9eb
--- /dev/null
+++ b/src/input_common/gcadapter/gc_adapter.cpp
@@ -0,0 +1,398 @@
1// Copyright 2014 Dolphin Emulator Project
2// Licensed under GPLv2+
3// Refer to the license.txt file included.
4
5#include <chrono>
6#include <thread>
7#include "common/logging/log.h"
8#include "input_common/gcadapter/gc_adapter.h"
9
10namespace GCAdapter {
11
12/// Used to loop through and assign button in poller
13constexpr std::array<PadButton, 12> PadButtonArray{
14 PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT, PadButton::PAD_BUTTON_DOWN,
15 PadButton::PAD_BUTTON_UP, PadButton::PAD_TRIGGER_Z, PadButton::PAD_TRIGGER_R,
16 PadButton::PAD_TRIGGER_L, PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B,
17 PadButton::PAD_BUTTON_X, PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_START,
18};
19
20Adapter::Adapter() {
21 if (usb_adapter_handle != nullptr) {
22 return;
23 }
24 LOG_INFO(Input, "GC Adapter Initialization started");
25
26 current_status = NO_ADAPTER_DETECTED;
27
28 const int init_res = libusb_init(&libusb_ctx);
29 if (init_res == LIBUSB_SUCCESS) {
30 StartScanThread();
31 } else {
32 LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res);
33 }
34}
35
36GCPadStatus Adapter::GetPadStatus(int port, const std::array<u8, 37>& adapter_payload) {
37 GCPadStatus pad = {};
38 bool get_origin = false;
39
40 ControllerTypes type = ControllerTypes(adapter_payload[1 + (9 * port)] >> 4);
41 if (type != ControllerTypes::None) {
42 get_origin = true;
43 }
44
45 adapter_controllers_status[port] = type;
46
47 static constexpr std::array<PadButton, 8> b1_buttons{
48 PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B, PadButton::PAD_BUTTON_X,
49 PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT,
50 PadButton::PAD_BUTTON_DOWN, PadButton::PAD_BUTTON_UP,
51 };
52
53 static constexpr std::array<PadButton, 4> b2_buttons{
54 PadButton::PAD_BUTTON_START,
55 PadButton::PAD_TRIGGER_Z,
56 PadButton::PAD_TRIGGER_R,
57 PadButton::PAD_TRIGGER_L,
58 };
59
60 if (adapter_controllers_status[port] != ControllerTypes::None) {
61 const u8 b1 = adapter_payload[1 + (9 * port) + 1];
62 const u8 b2 = adapter_payload[1 + (9 * port) + 2];
63
64 for (std::size_t i = 0; i < b1_buttons.size(); ++i) {
65 if ((b1 & (1U << i)) != 0) {
66 pad.button |= static_cast<u16>(b1_buttons[i]);
67 }
68 }
69
70 for (std::size_t j = 0; j < b2_buttons.size(); ++j) {
71 if ((b2 & (1U << j)) != 0) {
72 pad.button |= static_cast<u16>(b2_buttons[j]);
73 }
74 }
75
76 if (get_origin) {
77 pad.button |= PAD_GET_ORIGIN;
78 }
79
80 pad.stick_x = adapter_payload[1 + (9 * port) + 3];
81 pad.stick_y = adapter_payload[1 + (9 * port) + 4];
82 pad.substick_x = adapter_payload[1 + (9 * port) + 5];
83 pad.substick_y = adapter_payload[1 + (9 * port) + 6];
84 pad.trigger_left = adapter_payload[1 + (9 * port) + 7];
85 pad.trigger_right = adapter_payload[1 + (9 * port) + 8];
86 }
87 return pad;
88}
89
90void Adapter::PadToState(const GCPadStatus& pad, GCState& state) {
91 for (const auto& button : PadButtonArray) {
92 const u16 button_value = static_cast<u16>(button);
93 state.buttons.insert_or_assign(button_value, pad.button & button_value);
94 }
95
96 state.axes.insert_or_assign(static_cast<u8>(PadAxes::StickX), pad.stick_x);
97 state.axes.insert_or_assign(static_cast<u8>(PadAxes::StickY), pad.stick_y);
98 state.axes.insert_or_assign(static_cast<u8>(PadAxes::SubstickX), pad.substick_x);
99 state.axes.insert_or_assign(static_cast<u8>(PadAxes::SubstickY), pad.substick_y);
100 state.axes.insert_or_assign(static_cast<u8>(PadAxes::TriggerLeft), pad.trigger_left);
101 state.axes.insert_or_assign(static_cast<u8>(PadAxes::TriggerRight), pad.trigger_right);
102}
103
104void Adapter::Read() {
105 LOG_DEBUG(Input, "GC Adapter Read() thread started");
106
107 int payload_size_in, payload_size_copy;
108 std::array<u8, 37> adapter_payload;
109 std::array<u8, 37> adapter_payload_copy;
110 std::array<GCPadStatus, 4> pads;
111
112 while (adapter_thread_running) {
113 libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(),
114 sizeof(adapter_payload), &payload_size_in, 16);
115 payload_size_copy = 0;
116 // this mutex might be redundant?
117 {
118 std::lock_guard<std::mutex> lk(s_mutex);
119 std::copy(std::begin(adapter_payload), std::end(adapter_payload),
120 std::begin(adapter_payload_copy));
121 payload_size_copy = payload_size_in;
122 }
123
124 if (payload_size_copy != sizeof(adapter_payload_copy) ||
125 adapter_payload_copy[0] != LIBUSB_DT_HID) {
126 LOG_ERROR(Input, "error reading payload (size: {}, type: {:02x})", payload_size_copy,
127 adapter_payload_copy[0]);
128 adapter_thread_running = false; // error reading from adapter, stop reading.
129 break;
130 }
131 for (std::size_t port = 0; port < pads.size(); ++port) {
132 pads[port] = GetPadStatus(port, adapter_payload_copy);
133 if (DeviceConnected(port) && configuring) {
134 if (pads[port].button != PAD_GET_ORIGIN) {
135 pad_queue[port].Push(pads[port]);
136 }
137
138 // Accounting for a threshold here because of some controller variance
139 if (pads[port].stick_x > pads[port].MAIN_STICK_CENTER_X + pads[port].THRESHOLD ||
140 pads[port].stick_x < pads[port].MAIN_STICK_CENTER_X - pads[port].THRESHOLD) {
141 pads[port].axis = GCAdapter::PadAxes::StickX;
142 pads[port].axis_value = pads[port].stick_x;
143 pad_queue[port].Push(pads[port]);
144 }
145 if (pads[port].stick_y > pads[port].MAIN_STICK_CENTER_Y + pads[port].THRESHOLD ||
146 pads[port].stick_y < pads[port].MAIN_STICK_CENTER_Y - pads[port].THRESHOLD) {
147 pads[port].axis = GCAdapter::PadAxes::StickY;
148 pads[port].axis_value = pads[port].stick_y;
149 pad_queue[port].Push(pads[port]);
150 }
151 if (pads[port].substick_x > pads[port].C_STICK_CENTER_X + pads[port].THRESHOLD ||
152 pads[port].substick_x < pads[port].C_STICK_CENTER_X - pads[port].THRESHOLD) {
153 pads[port].axis = GCAdapter::PadAxes::SubstickX;
154 pads[port].axis_value = pads[port].substick_x;
155 pad_queue[port].Push(pads[port]);
156 }
157 if (pads[port].substick_y > pads[port].C_STICK_CENTER_Y + pads[port].THRESHOLD ||
158 pads[port].substick_y < pads[port].C_STICK_CENTER_Y - pads[port].THRESHOLD) {
159 pads[port].axis = GCAdapter::PadAxes::SubstickY;
160 pads[port].axis_value = pads[port].substick_y;
161 pad_queue[port].Push(pads[port]);
162 }
163 if (pads[port].trigger_left > pads[port].TRIGGER_THRESHOLD) {
164 pads[port].axis = GCAdapter::PadAxes::TriggerLeft;
165 pads[port].axis_value = pads[port].trigger_left;
166 pad_queue[port].Push(pads[port]);
167 }
168 if (pads[port].trigger_right > pads[port].TRIGGER_THRESHOLD) {
169 pads[port].axis = GCAdapter::PadAxes::TriggerRight;
170 pads[port].axis_value = pads[port].trigger_right;
171 pad_queue[port].Push(pads[port]);
172 }
173 }
174 PadToState(pads[port], state[port]);
175 }
176 std::this_thread::yield();
177 }
178}
179
180void Adapter::ScanThreadFunc() {
181 LOG_INFO(Input, "GC Adapter scanning thread started");
182
183 while (detect_thread_running) {
184 if (usb_adapter_handle == nullptr) {
185 std::lock_guard<std::mutex> lk(initialization_mutex);
186 Setup();
187 }
188 std::this_thread::sleep_for(std::chrono::milliseconds(500));
189 }
190}
191
192void Adapter::StartScanThread() {
193 if (detect_thread_running) {
194 return;
195 }
196 if (!libusb_ctx) {
197 return;
198 }
199
200 detect_thread_running = true;
201 detect_thread = std::thread([=] { ScanThreadFunc(); });
202}
203
204void Adapter::StopScanThread() {
205 detect_thread_running = false;
206 detect_thread.join();
207}
208
209void Adapter::Setup() {
210 // Reset the error status in case the adapter gets unplugged
211 if (current_status < 0) {
212 current_status = NO_ADAPTER_DETECTED;
213 }
214
215 adapter_controllers_status.fill(ControllerTypes::None);
216
217 // pointer to list of connected usb devices
218 libusb_device** devices{};
219
220 // populate the list of devices, get the count
221 const ssize_t device_count = libusb_get_device_list(libusb_ctx, &devices);
222 if (device_count < 0) {
223 LOG_ERROR(Input, "libusb_get_device_list failed with error: {}", device_count);
224 detect_thread_running = false; // Stop the loop constantly checking for gc adapter
225 // TODO: For hotplug+gc adapter checkbox implementation, revert this.
226 return;
227 }
228
229 if (devices != nullptr) {
230 for (std::size_t index = 0; index < device_count; ++index) {
231 if (CheckDeviceAccess(devices[index])) {
232 // GC Adapter found and accessible, registering it
233 GetGCEndpoint(devices[index]);
234 break;
235 }
236 }
237 libusb_free_device_list(devices, 1);
238 }
239}
240
241bool Adapter::CheckDeviceAccess(libusb_device* device) {
242 libusb_device_descriptor desc;
243 const int get_descriptor_error = libusb_get_device_descriptor(device, &desc);
244 if (get_descriptor_error) {
245 // could not acquire the descriptor, no point in trying to use it.
246 LOG_ERROR(Input, "libusb_get_device_descriptor failed with error: {}",
247 get_descriptor_error);
248 return false;
249 }
250
251 if (desc.idVendor != 0x057e || desc.idProduct != 0x0337) {
252 // This isn't the device we are looking for.
253 return false;
254 }
255 const int open_error = libusb_open(device, &usb_adapter_handle);
256
257 if (open_error == LIBUSB_ERROR_ACCESS) {
258 LOG_ERROR(Input, "Yuzu can not gain access to this device: ID {:04X}:{:04X}.",
259 desc.idVendor, desc.idProduct);
260 return false;
261 }
262 if (open_error) {
263 LOG_ERROR(Input, "libusb_open failed to open device with error = {}", open_error);
264 return false;
265 }
266
267 int kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0);
268 if (kernel_driver_error == 1) {
269 kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0);
270 if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
271 LOG_ERROR(Input, "libusb_detach_kernel_driver failed with error = {}",
272 kernel_driver_error);
273 }
274 }
275
276 if (kernel_driver_error && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
277 libusb_close(usb_adapter_handle);
278 usb_adapter_handle = nullptr;
279 return false;
280 }
281
282 const int interface_claim_error = libusb_claim_interface(usb_adapter_handle, 0);
283 if (interface_claim_error) {
284 LOG_ERROR(Input, "libusb_claim_interface failed with error = {}", interface_claim_error);
285 libusb_close(usb_adapter_handle);
286 usb_adapter_handle = nullptr;
287 return false;
288 }
289
290 return true;
291}
292
293void Adapter::GetGCEndpoint(libusb_device* device) {
294 libusb_config_descriptor* config = nullptr;
295 const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config);
296 if (config_descriptor_return != LIBUSB_SUCCESS) {
297 LOG_ERROR(Input, "libusb_get_config_descriptor failed with error = {}",
298 config_descriptor_return);
299 return;
300 }
301
302 for (u8 ic = 0; ic < config->bNumInterfaces; ic++) {
303 const libusb_interface* interfaceContainer = &config->interface[ic];
304 for (int i = 0; i < interfaceContainer->num_altsetting; i++) {
305 const libusb_interface_descriptor* interface = &interfaceContainer->altsetting[i];
306 for (u8 e = 0; e < interface->bNumEndpoints; e++) {
307 const libusb_endpoint_descriptor* endpoint = &interface->endpoint[e];
308 if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) {
309 input_endpoint = endpoint->bEndpointAddress;
310 } else {
311 output_endpoint = endpoint->bEndpointAddress;
312 }
313 }
314 }
315 }
316 // This transfer seems to be responsible for clearing the state of the adapter
317 // Used to clear the "busy" state of when the device is unexpectedly unplugged
318 unsigned char clear_payload = 0x13;
319 libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload,
320 sizeof(clear_payload), nullptr, 16);
321
322 adapter_thread_running = true;
323 current_status = ADAPTER_DETECTED;
324 adapter_input_thread = std::thread([=] { Read(); }); // Read input
325}
326
327Adapter::~Adapter() {
328 StopScanThread();
329 Reset();
330}
331
332void Adapter::Reset() {
333 std::unique_lock<std::mutex> lock(initialization_mutex, std::defer_lock);
334 if (!lock.try_lock()) {
335 return;
336 }
337 if (current_status != ADAPTER_DETECTED) {
338 return;
339 }
340
341 if (adapter_thread_running) {
342 adapter_thread_running = false;
343 }
344 adapter_input_thread.join();
345
346 adapter_controllers_status.fill(ControllerTypes::None);
347 current_status = NO_ADAPTER_DETECTED;
348
349 if (usb_adapter_handle) {
350 libusb_release_interface(usb_adapter_handle, 1);
351 libusb_close(usb_adapter_handle);
352 usb_adapter_handle = nullptr;
353 }
354
355 if (libusb_ctx) {
356 libusb_exit(libusb_ctx);
357 }
358}
359
360bool Adapter::DeviceConnected(int port) {
361 return adapter_controllers_status[port] != ControllerTypes::None;
362}
363
364void Adapter::ResetDeviceType(int port) {
365 adapter_controllers_status[port] = ControllerTypes::None;
366}
367
368void Adapter::BeginConfiguration() {
369 for (auto& pq : pad_queue) {
370 pq.Clear();
371 }
372 configuring = true;
373}
374
375void Adapter::EndConfiguration() {
376 for (auto& pq : pad_queue) {
377 pq.Clear();
378 }
379 configuring = false;
380}
381
382std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() {
383 return pad_queue;
384}
385
386const std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() const {
387 return pad_queue;
388}
389
390std::array<GCState, 4>& Adapter::GetPadState() {
391 return state;
392}
393
394const std::array<GCState, 4>& Adapter::GetPadState() const {
395 return state;
396}
397
398} // namespace GCAdapter