summaryrefslogtreecommitdiff
path: root/src/input_common/helpers/joycon_protocol/poller.cpp
diff options
context:
space:
mode:
authorGravatar liamwhite2023-01-24 09:29:37 -0500
committerGravatar GitHub2023-01-24 09:29:37 -0500
commita68af583ea378b48e2ed5a19f519a815ba89e40f (patch)
tree2983c14a7d4bc2797259c7d97462a439bec629f3 /src/input_common/helpers/joycon_protocol/poller.cpp
parentMerge pull request #9555 from abouvier/catch2-update (diff)
parentcore: hid: Make use of SCOPE_EXIT and SCOPE_GUARD where applicable (diff)
downloadyuzu-a68af583ea378b48e2ed5a19f519a815ba89e40f.tar.gz
yuzu-a68af583ea378b48e2ed5a19f519a815ba89e40f.tar.xz
yuzu-a68af583ea378b48e2ed5a19f519a815ba89e40f.zip
Merge pull request #9492 from german77/joycon_release
Input_common: Implement custom joycon driver v2
Diffstat (limited to 'src/input_common/helpers/joycon_protocol/poller.cpp')
-rw-r--r--src/input_common/helpers/joycon_protocol/poller.cpp341
1 files changed, 341 insertions, 0 deletions
diff --git a/src/input_common/helpers/joycon_protocol/poller.cpp b/src/input_common/helpers/joycon_protocol/poller.cpp
new file mode 100644
index 000000000..7f8e093fa
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/poller.cpp
@@ -0,0 +1,341 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/logging/log.h"
5#include "input_common/helpers/joycon_protocol/poller.h"
6
7namespace InputCommon::Joycon {
8
9JoyconPoller::JoyconPoller(ControllerType device_type_, JoyStickCalibration left_stick_calibration_,
10 JoyStickCalibration right_stick_calibration_,
11 MotionCalibration motion_calibration_)
12 : device_type{device_type_}, left_stick_calibration{left_stick_calibration_},
13 right_stick_calibration{right_stick_calibration_}, motion_calibration{motion_calibration_} {}
14
15void JoyconPoller::SetCallbacks(const Joycon::JoyconCallbacks& callbacks_) {
16 callbacks = std::move(callbacks_);
17}
18
19void JoyconPoller::ReadActiveMode(std::span<u8> buffer, const MotionStatus& motion_status,
20 const RingStatus& ring_status) {
21 InputReportActive data{};
22 memcpy(&data, buffer.data(), sizeof(InputReportActive));
23
24 switch (device_type) {
25 case Joycon::ControllerType::Left:
26 UpdateActiveLeftPadInput(data, motion_status);
27 break;
28 case Joycon::ControllerType::Right:
29 UpdateActiveRightPadInput(data, motion_status);
30 break;
31 case Joycon::ControllerType::Pro:
32 UpdateActiveProPadInput(data, motion_status);
33 break;
34 case Joycon::ControllerType::Grip:
35 case Joycon::ControllerType::Dual:
36 case Joycon::ControllerType::None:
37 break;
38 }
39
40 if (ring_status.is_enabled) {
41 UpdateRing(data.ring_input, ring_status);
42 }
43
44 callbacks.on_battery_data(data.battery_status);
45}
46
47void JoyconPoller::ReadPassiveMode(std::span<u8> buffer) {
48 InputReportPassive data{};
49 memcpy(&data, buffer.data(), sizeof(InputReportPassive));
50
51 switch (device_type) {
52 case Joycon::ControllerType::Left:
53 UpdatePasiveLeftPadInput(data);
54 break;
55 case Joycon::ControllerType::Right:
56 UpdatePasiveRightPadInput(data);
57 break;
58 case Joycon::ControllerType::Pro:
59 UpdatePasiveProPadInput(data);
60 break;
61 case Joycon::ControllerType::Grip:
62 case Joycon::ControllerType::Dual:
63 case Joycon::ControllerType::None:
64 break;
65 }
66}
67
68void JoyconPoller::ReadNfcIRMode(std::span<u8> buffer, const MotionStatus& motion_status) {
69 // This mode is compatible with the active mode
70 ReadActiveMode(buffer, motion_status, {});
71}
72
73void JoyconPoller::UpdateColor(const Color& color) {
74 callbacks.on_color_data(color);
75}
76
77void JoyconPoller::UpdateAmiibo(const std::vector<u8>& amiibo_data) {
78 callbacks.on_amiibo_data(amiibo_data);
79}
80
81void JoyconPoller::UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format) {
82 callbacks.on_camera_data(camera_data, format);
83}
84
85void JoyconPoller::UpdateRing(s16 value, const RingStatus& ring_status) {
86 float normalized_value = static_cast<float>(value - ring_status.default_value);
87 if (normalized_value > 0) {
88 normalized_value = normalized_value /
89 static_cast<float>(ring_status.max_value - ring_status.default_value);
90 }
91 if (normalized_value < 0) {
92 normalized_value = normalized_value /
93 static_cast<float>(ring_status.default_value - ring_status.min_value);
94 }
95 callbacks.on_ring_data(normalized_value);
96}
97
98void JoyconPoller::UpdateActiveLeftPadInput(const InputReportActive& input,
99 const MotionStatus& motion_status) {
100 static constexpr std::array<Joycon::PadButton, 11> left_buttons{
101 Joycon::PadButton::Down, Joycon::PadButton::Up, Joycon::PadButton::Right,
102 Joycon::PadButton::Left, Joycon::PadButton::LeftSL, Joycon::PadButton::LeftSR,
103 Joycon::PadButton::L, Joycon::PadButton::ZL, Joycon::PadButton::Minus,
104 Joycon::PadButton::Capture, Joycon::PadButton::StickL,
105 };
106
107 const u32 raw_button =
108 static_cast<u32>(input.button_input[2] | ((input.button_input[1] & 0b00101001) << 16));
109 for (std::size_t i = 0; i < left_buttons.size(); ++i) {
110 const bool button_status = (raw_button & static_cast<u32>(left_buttons[i])) != 0;
111 const int button = static_cast<int>(left_buttons[i]);
112 callbacks.on_button_data(button, button_status);
113 }
114
115 const u16 raw_left_axis_x =
116 static_cast<u16>(input.left_stick_state[0] | ((input.left_stick_state[1] & 0xf) << 8));
117 const u16 raw_left_axis_y =
118 static_cast<u16>((input.left_stick_state[1] >> 4) | (input.left_stick_state[2] << 4));
119 const f32 left_axis_x = GetAxisValue(raw_left_axis_x, left_stick_calibration.x);
120 const f32 left_axis_y = GetAxisValue(raw_left_axis_y, left_stick_calibration.y);
121 callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickX), left_axis_x);
122 callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickY), left_axis_y);
123
124 if (motion_status.is_enabled) {
125 auto left_motion = GetMotionInput(input, motion_status);
126 // Rotate motion axis to the correct direction
127 left_motion.accel_y = -left_motion.accel_y;
128 left_motion.accel_z = -left_motion.accel_z;
129 left_motion.gyro_x = -left_motion.gyro_x;
130 callbacks.on_motion_data(static_cast<int>(PadMotion::LeftMotion), left_motion);
131 }
132}
133
134void JoyconPoller::UpdateActiveRightPadInput(const InputReportActive& input,
135 const MotionStatus& motion_status) {
136 static constexpr std::array<Joycon::PadButton, 11> right_buttons{
137 Joycon::PadButton::Y, Joycon::PadButton::X, Joycon::PadButton::B,
138 Joycon::PadButton::A, Joycon::PadButton::RightSL, Joycon::PadButton::RightSR,
139 Joycon::PadButton::R, Joycon::PadButton::ZR, Joycon::PadButton::Plus,
140 Joycon::PadButton::Home, Joycon::PadButton::StickR,
141 };
142
143 const u32 raw_button =
144 static_cast<u32>((input.button_input[0] << 8) | (input.button_input[1] << 16));
145 for (std::size_t i = 0; i < right_buttons.size(); ++i) {
146 const bool button_status = (raw_button & static_cast<u32>(right_buttons[i])) != 0;
147 const int button = static_cast<int>(right_buttons[i]);
148 callbacks.on_button_data(button, button_status);
149 }
150
151 const u16 raw_right_axis_x =
152 static_cast<u16>(input.right_stick_state[0] | ((input.right_stick_state[1] & 0xf) << 8));
153 const u16 raw_right_axis_y =
154 static_cast<u16>((input.right_stick_state[1] >> 4) | (input.right_stick_state[2] << 4));
155 const f32 right_axis_x = GetAxisValue(raw_right_axis_x, right_stick_calibration.x);
156 const f32 right_axis_y = GetAxisValue(raw_right_axis_y, right_stick_calibration.y);
157 callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickX), right_axis_x);
158 callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickY), right_axis_y);
159
160 if (motion_status.is_enabled) {
161 auto right_motion = GetMotionInput(input, motion_status);
162 // Rotate motion axis to the correct direction
163 right_motion.accel_x = -right_motion.accel_x;
164 right_motion.accel_y = -right_motion.accel_y;
165 right_motion.gyro_z = -right_motion.gyro_z;
166 callbacks.on_motion_data(static_cast<int>(PadMotion::RightMotion), right_motion);
167 }
168}
169
170void JoyconPoller::UpdateActiveProPadInput(const InputReportActive& input,
171 const MotionStatus& motion_status) {
172 static constexpr std::array<Joycon::PadButton, 18> pro_buttons{
173 Joycon::PadButton::Down, Joycon::PadButton::Up, Joycon::PadButton::Right,
174 Joycon::PadButton::Left, Joycon::PadButton::L, Joycon::PadButton::ZL,
175 Joycon::PadButton::Minus, Joycon::PadButton::Capture, Joycon::PadButton::Y,
176 Joycon::PadButton::X, Joycon::PadButton::B, Joycon::PadButton::A,
177 Joycon::PadButton::R, Joycon::PadButton::ZR, Joycon::PadButton::Plus,
178 Joycon::PadButton::Home, Joycon::PadButton::StickL, Joycon::PadButton::StickR,
179 };
180
181 const u32 raw_button = static_cast<u32>(input.button_input[2] | (input.button_input[0] << 8) |
182 (input.button_input[1] << 16));
183 for (std::size_t i = 0; i < pro_buttons.size(); ++i) {
184 const bool button_status = (raw_button & static_cast<u32>(pro_buttons[i])) != 0;
185 const int button = static_cast<int>(pro_buttons[i]);
186 callbacks.on_button_data(button, button_status);
187 }
188
189 const u16 raw_left_axis_x =
190 static_cast<u16>(input.left_stick_state[0] | ((input.left_stick_state[1] & 0xf) << 8));
191 const u16 raw_left_axis_y =
192 static_cast<u16>((input.left_stick_state[1] >> 4) | (input.left_stick_state[2] << 4));
193 const u16 raw_right_axis_x =
194 static_cast<u16>(input.right_stick_state[0] | ((input.right_stick_state[1] & 0xf) << 8));
195 const u16 raw_right_axis_y =
196 static_cast<u16>((input.right_stick_state[1] >> 4) | (input.right_stick_state[2] << 4));
197
198 const f32 left_axis_x = GetAxisValue(raw_left_axis_x, left_stick_calibration.x);
199 const f32 left_axis_y = GetAxisValue(raw_left_axis_y, left_stick_calibration.y);
200 const f32 right_axis_x = GetAxisValue(raw_right_axis_x, right_stick_calibration.x);
201 const f32 right_axis_y = GetAxisValue(raw_right_axis_y, right_stick_calibration.y);
202 callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickX), left_axis_x);
203 callbacks.on_stick_data(static_cast<int>(PadAxes::LeftStickY), left_axis_y);
204 callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickX), right_axis_x);
205 callbacks.on_stick_data(static_cast<int>(PadAxes::RightStickY), right_axis_y);
206
207 if (motion_status.is_enabled) {
208 auto pro_motion = GetMotionInput(input, motion_status);
209 pro_motion.gyro_x = -pro_motion.gyro_x;
210 pro_motion.accel_y = -pro_motion.accel_y;
211 pro_motion.accel_z = -pro_motion.accel_z;
212 callbacks.on_motion_data(static_cast<int>(PadMotion::LeftMotion), pro_motion);
213 callbacks.on_motion_data(static_cast<int>(PadMotion::RightMotion), pro_motion);
214 }
215}
216
217void JoyconPoller::UpdatePasiveLeftPadInput(const InputReportPassive& input) {
218 static constexpr std::array<Joycon::PasivePadButton, 11> left_buttons{
219 Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X,
220 Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y,
221 Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR,
222 Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR,
223 Joycon::PasivePadButton::Minus, Joycon::PasivePadButton::Capture,
224 Joycon::PasivePadButton::StickL,
225 };
226
227 for (auto left_button : left_buttons) {
228 const bool button_status = (input.button_input & static_cast<u32>(left_button)) != 0;
229 const int button = static_cast<int>(left_button);
230 callbacks.on_button_data(button, button_status);
231 }
232}
233
234void JoyconPoller::UpdatePasiveRightPadInput(const InputReportPassive& input) {
235 static constexpr std::array<Joycon::PasivePadButton, 11> right_buttons{
236 Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X,
237 Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y,
238 Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR,
239 Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR,
240 Joycon::PasivePadButton::Plus, Joycon::PasivePadButton::Home,
241 Joycon::PasivePadButton::StickR,
242 };
243
244 for (auto right_button : right_buttons) {
245 const bool button_status = (input.button_input & static_cast<u32>(right_button)) != 0;
246 const int button = static_cast<int>(right_button);
247 callbacks.on_button_data(button, button_status);
248 }
249}
250
251void JoyconPoller::UpdatePasiveProPadInput(const InputReportPassive& input) {
252 static constexpr std::array<Joycon::PasivePadButton, 14> pro_buttons{
253 Joycon::PasivePadButton::Down_A, Joycon::PasivePadButton::Right_X,
254 Joycon::PasivePadButton::Left_B, Joycon::PasivePadButton::Up_Y,
255 Joycon::PasivePadButton::SL, Joycon::PasivePadButton::SR,
256 Joycon::PasivePadButton::L_R, Joycon::PasivePadButton::ZL_ZR,
257 Joycon::PasivePadButton::Minus, Joycon::PasivePadButton::Plus,
258 Joycon::PasivePadButton::Capture, Joycon::PasivePadButton::Home,
259 Joycon::PasivePadButton::StickL, Joycon::PasivePadButton::StickR,
260 };
261
262 for (auto pro_button : pro_buttons) {
263 const bool button_status = (input.button_input & static_cast<u32>(pro_button)) != 0;
264 const int button = static_cast<int>(pro_button);
265 callbacks.on_button_data(button, button_status);
266 }
267}
268
269f32 JoyconPoller::GetAxisValue(u16 raw_value, Joycon::JoyStickAxisCalibration calibration) const {
270 const f32 value = static_cast<f32>(raw_value - calibration.center);
271 if (value > 0.0f) {
272 return value / calibration.max;
273 }
274 return value / calibration.min;
275}
276
277f32 JoyconPoller::GetAccelerometerValue(s16 raw, const MotionSensorCalibration& cal,
278 AccelerometerSensitivity sensitivity) const {
279 const f32 value = raw * (1.0f / (cal.scale - cal.offset)) * 4;
280 switch (sensitivity) {
281 case Joycon::AccelerometerSensitivity::G2:
282 return value / 4.0f;
283 case Joycon::AccelerometerSensitivity::G4:
284 return value / 2.0f;
285 case Joycon::AccelerometerSensitivity::G8:
286 return value;
287 case Joycon::AccelerometerSensitivity::G16:
288 return value * 2.0f;
289 }
290 return value;
291}
292
293f32 JoyconPoller::GetGyroValue(s16 raw, const MotionSensorCalibration& cal,
294 GyroSensitivity sensitivity) const {
295 const f32 value = (raw - cal.offset) * (936.0f / (cal.scale - cal.offset)) / 360.0f;
296 switch (sensitivity) {
297 case Joycon::GyroSensitivity::DPS250:
298 return value / 8.0f;
299 case Joycon::GyroSensitivity::DPS500:
300 return value / 4.0f;
301 case Joycon::GyroSensitivity::DPS1000:
302 return value / 2.0f;
303 case Joycon::GyroSensitivity::DPS2000:
304 return value;
305 }
306 return value;
307}
308
309s16 JoyconPoller::GetRawIMUValues(std::size_t sensor, size_t axis,
310 const InputReportActive& input) const {
311 return input.motion_input[(sensor * 3) + axis];
312}
313
314MotionData JoyconPoller::GetMotionInput(const InputReportActive& input,
315 const MotionStatus& motion_status) const {
316 MotionData motion{};
317 const auto& accel_cal = motion_calibration.accelerometer;
318 const auto& gyro_cal = motion_calibration.gyro;
319 const s16 raw_accel_x = input.motion_input[1];
320 const s16 raw_accel_y = input.motion_input[0];
321 const s16 raw_accel_z = input.motion_input[2];
322 const s16 raw_gyro_x = input.motion_input[4];
323 const s16 raw_gyro_y = input.motion_input[3];
324 const s16 raw_gyro_z = input.motion_input[5];
325
326 motion.delta_timestamp = motion_status.delta_time;
327 motion.accel_x =
328 GetAccelerometerValue(raw_accel_x, accel_cal[1], motion_status.accelerometer_sensitivity);
329 motion.accel_y =
330 GetAccelerometerValue(raw_accel_y, accel_cal[0], motion_status.accelerometer_sensitivity);
331 motion.accel_z =
332 GetAccelerometerValue(raw_accel_z, accel_cal[2], motion_status.accelerometer_sensitivity);
333 motion.gyro_x = GetGyroValue(raw_gyro_x, gyro_cal[1], motion_status.gyro_sensitivity);
334 motion.gyro_y = GetGyroValue(raw_gyro_y, gyro_cal[0], motion_status.gyro_sensitivity);
335 motion.gyro_z = GetGyroValue(raw_gyro_z, gyro_cal[2], motion_status.gyro_sensitivity);
336
337 // TODO(German77): Return all three samples data
338 return motion;
339}
340
341} // namespace InputCommon::Joycon