| 
									
										
										
										
											2015-05-24 06:55:12 +02:00
										 |  |  | // Copyright 2014 Dolphin Emulator Project
 | 
					
						
							| 
									
										
										
										
											2015-05-18 01:08:10 +02:00
										 |  |  | // Licensed under GPLv2+
 | 
					
						
							| 
									
										
										
										
											2014-12-10 20:45:45 +11:00
										 |  |  | // Refer to the license.txt file included.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-08 18:13:58 +01:00
										 |  |  | #include <algorithm>
 | 
					
						
							| 
									
										
										
										
											2014-12-10 20:45:45 +11:00
										 |  |  | #include <libusb.h>
 | 
					
						
							| 
									
										
										
										
											2017-01-15 20:50:26 +01:00
										 |  |  | #include <memory>
 | 
					
						
							| 
									
										
										
										
											2015-05-26 22:44:51 -05:00
										 |  |  | #include <mutex>
 | 
					
						
							| 
									
										
										
										
											2014-12-10 20:45:45 +11:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-08 17:28:03 -04:00
										 |  |  | #include "Common/Flag.h"
 | 
					
						
							| 
									
										
										
										
											2017-01-15 20:50:26 +01:00
										 |  |  | #include "Common/LibusbContext.h"
 | 
					
						
							| 
									
										
										
										
											2015-12-31 11:09:47 -06:00
										 |  |  | #include "Common/Logging/Log.h"
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | #include "Common/Thread.h"
 | 
					
						
							| 
									
										
										
										
											2014-12-10 20:45:45 +11:00
										 |  |  | #include "Core/ConfigManager.h"
 | 
					
						
							| 
									
										
										
										
											2014-12-24 10:49:53 +11:00
										 |  |  | #include "Core/Core.h"
 | 
					
						
							|  |  |  | #include "Core/CoreTiming.h"
 | 
					
						
							| 
									
										
										
										
											2017-01-20 18:45:11 -05:00
										 |  |  | #include "Core/HW/SI/SI.h"
 | 
					
						
							| 
									
										
										
										
											2014-12-24 10:49:53 +11:00
										 |  |  | #include "Core/HW/SystemTimers.h"
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | #include "Core/NetPlayProto.h"
 | 
					
						
							| 
									
										
										
										
											2015-12-31 11:09:47 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "InputCommon/GCAdapter.h"
 | 
					
						
							| 
									
										
										
										
											2015-05-08 17:28:03 -04:00
										 |  |  | #include "InputCommon/GCPadStatus.h"
 | 
					
						
							| 
									
										
										
										
											2014-12-10 20:45:45 +11:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-31 11:09:47 -06:00
										 |  |  | namespace GCAdapter | 
					
						
							| 
									
										
										
										
											2014-12-10 20:45:45 +11:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2015-04-26 23:53:36 +02:00
										 |  |  | static bool CheckDeviceAccess(libusb_device* device); | 
					
						
							|  |  |  | static void AddGCAdapter(libusb_device* device); | 
					
						
							| 
									
										
										
										
											2016-04-30 13:29:11 +02:00
										 |  |  | static void ResetRumbleLockNeeded(); | 
					
						
							| 
									
										
										
										
											2016-05-19 21:52:44 +02:00
										 |  |  | static void Reset(); | 
					
						
							|  |  |  | static void Setup(); | 
					
						
							| 
									
										
										
										
											2015-04-26 23:53:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-24 10:49:53 +11:00
										 |  |  | static bool s_detected = false; | 
					
						
							| 
									
										
										
										
											2014-12-11 11:15:36 +11:00
										 |  |  | static libusb_device_handle* s_handle = nullptr; | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | static u8 s_controller_type[MAX_SI_CHANNELS] = { | 
					
						
							|  |  |  |     ControllerTypes::CONTROLLER_NONE, ControllerTypes::CONTROLLER_NONE, | 
					
						
							|  |  |  |     ControllerTypes::CONTROLLER_NONE, ControllerTypes::CONTROLLER_NONE}; | 
					
						
							| 
									
										
										
										
											2014-12-11 14:06:35 +11:00
										 |  |  | static u8 s_controller_rumble[4]; | 
					
						
							| 
									
										
										
										
											2014-12-10 20:45:45 +11:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-11 11:15:36 +11:00
										 |  |  | static std::mutex s_mutex; | 
					
						
							|  |  |  | static u8 s_controller_payload[37]; | 
					
						
							| 
									
										
										
										
											2015-01-05 12:43:26 +11:00
										 |  |  | static u8 s_controller_payload_swap[37]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-23 18:30:03 +01:00
										 |  |  | static std::atomic<int> s_controller_payload_size = {0}; | 
					
						
							| 
									
										
										
										
											2014-12-10 20:45:45 +11:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-11 11:15:36 +11:00
										 |  |  | static std::thread s_adapter_thread; | 
					
						
							|  |  |  | static Common::Flag s_adapter_thread_running; | 
					
						
							| 
									
										
										
										
											2014-12-10 20:45:45 +11:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-30 13:29:11 +02:00
										 |  |  | static std::mutex s_init_mutex; | 
					
						
							| 
									
										
										
										
											2015-04-26 23:53:36 +02:00
										 |  |  | static std::thread s_adapter_detect_thread; | 
					
						
							|  |  |  | static Common::Flag s_adapter_detect_thread_running; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static std::function<void(void)> s_detect_callback; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-11 11:15:36 +11:00
										 |  |  | static bool s_libusb_driver_not_supported = false; | 
					
						
							| 
									
										
										
										
											2017-01-15 20:50:26 +01:00
										 |  |  | static std::shared_ptr<libusb_context> s_libusb_context; | 
					
						
							| 
									
										
										
										
											2016-12-26 22:50:36 -08:00
										 |  |  | #if defined(__FreeBSD__) && __FreeBSD__ >= 11
 | 
					
						
							|  |  |  | static bool s_libusb_hotplug_enabled = true; | 
					
						
							|  |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2015-04-26 23:53:36 +02:00
										 |  |  | static bool s_libusb_hotplug_enabled = false; | 
					
						
							| 
									
										
										
										
											2016-12-26 22:50:36 -08:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2016-01-03 00:53:31 -08:00
										 |  |  | #if defined(LIBUSB_API_VERSION) && LIBUSB_API_VERSION >= 0x01000102
 | 
					
						
							| 
									
										
										
										
											2015-04-26 23:53:36 +02:00
										 |  |  | static libusb_hotplug_callback_handle s_hotplug_handle; | 
					
						
							| 
									
										
										
										
											2016-01-03 00:53:31 -08:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2014-12-10 20:45:45 +11:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-11 11:15:36 +11:00
										 |  |  | static u8 s_endpoint_in = 0; | 
					
						
							|  |  |  | static u8 s_endpoint_out = 0; | 
					
						
							| 
									
										
										
										
											2014-12-10 21:35:30 +11:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-24 10:49:53 +11:00
										 |  |  | static u64 s_last_init = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-05 12:43:26 +11:00
										 |  |  | static void Read() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   int payload_size = 0; | 
					
						
							|  |  |  |   while (s_adapter_thread_running.IsSet()) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     libusb_interrupt_transfer(s_handle, s_endpoint_in, s_controller_payload_swap, | 
					
						
							|  |  |  |                               sizeof(s_controller_payload_swap), &payload_size, 16); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       std::lock_guard<std::mutex> lk(s_mutex); | 
					
						
							|  |  |  |       std::swap(s_controller_payload_swap, s_controller_payload); | 
					
						
							|  |  |  |       s_controller_payload_size.store(payload_size); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Common::YieldCPU(); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2014-12-10 20:45:45 +11:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-03 00:53:31 -08:00
										 |  |  | #if defined(LIBUSB_API_VERSION) && LIBUSB_API_VERSION >= 0x01000102
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | static int HotplugCallback(libusb_context* ctx, libusb_device* dev, libusb_hotplug_event event, | 
					
						
							|  |  |  |                            void* user_data) | 
					
						
							| 
									
										
										
										
											2015-04-26 23:53:36 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     if (s_handle == nullptr && CheckDeviceAccess(dev)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       std::lock_guard<std::mutex> lk(s_init_mutex); | 
					
						
							|  |  |  |       AddGCAdapter(dev); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     if (s_handle != nullptr && libusb_get_device(s_handle) == dev) | 
					
						
							|  |  |  |       Reset(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return 0; | 
					
						
							| 
									
										
										
										
											2015-04-26 23:53:36 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-01-03 00:53:31 -08:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2015-04-26 23:53:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | static void ScanThreadFunc() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   Common::SetCurrentThreadName("GC Adapter Scanning Thread"); | 
					
						
							|  |  |  |   NOTICE_LOG(SERIALINTERFACE, "GC Adapter scanning thread started"); | 
					
						
							| 
									
										
										
										
											2015-04-26 23:53:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-03 00:53:31 -08:00
										 |  |  | #if defined(LIBUSB_API_VERSION) && LIBUSB_API_VERSION >= 0x01000102
 | 
					
						
							| 
									
										
										
										
											2016-12-26 22:50:36 -08:00
										 |  |  | #ifndef __FreeBSD__
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   s_libusb_hotplug_enabled = libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) != 0; | 
					
						
							| 
									
										
										
										
											2016-12-26 22:50:36 -08:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (s_libusb_hotplug_enabled) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     if (libusb_hotplug_register_callback( | 
					
						
							| 
									
										
										
										
											2017-01-15 20:50:26 +01:00
										 |  |  |             s_libusb_context.get(), (libusb_hotplug_event)(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | | 
					
						
							|  |  |  |                                                            LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT), | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |             LIBUSB_HOTPLUG_ENUMERATE, 0x057e, 0x0337, LIBUSB_HOTPLUG_MATCH_ANY, HotplugCallback, | 
					
						
							|  |  |  |             nullptr, &s_hotplug_handle) != LIBUSB_SUCCESS) | 
					
						
							|  |  |  |       s_libusb_hotplug_enabled = false; | 
					
						
							|  |  |  |     if (s_libusb_hotplug_enabled) | 
					
						
							|  |  |  |       NOTICE_LOG(SERIALINTERFACE, "Using libUSB hotplug detection"); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-01-03 00:53:31 -08:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2015-04-26 23:53:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   while (s_adapter_detect_thread_running.IsSet()) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     if (s_libusb_hotplug_enabled) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       static timeval tv = {0, 500000}; | 
					
						
							| 
									
										
										
										
											2017-01-15 20:50:26 +01:00
										 |  |  |       libusb_handle_events_timeout(s_libusb_context.get(), &tv); | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       if (s_handle == nullptr) | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         std::lock_guard<std::mutex> lk(s_init_mutex); | 
					
						
							|  |  |  |         Setup(); | 
					
						
							|  |  |  |         if (s_detected && s_detect_callback != nullptr) | 
					
						
							|  |  |  |           s_detect_callback(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       Common::SleepCurrentThread(500); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   NOTICE_LOG(SERIALINTERFACE, "GC Adapter scanning thread stopped"); | 
					
						
							| 
									
										
										
										
											2015-04-26 23:53:36 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void SetAdapterCallback(std::function<void(void)> func) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   s_detect_callback = func; | 
					
						
							| 
									
										
										
										
											2015-04-26 23:53:36 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-10 20:45:45 +11:00
										 |  |  | void Init() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (s_handle != nullptr) | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-05 07:39:58 -05:00
										 |  |  |   if (Core::GetState() != Core::State::Uninitialized) | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   { | 
					
						
							|  |  |  |     if ((CoreTiming::GetTicks() - s_last_init) < SystemTimers::GetTicksPerSecond()) | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     s_last_init = CoreTiming::GetTicks(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   s_libusb_driver_not_supported = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-06 23:04:14 +01:00
										 |  |  |   if (UseAdapter()) | 
					
						
							|  |  |  |     StartScanThread(); | 
					
						
							| 
									
										
										
										
											2015-04-26 23:53:36 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void StartScanThread() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (s_adapter_detect_thread_running.IsSet()) | 
					
						
							|  |  |  |     return; | 
					
						
							| 
									
										
										
										
											2015-12-31 11:09:47 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-06 23:04:14 +01:00
										 |  |  |   s_libusb_context = LibusbContext::Get(); | 
					
						
							|  |  |  |   if (!s_libusb_context) | 
					
						
							|  |  |  |     return; | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   s_adapter_detect_thread_running.Set(true); | 
					
						
							|  |  |  |   s_adapter_detect_thread = std::thread(ScanThreadFunc); | 
					
						
							| 
									
										
										
										
											2015-04-26 23:53:36 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void StopScanThread() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (s_adapter_detect_thread_running.TestAndClear()) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     s_adapter_detect_thread.join(); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2015-01-26 13:54:54 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-19 21:52:44 +02:00
										 |  |  | static void Setup() | 
					
						
							| 
									
										
										
										
											2015-01-26 13:54:54 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   libusb_device** list; | 
					
						
							| 
									
										
										
										
											2017-01-15 20:50:26 +01:00
										 |  |  |   ssize_t cnt = libusb_get_device_list(s_libusb_context.get(), &list); | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   for (int i = 0; i < MAX_SI_CHANNELS; i++) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     s_controller_type[i] = ControllerTypes::CONTROLLER_NONE; | 
					
						
							|  |  |  |     s_controller_rumble[i] = 0; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for (int d = 0; d < cnt; d++) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     libusb_device* device = list[d]; | 
					
						
							|  |  |  |     if (CheckDeviceAccess(device)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       // Only connect to a single adapter in case the user has multiple connected
 | 
					
						
							|  |  |  |       AddGCAdapter(device); | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   libusb_free_device_list(list, 1); | 
					
						
							| 
									
										
										
										
											2015-04-26 23:53:36 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool CheckDeviceAccess(libusb_device* device) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   int ret; | 
					
						
							|  |  |  |   libusb_device_descriptor desc; | 
					
						
							|  |  |  |   int dRet = libusb_get_device_descriptor(device, &desc); | 
					
						
							|  |  |  |   if (dRet) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     // could not acquire the descriptor, no point in trying to use it.
 | 
					
						
							|  |  |  |     ERROR_LOG(SERIALINTERFACE, "libusb_get_device_descriptor failed with error: %d", dRet); | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (desc.idVendor == 0x057e && desc.idProduct == 0x0337) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     NOTICE_LOG(SERIALINTERFACE, "Found GC Adapter with Vendor: %X Product: %X Devnum: %d", | 
					
						
							|  |  |  |                desc.idVendor, desc.idProduct, 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     u8 bus = libusb_get_bus_number(device); | 
					
						
							|  |  |  |     u8 port = libusb_get_device_address(device); | 
					
						
							|  |  |  |     ret = libusb_open(device, &s_handle); | 
					
						
							|  |  |  |     if (ret) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       if (ret == LIBUSB_ERROR_ACCESS) | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         if (dRet) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           ERROR_LOG(SERIALINTERFACE, "Dolphin does not have access to this device: Bus %03d Device " | 
					
						
							|  |  |  |                                      "%03d: ID ????:???? (couldn't get id).", | 
					
						
							|  |  |  |                     bus, port); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           ERROR_LOG( | 
					
						
							|  |  |  |               SERIALINTERFACE, | 
					
						
							|  |  |  |               "Dolphin does not have access to this device: Bus %03d Device %03d: ID %04X:%04X.", | 
					
						
							|  |  |  |               bus, port, desc.idVendor, desc.idProduct); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         ERROR_LOG(SERIALINTERFACE, "libusb_open failed to open device with error = %d", ret); | 
					
						
							|  |  |  |         if (ret == LIBUSB_ERROR_NOT_SUPPORTED) | 
					
						
							|  |  |  |           s_libusb_driver_not_supported = true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if ((ret = libusb_kernel_driver_active(s_handle, 0)) == 1) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       if ((ret = libusb_detach_kernel_driver(s_handle, 0)) && ret != LIBUSB_ERROR_NOT_SUPPORTED) | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         ERROR_LOG(SERIALINTERFACE, "libusb_detach_kernel_driver failed with error: %d", ret); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // this split is needed so that we don't avoid claiming the interface when
 | 
					
						
							|  |  |  |     // detaching the kernel driver is successful
 | 
					
						
							|  |  |  |     if (ret != 0 && ret != LIBUSB_ERROR_NOT_SUPPORTED) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if ((ret = libusb_claim_interface(s_handle, 0))) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       ERROR_LOG(SERIALINTERFACE, "libusb_claim_interface failed with error: %d", ret); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return false; | 
					
						
							| 
									
										
										
										
											2015-01-26 13:54:54 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2015-01-05 12:43:26 +11:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-26 23:53:36 +02:00
										 |  |  | static void AddGCAdapter(libusb_device* device) | 
					
						
							| 
									
										
										
										
											2015-01-26 13:54:54 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   libusb_config_descriptor* config = nullptr; | 
					
						
							|  |  |  |   libusb_get_config_descriptor(device, 0, &config); | 
					
						
							|  |  |  |   for (u8 ic = 0; ic < config->bNumInterfaces; ic++) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     const libusb_interface* interfaceContainer = &config->interface[ic]; | 
					
						
							|  |  |  |     for (int i = 0; i < interfaceContainer->num_altsetting; i++) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       const libusb_interface_descriptor* interface = &interfaceContainer->altsetting[i]; | 
					
						
							|  |  |  |       for (u8 e = 0; e < interface->bNumEndpoints; e++) | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         const libusb_endpoint_descriptor* endpoint = &interface->endpoint[e]; | 
					
						
							|  |  |  |         if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) | 
					
						
							|  |  |  |           s_endpoint_in = endpoint->bEndpointAddress; | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |           s_endpoint_out = endpoint->bEndpointAddress; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   int tmp = 0; | 
					
						
							|  |  |  |   unsigned char payload = 0x13; | 
					
						
							|  |  |  |   libusb_interrupt_transfer(s_handle, s_endpoint_out, &payload, sizeof(payload), &tmp, 16); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   s_adapter_thread_running.Set(true); | 
					
						
							|  |  |  |   s_adapter_thread = std::thread(Read); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   s_detected = true; | 
					
						
							|  |  |  |   if (s_detect_callback != nullptr) | 
					
						
							|  |  |  |     s_detect_callback(); | 
					
						
							|  |  |  |   ResetRumbleLockNeeded(); | 
					
						
							| 
									
										
										
										
											2014-12-10 20:45:45 +11:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Shutdown() | 
					
						
							| 
									
										
										
										
											2015-01-26 13:54:54 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   StopScanThread(); | 
					
						
							| 
									
										
										
										
											2016-01-03 00:53:31 -08:00
										 |  |  | #if defined(LIBUSB_API_VERSION) && LIBUSB_API_VERSION >= 0x01000102
 | 
					
						
							| 
									
										
										
										
											2017-02-06 23:04:14 +01:00
										 |  |  |   if (s_libusb_context && s_libusb_hotplug_enabled) | 
					
						
							| 
									
										
										
										
											2017-01-15 20:50:26 +01:00
										 |  |  |     libusb_hotplug_deregister_callback(s_libusb_context.get(), s_hotplug_handle); | 
					
						
							| 
									
										
										
										
											2016-01-03 00:53:31 -08:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   Reset(); | 
					
						
							| 
									
										
										
										
											2015-01-26 13:54:54 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-15 20:50:26 +01:00
										 |  |  |   s_libusb_context.reset(); | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   s_libusb_driver_not_supported = false; | 
					
						
							| 
									
										
										
										
											2015-01-26 13:54:54 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-19 21:52:44 +02:00
										 |  |  | static void Reset() | 
					
						
							| 
									
										
										
										
											2014-12-10 20:45:45 +11:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   std::unique_lock<std::mutex> lock(s_init_mutex, std::defer_lock); | 
					
						
							|  |  |  |   if (!lock.try_lock()) | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   if (!s_detected) | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (s_adapter_thread_running.TestAndClear()) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     s_adapter_thread.join(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for (int i = 0; i < MAX_SI_CHANNELS; i++) | 
					
						
							|  |  |  |     s_controller_type[i] = ControllerTypes::CONTROLLER_NONE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   s_detected = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (s_handle) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     libusb_release_interface(s_handle, 0); | 
					
						
							|  |  |  |     libusb_close(s_handle); | 
					
						
							|  |  |  |     s_handle = nullptr; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   if (s_detect_callback != nullptr) | 
					
						
							|  |  |  |     s_detect_callback(); | 
					
						
							|  |  |  |   NOTICE_LOG(SERIALINTERFACE, "GC Adapter detached"); | 
					
						
							| 
									
										
										
										
											2014-12-10 20:45:45 +11:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 21:16:00 -04:00
										 |  |  | GCPadStatus Input(int chan) | 
					
						
							| 
									
										
										
										
											2014-12-10 20:45:45 +11:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (!UseAdapter()) | 
					
						
							| 
									
										
										
										
											2016-08-01 21:16:00 -04:00
										 |  |  |     return {}; | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (s_handle == nullptr || !s_detected) | 
					
						
							| 
									
										
										
										
											2016-08-01 21:16:00 -04:00
										 |  |  |     return {}; | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   int payload_size = 0; | 
					
						
							|  |  |  |   u8 controller_payload_copy[37]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     std::lock_guard<std::mutex> lk(s_mutex); | 
					
						
							|  |  |  |     std::copy(std::begin(s_controller_payload), std::end(s_controller_payload), | 
					
						
							|  |  |  |               std::begin(controller_payload_copy)); | 
					
						
							|  |  |  |     payload_size = s_controller_payload_size.load(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 21:16:00 -04:00
										 |  |  |   GCPadStatus pad = {}; | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (payload_size != sizeof(controller_payload_copy) || | 
					
						
							|  |  |  |       controller_payload_copy[0] != LIBUSB_DT_HID) | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2016-09-24 19:06:47 -04:00
										 |  |  |     ERROR_LOG(SERIALINTERFACE, "error reading payload (size: %d, type: %02x)", payload_size, | 
					
						
							|  |  |  |               controller_payload_copy[0]); | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |     Reset(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     bool get_origin = false; | 
					
						
							|  |  |  |     u8 type = controller_payload_copy[1 + (9 * chan)] >> 4; | 
					
						
							|  |  |  |     if (type != ControllerTypes::CONTROLLER_NONE && | 
					
						
							|  |  |  |         s_controller_type[chan] == ControllerTypes::CONTROLLER_NONE) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       NOTICE_LOG(SERIALINTERFACE, "New device connected to Port %d of Type: %02x", chan + 1, | 
					
						
							|  |  |  |                  controller_payload_copy[1 + (9 * chan)]); | 
					
						
							|  |  |  |       get_origin = true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     s_controller_type[chan] = type; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (s_controller_type[chan] != ControllerTypes::CONTROLLER_NONE) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       u8 b1 = controller_payload_copy[1 + (9 * chan) + 1]; | 
					
						
							|  |  |  |       u8 b2 = controller_payload_copy[1 + (9 * chan) + 2]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (b1 & (1 << 0)) | 
					
						
							| 
									
										
										
										
											2016-08-01 21:16:00 -04:00
										 |  |  |         pad.button |= PAD_BUTTON_A; | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |       if (b1 & (1 << 1)) | 
					
						
							| 
									
										
										
										
											2016-08-01 21:16:00 -04:00
										 |  |  |         pad.button |= PAD_BUTTON_B; | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |       if (b1 & (1 << 2)) | 
					
						
							| 
									
										
										
										
											2016-08-01 21:16:00 -04:00
										 |  |  |         pad.button |= PAD_BUTTON_X; | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |       if (b1 & (1 << 3)) | 
					
						
							| 
									
										
										
										
											2016-08-01 21:16:00 -04:00
										 |  |  |         pad.button |= PAD_BUTTON_Y; | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       if (b1 & (1 << 4)) | 
					
						
							| 
									
										
										
										
											2016-08-01 21:16:00 -04:00
										 |  |  |         pad.button |= PAD_BUTTON_LEFT; | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |       if (b1 & (1 << 5)) | 
					
						
							| 
									
										
										
										
											2016-08-01 21:16:00 -04:00
										 |  |  |         pad.button |= PAD_BUTTON_RIGHT; | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |       if (b1 & (1 << 6)) | 
					
						
							| 
									
										
										
										
											2016-08-01 21:16:00 -04:00
										 |  |  |         pad.button |= PAD_BUTTON_DOWN; | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |       if (b1 & (1 << 7)) | 
					
						
							| 
									
										
										
										
											2016-08-01 21:16:00 -04:00
										 |  |  |         pad.button |= PAD_BUTTON_UP; | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       if (b2 & (1 << 0)) | 
					
						
							| 
									
										
										
										
											2016-08-01 21:16:00 -04:00
										 |  |  |         pad.button |= PAD_BUTTON_START; | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |       if (b2 & (1 << 1)) | 
					
						
							| 
									
										
										
										
											2016-08-01 21:16:00 -04:00
										 |  |  |         pad.button |= PAD_TRIGGER_Z; | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |       if (b2 & (1 << 2)) | 
					
						
							| 
									
										
										
										
											2016-08-01 21:16:00 -04:00
										 |  |  |         pad.button |= PAD_TRIGGER_R; | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |       if (b2 & (1 << 3)) | 
					
						
							| 
									
										
										
										
											2016-08-01 21:16:00 -04:00
										 |  |  |         pad.button |= PAD_TRIGGER_L; | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       if (get_origin) | 
					
						
							| 
									
										
										
										
											2016-08-01 21:16:00 -04:00
										 |  |  |         pad.button |= PAD_GET_ORIGIN; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       pad.stickX = controller_payload_copy[1 + (9 * chan) + 3]; | 
					
						
							|  |  |  |       pad.stickY = controller_payload_copy[1 + (9 * chan) + 4]; | 
					
						
							|  |  |  |       pad.substickX = controller_payload_copy[1 + (9 * chan) + 5]; | 
					
						
							|  |  |  |       pad.substickY = controller_payload_copy[1 + (9 * chan) + 6]; | 
					
						
							|  |  |  |       pad.triggerLeft = controller_payload_copy[1 + (9 * chan) + 7]; | 
					
						
							|  |  |  |       pad.triggerRight = controller_payload_copy[1 + (9 * chan) + 8]; | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-07-05 20:58:44 +02:00
										 |  |  |     else if (!Core::g_want_determinism) | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2016-07-05 20:58:44 +02:00
										 |  |  |       // This is a hack to prevent a desync due to SI devices
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |       // being different and returning different values.
 | 
					
						
							|  |  |  |       // The corresponding code in DeviceGCAdapter has the same check
 | 
					
						
							| 
									
										
										
										
											2016-08-01 21:16:00 -04:00
										 |  |  |       pad.button = PAD_ERR_STATUS; | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-08-01 21:16:00 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return pad; | 
					
						
							| 
									
										
										
										
											2014-12-10 20:45:45 +11:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-31 11:09:47 -06:00
										 |  |  | bool DeviceConnected(int chan) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   return s_controller_type[chan] != ControllerTypes::CONTROLLER_NONE; | 
					
						
							| 
									
										
										
										
											2015-12-31 11:09:47 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool UseAdapter() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   return SConfig::GetInstance().m_SIDevice[0] == SIDEVICE_WIIU_ADAPTER || | 
					
						
							|  |  |  |          SConfig::GetInstance().m_SIDevice[1] == SIDEVICE_WIIU_ADAPTER || | 
					
						
							|  |  |  |          SConfig::GetInstance().m_SIDevice[2] == SIDEVICE_WIIU_ADAPTER || | 
					
						
							|  |  |  |          SConfig::GetInstance().m_SIDevice[3] == SIDEVICE_WIIU_ADAPTER; | 
					
						
							| 
									
										
										
										
											2015-12-31 11:09:47 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-08 18:13:58 +01:00
										 |  |  | void ResetRumble() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   std::unique_lock<std::mutex> lock(s_init_mutex, std::defer_lock); | 
					
						
							|  |  |  |   if (!lock.try_lock()) | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   ResetRumbleLockNeeded(); | 
					
						
							| 
									
										
										
										
											2016-04-30 13:29:11 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Needs to be called when s_init_mutex is locked in order to avoid
 | 
					
						
							|  |  |  | // being called while the libusb state is being reset
 | 
					
						
							|  |  |  | static void ResetRumbleLockNeeded() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (!UseAdapter() || (s_handle == nullptr || !s_detected)) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2015-11-08 18:13:58 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   std::fill(std::begin(s_controller_rumble), std::end(s_controller_rumble), 0); | 
					
						
							| 
									
										
										
										
											2015-11-08 18:13:58 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   unsigned char rumble[5] = {0x11, s_controller_rumble[0], s_controller_rumble[1], | 
					
						
							|  |  |  |                              s_controller_rumble[2], s_controller_rumble[3]}; | 
					
						
							| 
									
										
										
										
											2015-11-08 18:13:58 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   int size = 0; | 
					
						
							|  |  |  |   libusb_interrupt_transfer(s_handle, s_endpoint_out, rumble, sizeof(rumble), &size, 16); | 
					
						
							| 
									
										
										
										
											2015-11-08 18:13:58 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-24 19:06:47 -04:00
										 |  |  |   INFO_LOG(SERIALINTERFACE, "Rumble state reset"); | 
					
						
							| 
									
										
										
										
											2015-11-08 18:13:58 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-13 15:04:03 +11:00
										 |  |  | void Output(int chan, u8 rumble_command) | 
					
						
							| 
									
										
										
										
											2014-12-10 20:45:45 +11:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (s_handle == nullptr || !UseAdapter() || !SConfig::GetInstance().m_AdapterRumble[chan]) | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Skip over rumble commands if it has not changed or the controller is wireless
 | 
					
						
							|  |  |  |   if (rumble_command != s_controller_rumble[chan] && | 
					
						
							|  |  |  |       s_controller_type[chan] != ControllerTypes::CONTROLLER_WIRELESS) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     s_controller_rumble[chan] = rumble_command; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     unsigned char rumble[5] = {0x11, s_controller_rumble[0], s_controller_rumble[1], | 
					
						
							|  |  |  |                                s_controller_rumble[2], s_controller_rumble[3]}; | 
					
						
							|  |  |  |     int size = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     libusb_interrupt_transfer(s_handle, s_endpoint_out, rumble, sizeof(rumble), &size, 16); | 
					
						
							|  |  |  |     // Netplay sends invalid data which results in size = 0x00.  Ignore it.
 | 
					
						
							|  |  |  |     if (size != 0x05 && size != 0x00) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2016-09-24 19:06:47 -04:00
										 |  |  |       ERROR_LOG(SERIALINTERFACE, "error writing rumble (size: %d)", size); | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |       Reset(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2014-12-10 20:45:45 +11:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool IsDetected() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   return s_detected; | 
					
						
							| 
									
										
										
										
											2014-12-10 20:45:45 +11:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool IsDriverDetected() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   return !s_libusb_driver_not_supported; | 
					
						
							| 
									
										
										
										
											2014-12-10 20:45:45 +11:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | }  // end of namespace GCAdapter
 |