1 // Copyright 2014 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #pragma once
6 
7 #include <algorithm>
8 #include <array>
9 #include <functional>
10 #include <mutex>
11 #include <thread>
12 #include <unordered_map>
13 #include "common/common_types.h"
14 #include "common/threadsafe_queue.h"
15 
16 struct libusb_context;
17 struct libusb_device;
18 struct libusb_device_handle;
19 
20 namespace Common {
21 class ParamPackage;
22 }
23 
24 namespace GCAdapter {
25 
26 enum class PadButton {
27     Undefined = 0x0000,
28     ButtonLeft = 0x0001,
29     ButtonRight = 0x0002,
30     ButtonDown = 0x0004,
31     ButtonUp = 0x0008,
32     TriggerZ = 0x0010,
33     TriggerR = 0x0020,
34     TriggerL = 0x0040,
35     ButtonA = 0x0100,
36     ButtonB = 0x0200,
37     ButtonX = 0x0400,
38     ButtonY = 0x0800,
39     ButtonStart = 0x1000,
40     // Below is for compatibility with "AxisButton" type
41     Stick = 0x2000,
42 };
43 
44 enum class PadAxes : u8 {
45     StickX,
46     StickY,
47     SubstickX,
48     SubstickY,
49     TriggerLeft,
50     TriggerRight,
51     Undefined,
52 };
53 
54 enum class ControllerTypes {
55     None,
56     Wired,
57     Wireless,
58 };
59 
60 struct GCPadStatus {
61     std::size_t port{};
62 
63     PadButton button{PadButton::Undefined}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits
64 
65     PadAxes axis{PadAxes::Undefined};
66     s16 axis_value{};
67     u8 axis_threshold{50};
68 };
69 
70 struct GCController {
71     ControllerTypes type{};
72     u16 buttons{};
73     PadButton last_button{};
74     std::array<s16, 6> axis_values{};
75     std::array<u8, 6> axis_origin{};
76 };
77 
78 class Adapter {
79 public:
80     Adapter();
81     ~Adapter();
82 
83     /// Used for polling
84     void BeginConfiguration();
85     void EndConfiguration();
86 
87     Common::SPSCQueue<GCPadStatus>& GetPadQueue();
88     const Common::SPSCQueue<GCPadStatus>& GetPadQueue() const;
89 
90     GCController& GetPadState(std::size_t port);
91     const GCController& GetPadState(std::size_t port) const;
92 
93     /// Returns true if there is a device connected to port
94     bool DeviceConnected(std::size_t port) const;
95 
96     std::vector<Common::ParamPackage> GetInputDevices() const;
97 
98 private:
99     using AdapterPayload = std::array<u8, 37>;
100 
101     void UpdatePadType(std::size_t port, ControllerTypes pad_type);
102     void UpdateControllers(const AdapterPayload& adapter_payload);
103     void UpdateSettings(std::size_t port);
104     void UpdateStateButtons(std::size_t port, u8 b1, u8 b2);
105     void UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload);
106 
107     void AdapterInputThread();
108 
109     void AdapterScanThread();
110 
111     bool IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size);
112 
113     /// For use in initialization, querying devices to find the adapter
114     void Setup();
115 
116     /// Resets status of all GC controller devices to a disconnected state
117     void ResetDevices();
118 
119     /// Resets status of device connected to a disconnected state
120     void ResetDevice(std::size_t port);
121 
122     /// Returns true if we successfully gain access to GC Adapter
123     bool CheckDeviceAccess();
124 
125     /// Captures GC Adapter endpoint address
126     /// Returns true if the endpoint was set correctly
127     bool GetGCEndpoint(libusb_device* device);
128 
129     // Join all threads
130     void JoinThreads();
131 
132     // Release usb handles
133     void ClearLibusbHandle();
134 
135     libusb_device_handle* usb_adapter_handle = nullptr;
136     std::array<GCController, 4> pads;
137     Common::SPSCQueue<GCPadStatus> pad_queue;
138 
139     std::thread adapter_input_thread;
140     std::thread adapter_scan_thread;
141     bool adapter_input_thread_running;
142     bool adapter_scan_thread_running;
143     bool restart_scan_thread;
144 
145     libusb_context* libusb_ctx;
146 
147     u8 input_endpoint{0};
148     u8 output_endpoint{0};
149     u8 input_error_counter{0};
150 
151     bool configuring{false};
152 };
153 } // namespace GCAdapter
154