| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | // Copyright 2014 Dolphin Emulator Project
 | 
					
						
							|  |  |  | // Licensed under GPLv2+
 | 
					
						
							|  |  |  | // Refer to the license.txt file included.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <algorithm>
 | 
					
						
							|  |  |  | #include <jni.h>
 | 
					
						
							|  |  |  | #include <mutex>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "Common/Event.h"
 | 
					
						
							|  |  |  | #include "Common/Flag.h"
 | 
					
						
							|  |  |  | #include "Common/Logging/Log.h"
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | #include "Common/Thread.h"
 | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | #include "Core/ConfigManager.h"
 | 
					
						
							|  |  |  | #include "Core/Core.h"
 | 
					
						
							|  |  |  | #include "Core/CoreTiming.h"
 | 
					
						
							| 
									
										
										
										
											2017-01-20 18:45:11 -05:00
										 |  |  | #include "Core/HW/SI/SI.h"
 | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | #include "Core/HW/SystemTimers.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "InputCommon/GCAdapter.h"
 | 
					
						
							|  |  |  | #include "InputCommon/GCPadStatus.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Global java_vm class
 | 
					
						
							|  |  |  | extern JavaVM* g_java_vm; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace GCAdapter | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-05-19 21:52:44 +02:00
										 |  |  | static void Setup(); | 
					
						
							|  |  |  | static void Reset(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | // Java classes
 | 
					
						
							|  |  |  | static jclass s_adapter_class; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool s_detected = false; | 
					
						
							|  |  |  | static int s_fd = 0; | 
					
						
							| 
									
										
										
										
											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}; | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | static u8 s_controller_rumble[4]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Input handling
 | 
					
						
							|  |  |  | static std::mutex s_read_mutex; | 
					
						
							|  |  |  | static u8 s_controller_payload[37]; | 
					
						
							| 
									
										
										
										
											2016-01-23 06:32:07 -06:00
										 |  |  | static std::atomic<int> s_controller_payload_size{0}; | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Output handling
 | 
					
						
							|  |  |  | static std::mutex s_write_mutex; | 
					
						
							|  |  |  | static u8 s_controller_write_payload[5]; | 
					
						
							| 
									
										
										
										
											2016-02-13 08:17:20 -06:00
										 |  |  | static std::atomic<int> s_controller_write_payload_size{0}; | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Adapter running thread
 | 
					
						
							|  |  |  | static std::thread s_read_adapter_thread; | 
					
						
							|  |  |  | static Common::Flag s_read_adapter_thread_running; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static Common::Flag s_write_adapter_thread_running; | 
					
						
							|  |  |  | static Common::Event s_write_happened; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Adapter scanning thread
 | 
					
						
							|  |  |  | static std::thread s_adapter_detect_thread; | 
					
						
							|  |  |  | static Common::Flag s_adapter_detect_thread_running; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static u64 s_last_init = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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"); | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   JNIEnv* env; | 
					
						
							|  |  |  |   g_java_vm->AttachCurrentThread(&env, NULL); | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   jmethodID queryadapter_func = env->GetStaticMethodID(s_adapter_class, "QueryAdapter", "()Z"); | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   while (s_adapter_detect_thread_running.IsSet()) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     if (!s_detected && UseAdapter() && | 
					
						
							|  |  |  |         env->CallStaticBooleanMethod(s_adapter_class, queryadapter_func)) | 
					
						
							|  |  |  |       Setup(); | 
					
						
							|  |  |  |     Common::SleepCurrentThread(1000); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   g_java_vm->DetachCurrentThread(); | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   NOTICE_LOG(SERIALINTERFACE, "GC Adapter scanning thread stopped"); | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-13 08:17:20 -06:00
										 |  |  | static void Write() | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   Common::SetCurrentThreadName("GC Adapter Write Thread"); | 
					
						
							|  |  |  |   NOTICE_LOG(SERIALINTERFACE, "GC Adapter write thread started"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   JNIEnv* env; | 
					
						
							|  |  |  |   g_java_vm->AttachCurrentThread(&env, NULL); | 
					
						
							|  |  |  |   jmethodID output_func = env->GetStaticMethodID(s_adapter_class, "Output", "([B)I"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   while (s_write_adapter_thread_running.IsSet()) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     s_write_happened.Wait(); | 
					
						
							|  |  |  |     int write_size = s_controller_write_payload_size.load(); | 
					
						
							|  |  |  |     if (write_size) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       jbyteArray jrumble_array = env->NewByteArray(5); | 
					
						
							|  |  |  |       jbyte* jrumble = env->GetByteArrayElements(jrumble_array, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         std::lock_guard<std::mutex> lk(s_write_mutex); | 
					
						
							|  |  |  |         memcpy(jrumble, s_controller_write_payload, write_size); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       env->ReleaseByteArrayElements(jrumble_array, jrumble, 0); | 
					
						
							|  |  |  |       int size = env->CallStaticIntMethod(s_adapter_class, output_func, jrumble_array); | 
					
						
							|  |  |  |       // Netplay sends invalid data which results in size = 0x00.  Ignore it.
 | 
					
						
							|  |  |  |       if (size != write_size && size != 0x00) | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         ERROR_LOG(SERIALINTERFACE, "error writing rumble (size: %d)", size); | 
					
						
							|  |  |  |         Reset(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Common::YieldCPU(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   g_java_vm->DetachCurrentThread(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   NOTICE_LOG(SERIALINTERFACE, "GC Adapter write thread stopped"); | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-13 08:17:20 -06:00
										 |  |  | static void Read() | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   Common::SetCurrentThreadName("GC Adapter Read Thread"); | 
					
						
							|  |  |  |   NOTICE_LOG(SERIALINTERFACE, "GC Adapter read thread started"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   bool first_read = true; | 
					
						
							|  |  |  |   JNIEnv* env; | 
					
						
							|  |  |  |   g_java_vm->AttachCurrentThread(&env, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   jfieldID payload_field = env->GetStaticFieldID(s_adapter_class, "controller_payload", "[B"); | 
					
						
							|  |  |  |   jobject payload_object = env->GetStaticObjectField(s_adapter_class, payload_field); | 
					
						
							|  |  |  |   jbyteArray* java_controller_payload = reinterpret_cast<jbyteArray*>(&payload_object); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Get function pointers
 | 
					
						
							|  |  |  |   jmethodID getfd_func = env->GetStaticMethodID(s_adapter_class, "GetFD", "()I"); | 
					
						
							|  |  |  |   jmethodID input_func = env->GetStaticMethodID(s_adapter_class, "Input", "()I"); | 
					
						
							|  |  |  |   jmethodID openadapter_func = env->GetStaticMethodID(s_adapter_class, "OpenAdapter", "()Z"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   bool connected = env->CallStaticBooleanMethod(s_adapter_class, openadapter_func); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (connected) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     s_write_adapter_thread_running.Set(true); | 
					
						
							|  |  |  |     std::thread write_adapter_thread(Write); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Reset rumble once on initial reading
 | 
					
						
							|  |  |  |     ResetRumble(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while (s_read_adapter_thread_running.IsSet()) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       int read_size = env->CallStaticIntMethod(s_adapter_class, input_func); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       jbyte* java_data = env->GetByteArrayElements(*java_controller_payload, nullptr); | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         std::lock_guard<std::mutex> lk(s_read_mutex); | 
					
						
							|  |  |  |         memcpy(s_controller_payload, java_data, 0x37); | 
					
						
							|  |  |  |         s_controller_payload_size.store(read_size); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       env->ReleaseByteArrayElements(*java_controller_payload, java_data, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (first_read) | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         first_read = false; | 
					
						
							|  |  |  |         s_fd = env->CallStaticIntMethod(s_adapter_class, getfd_func); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       Common::YieldCPU(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Terminate the write thread on leaving
 | 
					
						
							|  |  |  |     if (s_write_adapter_thread_running.TestAndClear()) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       s_controller_write_payload_size.store(0); | 
					
						
							|  |  |  |       s_write_happened.Set();  // Kick the waiting event
 | 
					
						
							|  |  |  |       write_adapter_thread.join(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   s_fd = 0; | 
					
						
							|  |  |  |   s_detected = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   g_java_vm->DetachCurrentThread(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   NOTICE_LOG(SERIALINTERFACE, "GC Adapter read thread stopped"); | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Init() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (s_fd) | 
					
						
							|  |  |  |     return; | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (Core::GetState() != Core::CORE_UNINITIALIZED) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     if ((CoreTiming::GetTicks() - s_last_init) < SystemTimers::GetTicksPerSecond()) | 
					
						
							|  |  |  |       return; | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |     s_last_init = CoreTiming::GetTicks(); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   JNIEnv* env; | 
					
						
							|  |  |  |   g_java_vm->AttachCurrentThread(&env, NULL); | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   jclass adapter_class = env->FindClass("org/dolphinemu/dolphinemu/utils/Java_GCAdapter"); | 
					
						
							|  |  |  |   s_adapter_class = reinterpret_cast<jclass>(env->NewGlobalRef(adapter_class)); | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (UseAdapter()) | 
					
						
							|  |  |  |     StartScanThread(); | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-19 21:52:44 +02:00
										 |  |  | static void Setup() | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   s_fd = 0; | 
					
						
							|  |  |  |   s_detected = true; | 
					
						
							| 
									
										
										
										
											2016-02-13 08:17:20 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   // Make sure the thread isn't in the middle of shutting down while starting a new one
 | 
					
						
							|  |  |  |   if (s_read_adapter_thread_running.TestAndClear()) | 
					
						
							|  |  |  |     s_read_adapter_thread.join(); | 
					
						
							| 
									
										
										
										
											2016-01-23 06:27:03 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   s_read_adapter_thread_running.Set(true); | 
					
						
							|  |  |  |   s_read_adapter_thread = std::thread(Read); | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-19 21:52:44 +02:00
										 |  |  | static void Reset() | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (!s_detected) | 
					
						
							|  |  |  |     return; | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (s_read_adapter_thread_running.TestAndClear()) | 
					
						
							|  |  |  |     s_read_adapter_thread.join(); | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   for (int i = 0; i < MAX_SI_CHANNELS; i++) | 
					
						
							|  |  |  |     s_controller_type[i] = ControllerTypes::CONTROLLER_NONE; | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   s_detected = false; | 
					
						
							|  |  |  |   s_fd = 0; | 
					
						
							|  |  |  |   NOTICE_LOG(SERIALINTERFACE, "GC Adapter detached"); | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Shutdown() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   StopScanThread(); | 
					
						
							|  |  |  |   Reset(); | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void StartScanThread() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (s_adapter_detect_thread_running.IsSet()) | 
					
						
							|  |  |  |     return; | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   s_adapter_detect_thread_running.Set(true); | 
					
						
							|  |  |  |   s_adapter_detect_thread = std::thread(ScanThreadFunc); | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void StopScanThread() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (s_adapter_detect_thread_running.TestAndClear()) | 
					
						
							|  |  |  |     s_adapter_detect_thread.join(); | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 21:16:00 -04:00
										 |  |  | GCPadStatus Input(int chan) | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (!UseAdapter() || !s_detected || !s_fd) | 
					
						
							| 
									
										
										
										
											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_read_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)) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     ERROR_LOG(SERIALINTERFACE, "error reading payload (size: %d, type: %02x)", payload_size, | 
					
						
							|  |  |  |               controller_payload_copy[0]); | 
					
						
							|  |  |  |     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) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       ERROR_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
										 |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void Output(int chan, u8 rumble_command) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   if (!UseAdapter() || !s_detected || !s_fd) | 
					
						
							|  |  |  |     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]}; | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       std::lock_guard<std::mutex> lk(s_write_mutex); | 
					
						
							|  |  |  |       memcpy(s_controller_write_payload, rumble, 5); | 
					
						
							|  |  |  |       s_controller_write_payload_size.store(5); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     s_write_happened.Set(); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | bool IsDetected() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return s_detected; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | bool IsDriverDetected() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   return true; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | bool DeviceConnected(int chan) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   return s_controller_type[chan] != ControllerTypes::CONTROLLER_NONE; | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -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; | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void ResetRumble() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  |   unsigned char rumble[5] = {0x11, 0, 0, 0, 0}; | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     std::lock_guard<std::mutex> lk(s_read_mutex); | 
					
						
							|  |  |  |     memcpy(s_controller_write_payload, rumble, 5); | 
					
						
							|  |  |  |     s_controller_write_payload_size.store(5); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   s_write_happened.Set(); | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | void SetAdapterCallback(std::function<void(void)> func) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-01-06 01:00:02 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-24 10:43:46 +02:00
										 |  |  | }  // end of namespace GCAdapter
 |