summaryrefslogtreecommitdiff
path: root/src/input_common
diff options
context:
space:
mode:
Diffstat (limited to 'src/input_common')
-rw-r--r--src/input_common/drivers/gc_adapter.cpp4
-rw-r--r--src/input_common/drivers/joycon.cpp11
-rw-r--r--src/input_common/drivers/joycon.h2
-rw-r--r--src/input_common/helpers/joycon_driver.cpp21
-rw-r--r--src/input_common/helpers/joycon_protocol/calibration.cpp210
-rw-r--r--src/input_common/helpers/joycon_protocol/calibration.h18
-rw-r--r--src/input_common/helpers/joycon_protocol/common_protocol.cpp157
-rw-r--r--src/input_common/helpers/joycon_protocol/common_protocol.h52
-rw-r--r--src/input_common/helpers/joycon_protocol/generic_functions.cpp23
-rw-r--r--src/input_common/helpers/joycon_protocol/generic_functions.h6
-rw-r--r--src/input_common/helpers/joycon_protocol/irs.cpp13
-rw-r--r--src/input_common/helpers/joycon_protocol/joycon_types.h187
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.cpp68
-rw-r--r--src/input_common/helpers/joycon_protocol/nfc.h8
-rw-r--r--src/input_common/helpers/joycon_protocol/poller.cpp8
-rw-r--r--src/input_common/helpers/joycon_protocol/ringcon.cpp12
-rw-r--r--src/input_common/helpers/stick_from_buttons.cpp45
-rw-r--r--src/input_common/input_poller.cpp30
18 files changed, 532 insertions, 343 deletions
diff --git a/src/input_common/drivers/gc_adapter.cpp b/src/input_common/drivers/gc_adapter.cpp
index ecb3e9dc2..d09ff178b 100644
--- a/src/input_common/drivers/gc_adapter.cpp
+++ b/src/input_common/drivers/gc_adapter.cpp
@@ -6,6 +6,7 @@
6 6
7#include "common/logging/log.h" 7#include "common/logging/log.h"
8#include "common/param_package.h" 8#include "common/param_package.h"
9#include "common/polyfill_thread.h"
9#include "common/settings_input.h" 10#include "common/settings_input.h"
10#include "common/thread.h" 11#include "common/thread.h"
11#include "input_common/drivers/gc_adapter.h" 12#include "input_common/drivers/gc_adapter.h"
@@ -217,8 +218,7 @@ void GCAdapter::AdapterScanThread(std::stop_token stop_token) {
217 Common::SetCurrentThreadName("ScanGCAdapter"); 218 Common::SetCurrentThreadName("ScanGCAdapter");
218 usb_adapter_handle = nullptr; 219 usb_adapter_handle = nullptr;
219 pads = {}; 220 pads = {};
220 while (!stop_token.stop_requested() && !Setup()) { 221 while (!Setup() && Common::StoppableTimedWait(stop_token, std::chrono::seconds{2})) {
221 std::this_thread::sleep_for(std::chrono::seconds(2));
222 } 222 }
223} 223}
224 224
diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp
index 40cda400d..4fcfb4510 100644
--- a/src/input_common/drivers/joycon.cpp
+++ b/src/input_common/drivers/joycon.cpp
@@ -5,6 +5,7 @@
5 5
6#include "common/param_package.h" 6#include "common/param_package.h"
7#include "common/polyfill_ranges.h" 7#include "common/polyfill_ranges.h"
8#include "common/polyfill_thread.h"
8#include "common/settings.h" 9#include "common/settings.h"
9#include "common/thread.h" 10#include "common/thread.h"
10#include "input_common/drivers/joycon.h" 11#include "input_common/drivers/joycon.h"
@@ -67,7 +68,8 @@ void Joycons::Setup() {
67void Joycons::ScanThread(std::stop_token stop_token) { 68void Joycons::ScanThread(std::stop_token stop_token) {
68 constexpr u16 nintendo_vendor_id = 0x057e; 69 constexpr u16 nintendo_vendor_id = 0x057e;
69 Common::SetCurrentThreadName("JoyconScanThread"); 70 Common::SetCurrentThreadName("JoyconScanThread");
70 while (!stop_token.stop_requested()) { 71
72 do {
71 SDL_hid_device_info* devs = SDL_hid_enumerate(nintendo_vendor_id, 0x0); 73 SDL_hid_device_info* devs = SDL_hid_enumerate(nintendo_vendor_id, 0x0);
72 SDL_hid_device_info* cur_dev = devs; 74 SDL_hid_device_info* cur_dev = devs;
73 75
@@ -81,8 +83,7 @@ void Joycons::ScanThread(std::stop_token stop_token) {
81 } 83 }
82 84
83 SDL_hid_free_enumeration(devs); 85 SDL_hid_free_enumeration(devs);
84 std::this_thread::sleep_for(std::chrono::seconds(5)); 86 } while (Common::StoppableTimedWait(stop_token, std::chrono::seconds{5}));
85 }
86} 87}
87 88
88bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const { 89bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const {
@@ -667,12 +668,10 @@ std::string Joycons::JoyconName(Joycon::ControllerType type) const {
667 return "Right Joycon"; 668 return "Right Joycon";
668 case Joycon::ControllerType::Pro: 669 case Joycon::ControllerType::Pro:
669 return "Pro Controller"; 670 return "Pro Controller";
670 case Joycon::ControllerType::Grip:
671 return "Grip Controller";
672 case Joycon::ControllerType::Dual: 671 case Joycon::ControllerType::Dual:
673 return "Dual Joycon"; 672 return "Dual Joycon";
674 default: 673 default:
675 return "Unknown Joycon"; 674 return "Unknown Switch Controller";
676 } 675 }
677} 676}
678} // namespace InputCommon 677} // namespace InputCommon
diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h
index 316d383d8..2149ab7fd 100644
--- a/src/input_common/drivers/joycon.h
+++ b/src/input_common/drivers/joycon.h
@@ -15,7 +15,7 @@ using SerialNumber = std::array<u8, 15>;
15struct Battery; 15struct Battery;
16struct Color; 16struct Color;
17struct MotionData; 17struct MotionData;
18enum class ControllerType; 18enum class ControllerType : u8;
19enum class DriverResult; 19enum class DriverResult;
20enum class IrsResolution; 20enum class IrsResolution;
21class JoyconDriver; 21class JoyconDriver;
diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp
index 4159e5717..8f94c9f45 100644
--- a/src/input_common/helpers/joycon_driver.cpp
+++ b/src/input_common/helpers/joycon_driver.cpp
@@ -86,6 +86,7 @@ DriverResult JoyconDriver::InitializeDevice() {
86 86
87 // Get fixed joycon info 87 // Get fixed joycon info
88 generic_protocol->GetVersionNumber(version); 88 generic_protocol->GetVersionNumber(version);
89 generic_protocol->SetLowPowerMode(false);
89 generic_protocol->GetColor(color); 90 generic_protocol->GetColor(color);
90 if (handle_device_type == ControllerType::Pro) { 91 if (handle_device_type == ControllerType::Pro) {
91 // Some 3rd party controllers aren't pro controllers 92 // Some 3rd party controllers aren't pro controllers
@@ -161,14 +162,14 @@ void JoyconDriver::InputThread(std::stop_token stop_token) {
161} 162}
162 163
163void JoyconDriver::OnNewData(std::span<u8> buffer) { 164void JoyconDriver::OnNewData(std::span<u8> buffer) {
164 const auto report_mode = static_cast<InputReport>(buffer[0]); 165 const auto report_mode = static_cast<ReportMode>(buffer[0]);
165 166
166 // Packages can be a litte bit inconsistent. Average the delta time to provide a smoother motion 167 // Packages can be a litte bit inconsistent. Average the delta time to provide a smoother motion
167 // experience 168 // experience
168 switch (report_mode) { 169 switch (report_mode) {
169 case InputReport::STANDARD_FULL_60HZ: 170 case ReportMode::STANDARD_FULL_60HZ:
170 case InputReport::NFC_IR_MODE_60HZ: 171 case ReportMode::NFC_IR_MODE_60HZ:
171 case InputReport::SIMPLE_HID_MODE: { 172 case ReportMode::SIMPLE_HID_MODE: {
172 const auto now = std::chrono::steady_clock::now(); 173 const auto now = std::chrono::steady_clock::now();
173 const auto new_delta_time = static_cast<u64>( 174 const auto new_delta_time = static_cast<u64>(
174 std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count()); 175 std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count());
@@ -189,7 +190,7 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
189 }; 190 };
190 191
191 // TODO: Remove this when calibration is properly loaded and not calculated 192 // TODO: Remove this when calibration is properly loaded and not calculated
192 if (ring_connected && report_mode == InputReport::STANDARD_FULL_60HZ) { 193 if (ring_connected && report_mode == ReportMode::STANDARD_FULL_60HZ) {
193 InputReportActive data{}; 194 InputReportActive data{};
194 memcpy(&data, buffer.data(), sizeof(InputReportActive)); 195 memcpy(&data, buffer.data(), sizeof(InputReportActive));
195 calibration_protocol->GetRingCalibration(ring_calibration, data.ring_input); 196 calibration_protocol->GetRingCalibration(ring_calibration, data.ring_input);
@@ -227,16 +228,16 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {
227 } 228 }
228 229
229 switch (report_mode) { 230 switch (report_mode) {
230 case InputReport::STANDARD_FULL_60HZ: 231 case ReportMode::STANDARD_FULL_60HZ:
231 joycon_poller->ReadActiveMode(buffer, motion_status, ring_status); 232 joycon_poller->ReadActiveMode(buffer, motion_status, ring_status);
232 break; 233 break;
233 case InputReport::NFC_IR_MODE_60HZ: 234 case ReportMode::NFC_IR_MODE_60HZ:
234 joycon_poller->ReadNfcIRMode(buffer, motion_status); 235 joycon_poller->ReadNfcIRMode(buffer, motion_status);
235 break; 236 break;
236 case InputReport::SIMPLE_HID_MODE: 237 case ReportMode::SIMPLE_HID_MODE:
237 joycon_poller->ReadPassiveMode(buffer); 238 joycon_poller->ReadPassiveMode(buffer);
238 break; 239 break;
239 case InputReport::SUBCMD_REPLY: 240 case ReportMode::SUBCMD_REPLY:
240 LOG_DEBUG(Input, "Unhandled command reply"); 241 LOG_DEBUG(Input, "Unhandled command reply");
241 break; 242 break;
242 default: 243 default:
@@ -324,6 +325,8 @@ DriverResult JoyconDriver::SetPollingMode() {
324 if (result != DriverResult::Success) { 325 if (result != DriverResult::Success) {
325 LOG_ERROR(Input, "Error enabling active mode"); 326 LOG_ERROR(Input, "Error enabling active mode");
326 } 327 }
328 // Switch calls this function after enabling active mode
329 generic_protocol->TriggersElapsed();
327 330
328 disable_input_thread = false; 331 disable_input_thread = false;
329 return result; 332 return result;
diff --git a/src/input_common/helpers/joycon_protocol/calibration.cpp b/src/input_common/helpers/joycon_protocol/calibration.cpp
index f6e7e97d5..d8f040f75 100644
--- a/src/input_common/helpers/joycon_protocol/calibration.cpp
+++ b/src/input_common/helpers/joycon_protocol/calibration.cpp
@@ -13,33 +13,33 @@ CalibrationProtocol::CalibrationProtocol(std::shared_ptr<JoyconHandle> handle)
13 13
14DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration& calibration) { 14DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration& calibration) {
15 ScopedSetBlocking sb(this); 15 ScopedSetBlocking sb(this);
16 std::vector<u8> buffer;
17 DriverResult result{DriverResult::Success}; 16 DriverResult result{DriverResult::Success};
17 JoystickLeftSpiCalibration spi_calibration{};
18 bool has_user_calibration = false;
18 calibration = {}; 19 calibration = {};
19 20
20 result = ReadSPI(CalAddr::USER_LEFT_MAGIC, sizeof(u16), buffer);
21
22 if (result == DriverResult::Success) { 21 if (result == DriverResult::Success) {
23 const bool has_user_calibration = buffer[0] == 0xB2 && buffer[1] == 0xA1; 22 result = HasUserCalibration(SpiAddress::USER_LEFT_MAGIC, has_user_calibration);
24 if (has_user_calibration) {
25 result = ReadSPI(CalAddr::USER_LEFT_DATA, 9, buffer);
26 } else {
27 result = ReadSPI(CalAddr::FACT_LEFT_DATA, 9, buffer);
28 }
29 } 23 }
30 24
31 if (result == DriverResult::Success) { 25 // Read User defined calibration
32 calibration.x.max = static_cast<u16>(((buffer[1] & 0x0F) << 8) | buffer[0]); 26 if (result == DriverResult::Success && has_user_calibration) {
33 calibration.y.max = static_cast<u16>((buffer[2] << 4) | (buffer[1] >> 4)); 27 result = ReadSPI(SpiAddress::USER_LEFT_DATA, spi_calibration);
34 calibration.x.center = static_cast<u16>(((buffer[4] & 0x0F) << 8) | buffer[3]);
35 calibration.y.center = static_cast<u16>((buffer[5] << 4) | (buffer[4] >> 4));
36 calibration.x.min = static_cast<u16>(((buffer[7] & 0x0F) << 8) | buffer[6]);
37 calibration.y.min = static_cast<u16>((buffer[8] << 4) | (buffer[7] >> 4));
38 } 28 }
39 29
40 // Nintendo fix for drifting stick 30 // Read Factory calibration
41 // result = ReadSPI(0x60, 0x86 ,buffer, 16); 31 if (result == DriverResult::Success && !has_user_calibration) {
42 // calibration.deadzone = (u16)((buffer[4] << 8) & 0xF00 | buffer[3]); 32 result = ReadSPI(SpiAddress::FACT_LEFT_DATA, spi_calibration);
33 }
34
35 if (result == DriverResult::Success) {
36 calibration.x.center = GetXAxisCalibrationValue(spi_calibration.center);
37 calibration.y.center = GetYAxisCalibrationValue(spi_calibration.center);
38 calibration.x.min = GetXAxisCalibrationValue(spi_calibration.min);
39 calibration.y.min = GetYAxisCalibrationValue(spi_calibration.min);
40 calibration.x.max = GetXAxisCalibrationValue(spi_calibration.max);
41 calibration.y.max = GetYAxisCalibrationValue(spi_calibration.max);
42 }
43 43
44 // Set a valid default calibration if data is missing 44 // Set a valid default calibration if data is missing
45 ValidateCalibration(calibration); 45 ValidateCalibration(calibration);
@@ -49,33 +49,33 @@ DriverResult CalibrationProtocol::GetLeftJoyStickCalibration(JoyStickCalibration
49 49
50DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibration& calibration) { 50DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibration& calibration) {
51 ScopedSetBlocking sb(this); 51 ScopedSetBlocking sb(this);
52 std::vector<u8> buffer;
53 DriverResult result{DriverResult::Success}; 52 DriverResult result{DriverResult::Success};
53 JoystickRightSpiCalibration spi_calibration{};
54 bool has_user_calibration = false;
54 calibration = {}; 55 calibration = {};
55 56
56 result = ReadSPI(CalAddr::USER_RIGHT_MAGIC, sizeof(u16), buffer);
57
58 if (result == DriverResult::Success) { 57 if (result == DriverResult::Success) {
59 const bool has_user_calibration = buffer[0] == 0xB2 && buffer[1] == 0xA1; 58 result = HasUserCalibration(SpiAddress::USER_RIGHT_MAGIC, has_user_calibration);
60 if (has_user_calibration) {
61 result = ReadSPI(CalAddr::USER_RIGHT_DATA, 9, buffer);
62 } else {
63 result = ReadSPI(CalAddr::FACT_RIGHT_DATA, 9, buffer);
64 }
65 } 59 }
66 60
67 if (result == DriverResult::Success) { 61 // Read User defined calibration
68 calibration.x.center = static_cast<u16>(((buffer[1] & 0x0F) << 8) | buffer[0]); 62 if (result == DriverResult::Success && has_user_calibration) {
69 calibration.y.center = static_cast<u16>((buffer[2] << 4) | (buffer[1] >> 4)); 63 result = ReadSPI(SpiAddress::USER_RIGHT_DATA, spi_calibration);
70 calibration.x.min = static_cast<u16>(((buffer[4] & 0x0F) << 8) | buffer[3]); 64 }
71 calibration.y.min = static_cast<u16>((buffer[5] << 4) | (buffer[4] >> 4)); 65
72 calibration.x.max = static_cast<u16>(((buffer[7] & 0x0F) << 8) | buffer[6]); 66 // Read Factory calibration
73 calibration.y.max = static_cast<u16>((buffer[8] << 4) | (buffer[7] >> 4)); 67 if (result == DriverResult::Success && !has_user_calibration) {
68 result = ReadSPI(SpiAddress::FACT_RIGHT_DATA, spi_calibration);
74 } 69 }
75 70
76 // Nintendo fix for drifting stick 71 if (result == DriverResult::Success) {
77 // buffer = ReadSPI(0x60, 0x98 , 16); 72 calibration.x.center = GetXAxisCalibrationValue(spi_calibration.center);
78 // joystick.deadzone = (u16)((buffer[4] << 8) & 0xF00 | buffer[3]); 73 calibration.y.center = GetYAxisCalibrationValue(spi_calibration.center);
74 calibration.x.min = GetXAxisCalibrationValue(spi_calibration.min);
75 calibration.y.min = GetYAxisCalibrationValue(spi_calibration.min);
76 calibration.x.max = GetXAxisCalibrationValue(spi_calibration.max);
77 calibration.y.max = GetYAxisCalibrationValue(spi_calibration.max);
78 }
79 79
80 // Set a valid default calibration if data is missing 80 // Set a valid default calibration if data is missing
81 ValidateCalibration(calibration); 81 ValidateCalibration(calibration);
@@ -85,39 +85,41 @@ DriverResult CalibrationProtocol::GetRightJoyStickCalibration(JoyStickCalibratio
85 85
86DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibration) { 86DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibration) {
87 ScopedSetBlocking sb(this); 87 ScopedSetBlocking sb(this);
88 std::vector<u8> buffer;
89 DriverResult result{DriverResult::Success}; 88 DriverResult result{DriverResult::Success};
89 ImuSpiCalibration spi_calibration{};
90 bool has_user_calibration = false;
90 calibration = {}; 91 calibration = {};
91 92
92 result = ReadSPI(CalAddr::USER_IMU_MAGIC, sizeof(u16), buffer);
93
94 if (result == DriverResult::Success) { 93 if (result == DriverResult::Success) {
95 const bool has_user_calibration = buffer[0] == 0xB2 && buffer[1] == 0xA1; 94 result = HasUserCalibration(SpiAddress::USER_IMU_MAGIC, has_user_calibration);
96 if (has_user_calibration) { 95 }
97 result = ReadSPI(CalAddr::USER_IMU_DATA, sizeof(IMUCalibration), buffer); 96
98 } else { 97 // Read User defined calibration
99 result = ReadSPI(CalAddr::FACT_IMU_DATA, sizeof(IMUCalibration), buffer); 98 if (result == DriverResult::Success && has_user_calibration) {
100 } 99 result = ReadSPI(SpiAddress::USER_IMU_DATA, spi_calibration);
100 }
101
102 // Read Factory calibration
103 if (result == DriverResult::Success && !has_user_calibration) {
104 result = ReadSPI(SpiAddress::FACT_IMU_DATA, spi_calibration);
101 } 105 }
102 106
103 if (result == DriverResult::Success) { 107 if (result == DriverResult::Success) {
104 IMUCalibration device_calibration{}; 108 calibration.accelerometer[0].offset = spi_calibration.accelerometer_offset[0];
105 memcpy(&device_calibration, buffer.data(), sizeof(IMUCalibration)); 109 calibration.accelerometer[1].offset = spi_calibration.accelerometer_offset[1];
106 calibration.accelerometer[0].offset = device_calibration.accelerometer_offset[0]; 110 calibration.accelerometer[2].offset = spi_calibration.accelerometer_offset[2];
107 calibration.accelerometer[1].offset = device_calibration.accelerometer_offset[1];
108 calibration.accelerometer[2].offset = device_calibration.accelerometer_offset[2];
109 111
110 calibration.accelerometer[0].scale = device_calibration.accelerometer_scale[0]; 112 calibration.accelerometer[0].scale = spi_calibration.accelerometer_scale[0];
111 calibration.accelerometer[1].scale = device_calibration.accelerometer_scale[1]; 113 calibration.accelerometer[1].scale = spi_calibration.accelerometer_scale[1];
112 calibration.accelerometer[2].scale = device_calibration.accelerometer_scale[2]; 114 calibration.accelerometer[2].scale = spi_calibration.accelerometer_scale[2];
113 115
114 calibration.gyro[0].offset = device_calibration.gyroscope_offset[0]; 116 calibration.gyro[0].offset = spi_calibration.gyroscope_offset[0];
115 calibration.gyro[1].offset = device_calibration.gyroscope_offset[1]; 117 calibration.gyro[1].offset = spi_calibration.gyroscope_offset[1];
116 calibration.gyro[2].offset = device_calibration.gyroscope_offset[2]; 118 calibration.gyro[2].offset = spi_calibration.gyroscope_offset[2];
117 119
118 calibration.gyro[0].scale = device_calibration.gyroscope_scale[0]; 120 calibration.gyro[0].scale = spi_calibration.gyroscope_scale[0];
119 calibration.gyro[1].scale = device_calibration.gyroscope_scale[1]; 121 calibration.gyro[1].scale = spi_calibration.gyroscope_scale[1];
120 calibration.gyro[2].scale = device_calibration.gyroscope_scale[2]; 122 calibration.gyro[2].scale = spi_calibration.gyroscope_scale[2];
121 } 123 }
122 124
123 ValidateCalibration(calibration); 125 ValidateCalibration(calibration);
@@ -127,10 +129,12 @@ DriverResult CalibrationProtocol::GetImuCalibration(MotionCalibration& calibrati
127 129
128DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibration, 130DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibration,
129 s16 current_value) { 131 s16 current_value) {
132 constexpr s16 DefaultRingRange{800};
133
130 // TODO: Get default calibration form ring itself 134 // TODO: Get default calibration form ring itself
131 if (ring_data_max == 0 && ring_data_min == 0) { 135 if (ring_data_max == 0 && ring_data_min == 0) {
132 ring_data_max = current_value + 800; 136 ring_data_max = current_value + DefaultRingRange;
133 ring_data_min = current_value - 800; 137 ring_data_min = current_value - DefaultRingRange;
134 ring_data_default = current_value; 138 ring_data_default = current_value;
135 } 139 }
136 ring_data_max = std::max(ring_data_max, current_value); 140 ring_data_max = std::max(ring_data_max, current_value);
@@ -143,42 +147,72 @@ DriverResult CalibrationProtocol::GetRingCalibration(RingCalibration& calibratio
143 return DriverResult::Success; 147 return DriverResult::Success;
144} 148}
145 149
150DriverResult CalibrationProtocol::HasUserCalibration(SpiAddress address,
151 bool& has_user_calibration) {
152 MagicSpiCalibration spi_magic{};
153 const DriverResult result{ReadSPI(address, spi_magic)};
154 has_user_calibration = false;
155 if (result == DriverResult::Success) {
156 has_user_calibration = spi_magic.first == CalibrationMagic::USR_MAGIC_0 &&
157 spi_magic.second == CalibrationMagic::USR_MAGIC_1;
158 }
159 return result;
160}
161
162u16 CalibrationProtocol::GetXAxisCalibrationValue(std::span<u8> block) const {
163 return static_cast<u16>(((block[1] & 0x0F) << 8) | block[0]);
164}
165
166u16 CalibrationProtocol::GetYAxisCalibrationValue(std::span<u8> block) const {
167 return static_cast<u16>((block[2] << 4) | (block[1] >> 4));
168}
169
146void CalibrationProtocol::ValidateCalibration(JoyStickCalibration& calibration) { 170void CalibrationProtocol::ValidateCalibration(JoyStickCalibration& calibration) {
147 constexpr u16 DefaultStickCenter{2048}; 171 constexpr u16 DefaultStickCenter{0x800};
148 constexpr u16 DefaultStickRange{1740}; 172 constexpr u16 DefaultStickRange{0x6cc};
149 173
150 if (calibration.x.center == 0xFFF || calibration.x.center == 0) { 174 calibration.x.center = ValidateValue(calibration.x.center, DefaultStickCenter);
151 calibration.x.center = DefaultStickCenter; 175 calibration.x.max = ValidateValue(calibration.x.max, DefaultStickRange);
152 } 176 calibration.x.min = ValidateValue(calibration.x.min, DefaultStickRange);
153 if (calibration.x.max == 0xFFF || calibration.x.max == 0) { 177
154 calibration.x.max = DefaultStickRange; 178 calibration.y.center = ValidateValue(calibration.y.center, DefaultStickCenter);
179 calibration.y.max = ValidateValue(calibration.y.max, DefaultStickRange);
180 calibration.y.min = ValidateValue(calibration.y.min, DefaultStickRange);
181}
182
183void CalibrationProtocol::ValidateCalibration(MotionCalibration& calibration) {
184 constexpr s16 DefaultAccelerometerScale{0x4000};
185 constexpr s16 DefaultGyroScale{0x3be7};
186 constexpr s16 DefaultOffset{0};
187
188 for (auto& sensor : calibration.accelerometer) {
189 sensor.scale = ValidateValue(sensor.scale, DefaultAccelerometerScale);
190 sensor.offset = ValidateValue(sensor.offset, DefaultOffset);
155 } 191 }
156 if (calibration.x.min == 0xFFF || calibration.x.min == 0) { 192 for (auto& sensor : calibration.gyro) {
157 calibration.x.min = DefaultStickRange; 193 sensor.scale = ValidateValue(sensor.scale, DefaultGyroScale);
194 sensor.offset = ValidateValue(sensor.offset, DefaultOffset);
158 } 195 }
196}
159 197
160 if (calibration.y.center == 0xFFF || calibration.y.center == 0) { 198u16 CalibrationProtocol::ValidateValue(u16 value, u16 default_value) const {
161 calibration.y.center = DefaultStickCenter; 199 if (value == 0) {
162 } 200 return default_value;
163 if (calibration.y.max == 0xFFF || calibration.y.max == 0) {
164 calibration.y.max = DefaultStickRange;
165 } 201 }
166 if (calibration.y.min == 0xFFF || calibration.y.min == 0) { 202 if (value == 0xFFF) {
167 calibration.y.min = DefaultStickRange; 203 return default_value;
168 } 204 }
205 return value;
169} 206}
170 207
171void CalibrationProtocol::ValidateCalibration(MotionCalibration& calibration) { 208s16 CalibrationProtocol::ValidateValue(s16 value, s16 default_value) const {
172 for (auto& sensor : calibration.accelerometer) { 209 if (value == 0) {
173 if (sensor.scale == 0) { 210 return default_value;
174 sensor.scale = 0x4000;
175 }
176 } 211 }
177 for (auto& sensor : calibration.gyro) { 212 if (value == 0xFFF) {
178 if (sensor.scale == 0) { 213 return default_value;
179 sensor.scale = 0x3be7;
180 }
181 } 214 }
215 return value;
182} 216}
183 217
184} // namespace InputCommon::Joycon 218} // namespace InputCommon::Joycon
diff --git a/src/input_common/helpers/joycon_protocol/calibration.h b/src/input_common/helpers/joycon_protocol/calibration.h
index afb52a36a..c6fd0f729 100644
--- a/src/input_common/helpers/joycon_protocol/calibration.h
+++ b/src/input_common/helpers/joycon_protocol/calibration.h
@@ -53,9 +53,27 @@ public:
53 DriverResult GetRingCalibration(RingCalibration& calibration, s16 current_value); 53 DriverResult GetRingCalibration(RingCalibration& calibration, s16 current_value);
54 54
55private: 55private:
56 /// Returns true if the specified address corresponds to the magic value of user calibration
57 DriverResult HasUserCalibration(SpiAddress address, bool& has_user_calibration);
58
59 /// Converts a raw calibration block to an u16 value containing the x axis value
60 u16 GetXAxisCalibrationValue(std::span<u8> block) const;
61
62 /// Converts a raw calibration block to an u16 value containing the y axis value
63 u16 GetYAxisCalibrationValue(std::span<u8> block) const;
64
65 /// Ensures that all joystick calibration values are set
56 void ValidateCalibration(JoyStickCalibration& calibration); 66 void ValidateCalibration(JoyStickCalibration& calibration);
67
68 /// Ensures that all motion calibration values are set
57 void ValidateCalibration(MotionCalibration& calibration); 69 void ValidateCalibration(MotionCalibration& calibration);
58 70
71 /// Returns the default value if the value is either zero or 0xFFF
72 u16 ValidateValue(u16 value, u16 default_value) const;
73
74 /// Returns the default value if the value is either zero or 0xFFF
75 s16 ValidateValue(s16 value, s16 default_value) const;
76
59 s16 ring_data_max = 0; 77 s16 ring_data_max = 0;
60 s16 ring_data_default = 0; 78 s16 ring_data_default = 0;
61 s16 ring_data_min = 0; 79 s16 ring_data_min = 0;
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.cpp b/src/input_common/helpers/joycon_protocol/common_protocol.cpp
index 417d0dcc5..2b42a4555 100644
--- a/src/input_common/helpers/joycon_protocol/common_protocol.cpp
+++ b/src/input_common/helpers/joycon_protocol/common_protocol.cpp
@@ -22,12 +22,9 @@ void JoyconCommonProtocol::SetNonBlocking() {
22} 22}
23 23
24DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type) { 24DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type) {
25 std::vector<u8> buffer; 25 const auto result = ReadSPI(SpiAddress::DEVICE_TYPE, controller_type);
26 const auto result = ReadSPI(CalAddr::DEVICE_TYPE, 1, buffer);
27 controller_type = ControllerType::None;
28 26
29 if (result == DriverResult::Success) { 27 if (result == DriverResult::Success) {
30 controller_type = static_cast<ControllerType>(buffer[0]);
31 // Fallback to 3rd party pro controllers 28 // Fallback to 3rd party pro controllers
32 if (controller_type == ControllerType::None) { 29 if (controller_type == ControllerType::None) {
33 controller_type = ControllerType::Pro; 30 controller_type = ControllerType::Pro;
@@ -40,6 +37,7 @@ DriverResult JoyconCommonProtocol::GetDeviceType(ControllerType& controller_type
40DriverResult JoyconCommonProtocol::CheckDeviceAccess(SDL_hid_device_info* device_info) { 37DriverResult JoyconCommonProtocol::CheckDeviceAccess(SDL_hid_device_info* device_info) {
41 ControllerType controller_type{ControllerType::None}; 38 ControllerType controller_type{ControllerType::None};
42 const auto result = GetDeviceType(controller_type); 39 const auto result = GetDeviceType(controller_type);
40
43 if (result != DriverResult::Success || controller_type == ControllerType::None) { 41 if (result != DriverResult::Success || controller_type == ControllerType::None) {
44 return DriverResult::UnsupportedControllerType; 42 return DriverResult::UnsupportedControllerType;
45 } 43 }
@@ -62,7 +60,7 @@ DriverResult JoyconCommonProtocol::SetReportMode(ReportMode report_mode) {
62 return SendSubCommand(SubCommand::SET_REPORT_MODE, buffer); 60 return SendSubCommand(SubCommand::SET_REPORT_MODE, buffer);
63} 61}
64 62
65DriverResult JoyconCommonProtocol::SendData(std::span<const u8> buffer) { 63DriverResult JoyconCommonProtocol::SendRawData(std::span<const u8> buffer) {
66 const auto result = SDL_hid_write(hidapi_handle->handle, buffer.data(), buffer.size()); 64 const auto result = SDL_hid_write(hidapi_handle->handle, buffer.data(), buffer.size());
67 65
68 if (result == -1) { 66 if (result == -1) {
@@ -72,15 +70,15 @@ DriverResult JoyconCommonProtocol::SendData(std::span<const u8> buffer) {
72 return DriverResult::Success; 70 return DriverResult::Success;
73} 71}
74 72
75DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc, std::vector<u8>& output) { 73DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc,
74 SubCommandResponse& output) {
76 constexpr int timeout_mili = 66; 75 constexpr int timeout_mili = 66;
77 constexpr int MaxTries = 15; 76 constexpr int MaxTries = 15;
78 int tries = 0; 77 int tries = 0;
79 output.resize(MaxSubCommandResponseSize);
80 78
81 do { 79 do {
82 int result = SDL_hid_read_timeout(hidapi_handle->handle, output.data(), 80 int result = SDL_hid_read_timeout(hidapi_handle->handle, reinterpret_cast<u8*>(&output),
83 MaxSubCommandResponseSize, timeout_mili); 81 sizeof(SubCommandResponse), timeout_mili);
84 82
85 if (result < 1) { 83 if (result < 1) {
86 LOG_ERROR(Input, "No response from joycon"); 84 LOG_ERROR(Input, "No response from joycon");
@@ -88,27 +86,28 @@ DriverResult JoyconCommonProtocol::GetSubCommandResponse(SubCommand sc, std::vec
88 if (tries++ > MaxTries) { 86 if (tries++ > MaxTries) {
89 return DriverResult::Timeout; 87 return DriverResult::Timeout;
90 } 88 }
91 } while (output[0] != 0x21 && output[14] != static_cast<u8>(sc)); 89 } while (output.input_report.report_mode != ReportMode::SUBCMD_REPLY &&
92 90 output.sub_command != sc);
93 if (output[0] != 0x21 && output[14] != static_cast<u8>(sc)) {
94 return DriverResult::WrongReply;
95 }
96 91
97 return DriverResult::Success; 92 return DriverResult::Success;
98} 93}
99 94
100DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer, 95DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer,
101 std::vector<u8>& output) { 96 SubCommandResponse& output) {
102 std::vector<u8> local_buffer(MaxResponseSize); 97 SubCommandPacket packet{
103 98 .output_report = OutputReport::RUMBLE_AND_SUBCMD,
104 local_buffer[0] = static_cast<u8>(OutputReport::RUMBLE_AND_SUBCMD); 99 .packet_counter = GetCounter(),
105 local_buffer[1] = GetCounter(); 100 .sub_command = sc,
106 local_buffer[10] = static_cast<u8>(sc); 101 .command_data = {},
107 for (std::size_t i = 0; i < buffer.size(); ++i) { 102 };
108 local_buffer[11 + i] = buffer[i]; 103
104 if (buffer.size() > packet.command_data.size()) {
105 return DriverResult::InvalidParameters;
109 } 106 }
110 107
111 auto result = SendData(local_buffer); 108 memcpy(packet.command_data.data(), buffer.data(), buffer.size());
109
110 auto result = SendData(packet);
112 111
113 if (result != DriverResult::Success) { 112 if (result != DriverResult::Success) {
114 return result; 113 return result;
@@ -120,44 +119,57 @@ DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const
120} 119}
121 120
122DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer) { 121DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const u8> buffer) {
123 std::vector<u8> output; 122 SubCommandResponse output{};
124 return SendSubCommand(sc, buffer, output); 123 return SendSubCommand(sc, buffer, output);
125} 124}
126 125
127DriverResult JoyconCommonProtocol::SendMCUCommand(SubCommand sc, std::span<const u8> buffer) { 126DriverResult JoyconCommonProtocol::SendMCUCommand(SubCommand sc, std::span<const u8> buffer) {
128 std::vector<u8> local_buffer(MaxResponseSize); 127 SubCommandPacket packet{
129 128 .output_report = OutputReport::MCU_DATA,
130 local_buffer[0] = static_cast<u8>(OutputReport::MCU_DATA); 129 .packet_counter = GetCounter(),
131 local_buffer[1] = GetCounter(); 130 .sub_command = sc,
132 local_buffer[10] = static_cast<u8>(sc); 131 .command_data = {},
133 for (std::size_t i = 0; i < buffer.size(); ++i) { 132 };
134 local_buffer[11 + i] = buffer[i]; 133
134 if (buffer.size() > packet.command_data.size()) {
135 return DriverResult::InvalidParameters;
135 } 136 }
136 137
137 return SendData(local_buffer); 138 memcpy(packet.command_data.data(), buffer.data(), buffer.size());
139
140 return SendData(packet);
138} 141}
139 142
140DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffer) { 143DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffer) {
141 std::vector<u8> local_buffer(MaxResponseSize); 144 VibrationPacket packet{
142 145 .output_report = OutputReport::RUMBLE_ONLY,
143 local_buffer[0] = static_cast<u8>(Joycon::OutputReport::RUMBLE_ONLY); 146 .packet_counter = GetCounter(),
144 local_buffer[1] = GetCounter(); 147 .vibration_data = {},
148 };
149
150 if (buffer.size() > packet.vibration_data.size()) {
151 return DriverResult::InvalidParameters;
152 }
145 153
146 memcpy(local_buffer.data() + 2, buffer.data(), buffer.size()); 154 memcpy(packet.vibration_data.data(), buffer.data(), buffer.size());
147 155
148 return SendData(local_buffer); 156 return SendData(packet);
149} 157}
150 158
151DriverResult JoyconCommonProtocol::ReadSPI(CalAddr addr, u8 size, std::vector<u8>& output) { 159DriverResult JoyconCommonProtocol::ReadRawSPI(SpiAddress addr, std::span<u8> output) {
160 constexpr std::size_t HeaderSize = 5;
152 constexpr std::size_t MaxTries = 10; 161 constexpr std::size_t MaxTries = 10;
153 std::size_t tries = 0; 162 std::size_t tries = 0;
154 std::array<u8, 5> buffer = {0x00, 0x00, 0x00, 0x00, size}; 163 SubCommandResponse response{};
155 std::vector<u8> local_buffer(size + 20); 164 std::array<u8, sizeof(ReadSpiPacket)> buffer{};
156 165 const ReadSpiPacket packet_data{
157 buffer[0] = static_cast<u8>(static_cast<u16>(addr) & 0x00FF); 166 .spi_address = addr,
158 buffer[1] = static_cast<u8>((static_cast<u16>(addr) & 0xFF00) >> 8); 167 .size = static_cast<u8>(output.size()),
168 };
169
170 memcpy(buffer.data(), &packet_data, sizeof(ReadSpiPacket));
159 do { 171 do {
160 const auto result = SendSubCommand(SubCommand::SPI_FLASH_READ, buffer, local_buffer); 172 const auto result = SendSubCommand(SubCommand::SPI_FLASH_READ, buffer, response);
161 if (result != DriverResult::Success) { 173 if (result != DriverResult::Success) {
162 return result; 174 return result;
163 } 175 }
@@ -165,10 +177,14 @@ DriverResult JoyconCommonProtocol::ReadSPI(CalAddr addr, u8 size, std::vector<u8
165 if (tries++ > MaxTries) { 177 if (tries++ > MaxTries) {
166 return DriverResult::Timeout; 178 return DriverResult::Timeout;
167 } 179 }
168 } while (local_buffer[15] != buffer[0] || local_buffer[16] != buffer[1]); 180 } while (response.spi_address != addr);
181
182 if (response.command_data.size() < packet_data.size + HeaderSize) {
183 return DriverResult::WrongReply;
184 }
169 185
170 // Remove header from output 186 // Remove header from output
171 output = std::vector<u8>(local_buffer.begin() + 20, local_buffer.begin() + 20 + size); 187 memcpy(output.data(), response.command_data.data() + HeaderSize, packet_data.size);
172 return DriverResult::Success; 188 return DriverResult::Success;
173} 189}
174 190
@@ -177,7 +193,7 @@ DriverResult JoyconCommonProtocol::EnableMCU(bool enable) {
177 const auto result = SendSubCommand(SubCommand::SET_MCU_STATE, mcu_state); 193 const auto result = SendSubCommand(SubCommand::SET_MCU_STATE, mcu_state);
178 194
179 if (result != DriverResult::Success) { 195 if (result != DriverResult::Success) {
180 LOG_ERROR(Input, "SendMCUData failed with error {}", result); 196 LOG_ERROR(Input, "Failed with error {}", result);
181 } 197 }
182 198
183 return result; 199 return result;
@@ -192,22 +208,21 @@ DriverResult JoyconCommonProtocol::ConfigureMCU(const MCUConfig& config) {
192 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, config_buffer); 208 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, config_buffer);
193 209
194 if (result != DriverResult::Success) { 210 if (result != DriverResult::Success) {
195 LOG_ERROR(Input, "Set MCU config failed with error {}", result); 211 LOG_ERROR(Input, "Failed with error {}", result);
196 } 212 }
197 213
198 return result; 214 return result;
199} 215}
200 216
201DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode_, 217DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode,
202 std::vector<u8>& output) { 218 MCUCommandResponse& output) {
203 const int report_mode = static_cast<u8>(report_mode_);
204 constexpr int TimeoutMili = 200; 219 constexpr int TimeoutMili = 200;
205 constexpr int MaxTries = 9; 220 constexpr int MaxTries = 9;
206 int tries = 0; 221 int tries = 0;
207 output.resize(0x170);
208 222
209 do { 223 do {
210 int result = SDL_hid_read_timeout(hidapi_handle->handle, output.data(), 0x170, TimeoutMili); 224 int result = SDL_hid_read_timeout(hidapi_handle->handle, reinterpret_cast<u8*>(&output),
225 sizeof(MCUCommandResponse), TimeoutMili);
211 226
212 if (result < 1) { 227 if (result < 1) {
213 LOG_ERROR(Input, "No response from joycon attempt {}", tries); 228 LOG_ERROR(Input, "No response from joycon attempt {}", tries);
@@ -215,28 +230,29 @@ DriverResult JoyconCommonProtocol::GetMCUDataResponse(ReportMode report_mode_,
215 if (tries++ > MaxTries) { 230 if (tries++ > MaxTries) {
216 return DriverResult::Timeout; 231 return DriverResult::Timeout;
217 } 232 }
218 } while (output[0] != report_mode || output[49] == 0xFF); 233 } while (output.input_report.report_mode != report_mode ||
219 234 output.mcu_report == MCUReport::EmptyAwaitingCmd);
220 if (output[0] != report_mode || output[49] == 0xFF) {
221 return DriverResult::WrongReply;
222 }
223 235
224 return DriverResult::Success; 236 return DriverResult::Success;
225} 237}
226 238
227DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, SubCommand sc, 239DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, SubCommand sc,
228 std::span<const u8> buffer, 240 std::span<const u8> buffer,
229 std::vector<u8>& output) { 241 MCUCommandResponse& output) {
230 std::vector<u8> local_buffer(MaxResponseSize); 242 SubCommandPacket packet{
231 243 .output_report = OutputReport::MCU_DATA,
232 local_buffer[0] = static_cast<u8>(OutputReport::MCU_DATA); 244 .packet_counter = GetCounter(),
233 local_buffer[1] = GetCounter(); 245 .sub_command = sc,
234 local_buffer[9] = static_cast<u8>(sc); 246 .command_data = {},
235 for (std::size_t i = 0; i < buffer.size(); ++i) { 247 };
236 local_buffer[10 + i] = buffer[i]; 248
249 if (buffer.size() > packet.command_data.size()) {
250 return DriverResult::InvalidParameters;
237 } 251 }
238 252
239 auto result = SendData(local_buffer); 253 memcpy(packet.command_data.data(), buffer.data(), buffer.size());
254
255 auto result = SendData(packet);
240 256
241 if (result != DriverResult::Success) { 257 if (result != DriverResult::Success) {
242 return result; 258 return result;
@@ -248,7 +264,7 @@ DriverResult JoyconCommonProtocol::SendMCUData(ReportMode report_mode, SubComman
248} 264}
249 265
250DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMode mode) { 266DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMode mode) {
251 std::vector<u8> output; 267 MCUCommandResponse output{};
252 constexpr std::size_t MaxTries{8}; 268 constexpr std::size_t MaxTries{8};
253 std::size_t tries{}; 269 std::size_t tries{};
254 270
@@ -263,7 +279,8 @@ DriverResult JoyconCommonProtocol::WaitSetMCUMode(ReportMode report_mode, MCUMod
263 if (tries++ > MaxTries) { 279 if (tries++ > MaxTries) {
264 return DriverResult::WrongReply; 280 return DriverResult::WrongReply;
265 } 281 }
266 } while (output[49] != 1 || output[56] != static_cast<u8>(mode)); 282 } while (output.mcu_report != MCUReport::StateReport ||
283 output.mcu_data[6] != static_cast<u8>(mode));
267 284
268 return DriverResult::Success; 285 return DriverResult::Success;
269} 286}
diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.h b/src/input_common/helpers/joycon_protocol/common_protocol.h
index 903bcf402..f44f73ba4 100644
--- a/src/input_common/helpers/joycon_protocol/common_protocol.h
+++ b/src/input_common/helpers/joycon_protocol/common_protocol.h
@@ -57,22 +57,31 @@ public:
57 * Sends data to the joycon device 57 * Sends data to the joycon device
58 * @param buffer data to be send 58 * @param buffer data to be send
59 */ 59 */
60 DriverResult SendData(std::span<const u8> buffer); 60 DriverResult SendRawData(std::span<const u8> buffer);
61
62 template <typename Output>
63 requires std::is_trivially_copyable_v<Output>
64 DriverResult SendData(const Output& output) {
65 std::array<u8, sizeof(Output)> buffer;
66 std::memcpy(buffer.data(), &output, sizeof(Output));
67 return SendRawData(buffer);
68 }
61 69
62 /** 70 /**
63 * Waits for incoming data of the joycon device that matchs the subcommand 71 * Waits for incoming data of the joycon device that matchs the subcommand
64 * @param sub_command type of data to be returned 72 * @param sub_command type of data to be returned
65 * @returns a buffer containing the responce 73 * @returns a buffer containing the response
66 */ 74 */
67 DriverResult GetSubCommandResponse(SubCommand sub_command, std::vector<u8>& output); 75 DriverResult GetSubCommandResponse(SubCommand sub_command, SubCommandResponse& output);
68 76
69 /** 77 /**
70 * Sends a sub command to the device and waits for it's reply 78 * Sends a sub command to the device and waits for it's reply
71 * @param sc sub command to be send 79 * @param sc sub command to be send
72 * @param buffer data to be send 80 * @param buffer data to be send
73 * @returns output buffer containing the responce 81 * @returns output buffer containing the response
74 */ 82 */
75 DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer, std::vector<u8>& output); 83 DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer,
84 SubCommandResponse& output);
76 85
77 /** 86 /**
78 * Sends a sub command to the device and waits for it's reply and ignores the output 87 * Sends a sub command to the device and waits for it's reply and ignores the output
@@ -97,10 +106,29 @@ public:
97 /** 106 /**
98 * Reads the SPI memory stored on the joycon 107 * Reads the SPI memory stored on the joycon
99 * @param Initial address location 108 * @param Initial address location
100 * @param size in bytes to be read 109 * @returns output buffer containing the response
101 * @returns output buffer containing the responce
102 */ 110 */
103 DriverResult ReadSPI(CalAddr addr, u8 size, std::vector<u8>& output); 111 DriverResult ReadRawSPI(SpiAddress addr, std::span<u8> output);
112
113 /**
114 * Reads the SPI memory stored on the joycon
115 * @param Initial address location
116 * @returns output object containing the response
117 */
118 template <typename Output>
119 requires std::is_trivially_copyable_v<Output>
120 DriverResult ReadSPI(SpiAddress addr, Output& output) {
121 std::array<u8, sizeof(Output)> buffer;
122 output = {};
123
124 const auto result = ReadRawSPI(addr, buffer);
125 if (result != DriverResult::Success) {
126 return result;
127 }
128
129 std::memcpy(&output, buffer.data(), sizeof(Output));
130 return DriverResult::Success;
131 }
104 132
105 /** 133 /**
106 * Enables MCU chip on the joycon 134 * Enables MCU chip on the joycon
@@ -117,19 +145,19 @@ public:
117 /** 145 /**
118 * Waits until there's MCU data available. On timeout returns error 146 * Waits until there's MCU data available. On timeout returns error
119 * @param report mode of the expected reply 147 * @param report mode of the expected reply
120 * @returns a buffer containing the responce 148 * @returns a buffer containing the response
121 */ 149 */
122 DriverResult GetMCUDataResponse(ReportMode report_mode_, std::vector<u8>& output); 150 DriverResult GetMCUDataResponse(ReportMode report_mode_, MCUCommandResponse& output);
123 151
124 /** 152 /**
125 * Sends data to the MCU chip and waits for it's reply 153 * Sends data to the MCU chip and waits for it's reply
126 * @param report mode of the expected reply 154 * @param report mode of the expected reply
127 * @param sub command to be send 155 * @param sub command to be send
128 * @param buffer data to be send 156 * @param buffer data to be send
129 * @returns output buffer containing the responce 157 * @returns output buffer containing the response
130 */ 158 */
131 DriverResult SendMCUData(ReportMode report_mode, SubCommand sc, std::span<const u8> buffer, 159 DriverResult SendMCUData(ReportMode report_mode, SubCommand sc, std::span<const u8> buffer,
132 std::vector<u8>& output); 160 MCUCommandResponse& output);
133 161
134 /** 162 /**
135 * Wait's until the MCU chip is on the specified mode 163 * Wait's until the MCU chip is on the specified mode
diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.cpp b/src/input_common/helpers/joycon_protocol/generic_functions.cpp
index 52bb8b61a..548a4b9e3 100644
--- a/src/input_common/helpers/joycon_protocol/generic_functions.cpp
+++ b/src/input_common/helpers/joycon_protocol/generic_functions.cpp
@@ -19,15 +19,26 @@ DriverResult GenericProtocol::EnableActiveMode() {
19 return SetReportMode(ReportMode::STANDARD_FULL_60HZ); 19 return SetReportMode(ReportMode::STANDARD_FULL_60HZ);
20} 20}
21 21
22DriverResult GenericProtocol::SetLowPowerMode(bool enable) {
23 ScopedSetBlocking sb(this);
24 const std::array<u8, 1> buffer{static_cast<u8>(enable ? 1 : 0)};
25 return SendSubCommand(SubCommand::LOW_POWER_MODE, buffer);
26}
27
28DriverResult GenericProtocol::TriggersElapsed() {
29 ScopedSetBlocking sb(this);
30 return SendSubCommand(SubCommand::TRIGGERS_ELAPSED, {});
31}
32
22DriverResult GenericProtocol::GetDeviceInfo(DeviceInfo& device_info) { 33DriverResult GenericProtocol::GetDeviceInfo(DeviceInfo& device_info) {
23 ScopedSetBlocking sb(this); 34 ScopedSetBlocking sb(this);
24 std::vector<u8> output; 35 SubCommandResponse output{};
25 36
26 const auto result = SendSubCommand(SubCommand::REQ_DEV_INFO, {}, output); 37 const auto result = SendSubCommand(SubCommand::REQ_DEV_INFO, {}, output);
27 38
28 device_info = {}; 39 device_info = {};
29 if (result == DriverResult::Success) { 40 if (result == DriverResult::Success) {
30 memcpy(&device_info, output.data(), sizeof(DeviceInfo)); 41 device_info = output.device_info;
31 } 42 }
32 43
33 return result; 44 return result;
@@ -60,8 +71,8 @@ DriverResult GenericProtocol::GetBattery(u32& battery_level) {
60 71
61DriverResult GenericProtocol::GetColor(Color& color) { 72DriverResult GenericProtocol::GetColor(Color& color) {
62 ScopedSetBlocking sb(this); 73 ScopedSetBlocking sb(this);
63 std::vector<u8> buffer; 74 std::array<u8, 12> buffer{};
64 const auto result = ReadSPI(CalAddr::COLOR_DATA, 12, buffer); 75 const auto result = ReadRawSPI(SpiAddress::COLOR_DATA, buffer);
65 76
66 color = {}; 77 color = {};
67 if (result == DriverResult::Success) { 78 if (result == DriverResult::Success) {
@@ -76,8 +87,8 @@ DriverResult GenericProtocol::GetColor(Color& color) {
76 87
77DriverResult GenericProtocol::GetSerialNumber(SerialNumber& serial_number) { 88DriverResult GenericProtocol::GetSerialNumber(SerialNumber& serial_number) {
78 ScopedSetBlocking sb(this); 89 ScopedSetBlocking sb(this);
79 std::vector<u8> buffer; 90 std::array<u8, 16> buffer{};
80 const auto result = ReadSPI(CalAddr::SERIAL_NUMBER, 16, buffer); 91 const auto result = ReadRawSPI(SpiAddress::SERIAL_NUMBER, buffer);
81 92
82 serial_number = {}; 93 serial_number = {};
83 if (result == DriverResult::Success) { 94 if (result == DriverResult::Success) {
diff --git a/src/input_common/helpers/joycon_protocol/generic_functions.h b/src/input_common/helpers/joycon_protocol/generic_functions.h
index 239bb7dbf..424831e81 100644
--- a/src/input_common/helpers/joycon_protocol/generic_functions.h
+++ b/src/input_common/helpers/joycon_protocol/generic_functions.h
@@ -25,6 +25,12 @@ public:
25 /// Enables active mode. This mode will return the current status every 5-15ms 25 /// Enables active mode. This mode will return the current status every 5-15ms
26 DriverResult EnableActiveMode(); 26 DriverResult EnableActiveMode();
27 27
28 /// Enables or disables the low power mode
29 DriverResult SetLowPowerMode(bool enable);
30
31 /// Unknown function used by the switch
32 DriverResult TriggersElapsed();
33
28 /** 34 /**
29 * Sends a request to obtain the joycon firmware and mac from handle 35 * Sends a request to obtain the joycon firmware and mac from handle
30 * @returns controller device info 36 * @returns controller device info
diff --git a/src/input_common/helpers/joycon_protocol/irs.cpp b/src/input_common/helpers/joycon_protocol/irs.cpp
index 09e17bc5b..731fd5981 100644
--- a/src/input_common/helpers/joycon_protocol/irs.cpp
+++ b/src/input_common/helpers/joycon_protocol/irs.cpp
@@ -132,7 +132,7 @@ DriverResult IrsProtocol::RequestImage(std::span<u8> buffer) {
132DriverResult IrsProtocol::ConfigureIrs() { 132DriverResult IrsProtocol::ConfigureIrs() {
133 LOG_DEBUG(Input, "Configure IRS"); 133 LOG_DEBUG(Input, "Configure IRS");
134 constexpr std::size_t max_tries = 28; 134 constexpr std::size_t max_tries = 28;
135 std::vector<u8> output; 135 SubCommandResponse output{};
136 std::size_t tries = 0; 136 std::size_t tries = 0;
137 137
138 const IrsConfigure irs_configuration{ 138 const IrsConfigure irs_configuration{
@@ -158,7 +158,7 @@ DriverResult IrsProtocol::ConfigureIrs() {
158 if (tries++ >= max_tries) { 158 if (tries++ >= max_tries) {
159 return DriverResult::WrongReply; 159 return DriverResult::WrongReply;
160 } 160 }
161 } while (output[15] != 0x0b); 161 } while (output.command_data[0] != 0x0b);
162 162
163 return DriverResult::Success; 163 return DriverResult::Success;
164} 164}
@@ -167,7 +167,7 @@ DriverResult IrsProtocol::WriteRegistersStep1() {
167 LOG_DEBUG(Input, "WriteRegistersStep1"); 167 LOG_DEBUG(Input, "WriteRegistersStep1");
168 DriverResult result{DriverResult::Success}; 168 DriverResult result{DriverResult::Success};
169 constexpr std::size_t max_tries = 28; 169 constexpr std::size_t max_tries = 28;
170 std::vector<u8> output; 170 SubCommandResponse output{};
171 std::size_t tries = 0; 171 std::size_t tries = 0;
172 172
173 const IrsWriteRegisters irs_registers{ 173 const IrsWriteRegisters irs_registers{
@@ -218,7 +218,8 @@ DriverResult IrsProtocol::WriteRegistersStep1() {
218 if (tries++ >= max_tries) { 218 if (tries++ >= max_tries) {
219 return DriverResult::WrongReply; 219 return DriverResult::WrongReply;
220 } 220 }
221 } while (!(output[15] == 0x13 && output[17] == 0x07) && output[15] != 0x23); 221 } while (!(output.command_data[0] == 0x13 && output.command_data[2] == 0x07) &&
222 output.command_data[0] != 0x23);
222 223
223 return DriverResult::Success; 224 return DriverResult::Success;
224} 225}
@@ -226,7 +227,7 @@ DriverResult IrsProtocol::WriteRegistersStep1() {
226DriverResult IrsProtocol::WriteRegistersStep2() { 227DriverResult IrsProtocol::WriteRegistersStep2() {
227 LOG_DEBUG(Input, "WriteRegistersStep2"); 228 LOG_DEBUG(Input, "WriteRegistersStep2");
228 constexpr std::size_t max_tries = 28; 229 constexpr std::size_t max_tries = 28;
229 std::vector<u8> output; 230 SubCommandResponse output{};
230 std::size_t tries = 0; 231 std::size_t tries = 0;
231 232
232 const IrsWriteRegisters irs_registers{ 233 const IrsWriteRegisters irs_registers{
@@ -260,7 +261,7 @@ DriverResult IrsProtocol::WriteRegistersStep2() {
260 if (tries++ >= max_tries) { 261 if (tries++ >= max_tries) {
261 return DriverResult::WrongReply; 262 return DriverResult::WrongReply;
262 } 263 }
263 } while (output[15] != 0x13 && output[15] != 0x23); 264 } while (output.command_data[0] != 0x13 && output.command_data[0] != 0x23);
264 265
265 return DriverResult::Success; 266 return DriverResult::Success;
266} 267}
diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h
index e2d47349f..b91934990 100644
--- a/src/input_common/helpers/joycon_protocol/joycon_types.h
+++ b/src/input_common/helpers/joycon_protocol/joycon_types.h
@@ -19,20 +19,24 @@
19namespace InputCommon::Joycon { 19namespace InputCommon::Joycon {
20constexpr u32 MaxErrorCount = 50; 20constexpr u32 MaxErrorCount = 50;
21constexpr u32 MaxBufferSize = 368; 21constexpr u32 MaxBufferSize = 368;
22constexpr u32 MaxResponseSize = 49;
23constexpr u32 MaxSubCommandResponseSize = 64;
24constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x1, 0x40, 0x40}; 22constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x1, 0x40, 0x40};
25 23
26using MacAddress = std::array<u8, 6>; 24using MacAddress = std::array<u8, 6>;
27using SerialNumber = std::array<u8, 15>; 25using SerialNumber = std::array<u8, 15>;
28 26
29enum class ControllerType { 27enum class ControllerType : u8 {
30 None, 28 None = 0x00,
31 Left, 29 Left = 0x01,
32 Right, 30 Right = 0x02,
33 Pro, 31 Pro = 0x03,
34 Grip, 32 Dual = 0x05, // TODO: Verify this id
35 Dual, 33 LarkHvc1 = 0x07,
34 LarkHvc2 = 0x08,
35 LarkNesLeft = 0x09,
36 LarkNesRight = 0x0A,
37 Lucia = 0x0B,
38 Lagon = 0x0C,
39 Lager = 0x0D,
36}; 40};
37 41
38enum class PadAxes { 42enum class PadAxes {
@@ -99,14 +103,6 @@ enum class OutputReport : u8 {
99 USB_CMD = 0x80, 103 USB_CMD = 0x80,
100}; 104};
101 105
102enum class InputReport : u8 {
103 SUBCMD_REPLY = 0x21,
104 STANDARD_FULL_60HZ = 0x30,
105 NFC_IR_MODE_60HZ = 0x31,
106 SIMPLE_HID_MODE = 0x3F,
107 INPUT_USB_RESPONSE = 0x81,
108};
109
110enum class FeatureReport : u8 { 106enum class FeatureReport : u8 {
111 Last_SUBCMD = 0x02, 107 Last_SUBCMD = 0x02,
112 OTA_GW_UPGRADE = 0x70, 108 OTA_GW_UPGRADE = 0x70,
@@ -129,6 +125,7 @@ enum class SubCommand : u8 {
129 LOW_POWER_MODE = 0x08, 125 LOW_POWER_MODE = 0x08,
130 SPI_FLASH_READ = 0x10, 126 SPI_FLASH_READ = 0x10,
131 SPI_FLASH_WRITE = 0x11, 127 SPI_FLASH_WRITE = 0x11,
128 SPI_SECTOR_ERASE = 0x12,
132 RESET_MCU = 0x20, 129 RESET_MCU = 0x20,
133 SET_MCU_CONFIG = 0x21, 130 SET_MCU_CONFIG = 0x21,
134 SET_MCU_STATE = 0x22, 131 SET_MCU_STATE = 0x22,
@@ -142,9 +139,10 @@ enum class SubCommand : u8 {
142 ENABLE_VIBRATION = 0x48, 139 ENABLE_VIBRATION = 0x48,
143 GET_REGULATED_VOLTAGE = 0x50, 140 GET_REGULATED_VOLTAGE = 0x50,
144 SET_EXTERNAL_CONFIG = 0x58, 141 SET_EXTERNAL_CONFIG = 0x58,
145 UNKNOWN_RINGCON = 0x59, 142 GET_EXTERNAL_DEVICE_INFO = 0x59,
146 UNKNOWN_RINGCON2 = 0x5A, 143 ENABLE_EXTERNAL_POLLING = 0x5A,
147 UNKNOWN_RINGCON3 = 0x5C, 144 DISABLE_EXTERNAL_POLLING = 0x5B,
145 SET_EXTERNAL_FORMAT_CONFIG = 0x5C,
148}; 146};
149 147
150enum class UsbSubCommand : u8 { 148enum class UsbSubCommand : u8 {
@@ -158,26 +156,31 @@ enum class UsbSubCommand : u8 {
158 SEND_UART = 0x92, 156 SEND_UART = 0x92,
159}; 157};
160 158
161enum class CalMagic : u8 { 159enum class CalibrationMagic : u8 {
162 USR_MAGIC_0 = 0xB2, 160 USR_MAGIC_0 = 0xB2,
163 USR_MAGIC_1 = 0xA1, 161 USR_MAGIC_1 = 0xA1,
164 USRR_MAGI_SIZE = 2,
165}; 162};
166 163
167enum class CalAddr { 164enum class SpiAddress : u16 {
168 SERIAL_NUMBER = 0X6000, 165 MAGIC = 0x0000,
169 DEVICE_TYPE = 0X6012, 166 MAC_ADDRESS = 0x0015,
170 COLOR_EXIST = 0X601B, 167 PAIRING_INFO = 0x2000,
171 FACT_LEFT_DATA = 0X603d, 168 SHIPMENT = 0x5000,
172 FACT_RIGHT_DATA = 0X6046, 169 SERIAL_NUMBER = 0x6000,
173 COLOR_DATA = 0X6050, 170 DEVICE_TYPE = 0x6012,
174 FACT_IMU_DATA = 0X6020, 171 FORMAT_VERSION = 0x601B,
175 USER_LEFT_MAGIC = 0X8010, 172 FACT_IMU_DATA = 0x6020,
176 USER_LEFT_DATA = 0X8012, 173 FACT_LEFT_DATA = 0x603d,
177 USER_RIGHT_MAGIC = 0X801B, 174 FACT_RIGHT_DATA = 0x6046,
178 USER_RIGHT_DATA = 0X801D, 175 COLOR_DATA = 0x6050,
179 USER_IMU_MAGIC = 0X8026, 176 DESIGN_VARIATION = 0x605C,
180 USER_IMU_DATA = 0X8028, 177 SENSOR_DATA = 0x6080,
178 USER_LEFT_MAGIC = 0x8010,
179 USER_LEFT_DATA = 0x8012,
180 USER_RIGHT_MAGIC = 0x801B,
181 USER_RIGHT_DATA = 0x801D,
182 USER_IMU_MAGIC = 0x8026,
183 USER_IMU_DATA = 0x8028,
181}; 184};
182 185
183enum class ReportMode : u8 { 186enum class ReportMode : u8 {
@@ -185,10 +188,12 @@ enum class ReportMode : u8 {
185 ACTIVE_POLLING_NFC_IR_CAMERA_CONFIGURATION = 0x01, 188 ACTIVE_POLLING_NFC_IR_CAMERA_CONFIGURATION = 0x01,
186 ACTIVE_POLLING_NFC_IR_CAMERA_DATA_CONFIGURATION = 0x02, 189 ACTIVE_POLLING_NFC_IR_CAMERA_DATA_CONFIGURATION = 0x02,
187 ACTIVE_POLLING_IR_CAMERA_DATA = 0x03, 190 ACTIVE_POLLING_IR_CAMERA_DATA = 0x03,
191 SUBCMD_REPLY = 0x21,
188 MCU_UPDATE_STATE = 0x23, 192 MCU_UPDATE_STATE = 0x23,
189 STANDARD_FULL_60HZ = 0x30, 193 STANDARD_FULL_60HZ = 0x30,
190 NFC_IR_MODE_60HZ = 0x31, 194 NFC_IR_MODE_60HZ = 0x31,
191 SIMPLE_HID_MODE = 0x3F, 195 SIMPLE_HID_MODE = 0x3F,
196 INPUT_USB_RESPONSE = 0x81,
192}; 197};
193 198
194enum class GyroSensitivity : u8 { 199enum class GyroSensitivity : u8 {
@@ -359,10 +364,16 @@ enum class IrRegistersAddress : u16 {
359 DenoiseColor = 0x6901, 364 DenoiseColor = 0x6901,
360}; 365};
361 366
367enum class ExternalDeviceId : u16 {
368 RingController = 0x2000,
369 Starlink = 0x2800,
370};
371
362enum class DriverResult { 372enum class DriverResult {
363 Success, 373 Success,
364 WrongReply, 374 WrongReply,
365 Timeout, 375 Timeout,
376 InvalidParameters,
366 UnsupportedControllerType, 377 UnsupportedControllerType,
367 HandleInUse, 378 HandleInUse,
368 ErrorReadingData, 379 ErrorReadingData,
@@ -395,10 +406,35 @@ struct MotionData {
395 u64 delta_timestamp{}; 406 u64 delta_timestamp{};
396}; 407};
397 408
409// Output from SPI read command containing user calibration magic
410struct MagicSpiCalibration {
411 CalibrationMagic first;
412 CalibrationMagic second;
413};
414static_assert(sizeof(MagicSpiCalibration) == 0x2, "MagicSpiCalibration is an invalid size");
415
416// Output from SPI read command containing left joystick calibration
417struct JoystickLeftSpiCalibration {
418 std::array<u8, 3> max;
419 std::array<u8, 3> center;
420 std::array<u8, 3> min;
421};
422static_assert(sizeof(JoystickLeftSpiCalibration) == 0x9,
423 "JoystickLeftSpiCalibration is an invalid size");
424
425// Output from SPI read command containing right joystick calibration
426struct JoystickRightSpiCalibration {
427 std::array<u8, 3> center;
428 std::array<u8, 3> min;
429 std::array<u8, 3> max;
430};
431static_assert(sizeof(JoystickRightSpiCalibration) == 0x9,
432 "JoystickRightSpiCalibration is an invalid size");
433
398struct JoyStickAxisCalibration { 434struct JoyStickAxisCalibration {
399 u16 max{1}; 435 u16 max;
400 u16 min{1}; 436 u16 min;
401 u16 center{0}; 437 u16 center;
402}; 438};
403 439
404struct JoyStickCalibration { 440struct JoyStickCalibration {
@@ -406,6 +442,14 @@ struct JoyStickCalibration {
406 JoyStickAxisCalibration y; 442 JoyStickAxisCalibration y;
407}; 443};
408 444
445struct ImuSpiCalibration {
446 std::array<s16, 3> accelerometer_offset;
447 std::array<s16, 3> accelerometer_scale;
448 std::array<s16, 3> gyroscope_offset;
449 std::array<s16, 3> gyroscope_scale;
450};
451static_assert(sizeof(ImuSpiCalibration) == 0x18, "ImuSpiCalibration is an invalid size");
452
409struct RingCalibration { 453struct RingCalibration {
410 s16 default_value; 454 s16 default_value;
411 s16 max_value; 455 s16 max_value;
@@ -452,7 +496,7 @@ static_assert(sizeof(MCUConfig) == 0x26, "MCUConfig is an invalid size");
452 496
453#pragma pack(push, 1) 497#pragma pack(push, 1)
454struct InputReportPassive { 498struct InputReportPassive {
455 InputReport report_mode; 499 ReportMode report_mode;
456 u16 button_input; 500 u16 button_input;
457 u8 stick_state; 501 u8 stick_state;
458 std::array<u8, 10> unknown_data; 502 std::array<u8, 10> unknown_data;
@@ -460,7 +504,7 @@ struct InputReportPassive {
460static_assert(sizeof(InputReportPassive) == 0xE, "InputReportPassive is an invalid size"); 504static_assert(sizeof(InputReportPassive) == 0xE, "InputReportPassive is an invalid size");
461 505
462struct InputReportActive { 506struct InputReportActive {
463 InputReport report_mode; 507 ReportMode report_mode;
464 u8 packet_id; 508 u8 packet_id;
465 Battery battery_status; 509 Battery battery_status;
466 std::array<u8, 3> button_input; 510 std::array<u8, 3> button_input;
@@ -474,7 +518,7 @@ struct InputReportActive {
474static_assert(sizeof(InputReportActive) == 0x29, "InputReportActive is an invalid size"); 518static_assert(sizeof(InputReportActive) == 0x29, "InputReportActive is an invalid size");
475 519
476struct InputReportNfcIr { 520struct InputReportNfcIr {
477 InputReport report_mode; 521 ReportMode report_mode;
478 u8 packet_id; 522 u8 packet_id;
479 Battery battery_status; 523 Battery battery_status;
480 std::array<u8, 3> button_input; 524 std::array<u8, 3> button_input;
@@ -487,14 +531,6 @@ struct InputReportNfcIr {
487static_assert(sizeof(InputReportNfcIr) == 0x29, "InputReportNfcIr is an invalid size"); 531static_assert(sizeof(InputReportNfcIr) == 0x29, "InputReportNfcIr is an invalid size");
488#pragma pack(pop) 532#pragma pack(pop)
489 533
490struct IMUCalibration {
491 std::array<s16, 3> accelerometer_offset;
492 std::array<s16, 3> accelerometer_scale;
493 std::array<s16, 3> gyroscope_offset;
494 std::array<s16, 3> gyroscope_scale;
495};
496static_assert(sizeof(IMUCalibration) == 0x18, "IMUCalibration is an invalid size");
497
498struct NFCReadBlock { 534struct NFCReadBlock {
499 u8 start; 535 u8 start;
500 u8 end; 536 u8 end;
@@ -580,9 +616,11 @@ static_assert(sizeof(FirmwareVersion) == 0x2, "FirmwareVersion is an invalid siz
580 616
581struct DeviceInfo { 617struct DeviceInfo {
582 FirmwareVersion firmware; 618 FirmwareVersion firmware;
619 std::array<u8, 2> unknown_1;
583 MacAddress mac_address; 620 MacAddress mac_address;
621 std::array<u8, 2> unknown_2;
584}; 622};
585static_assert(sizeof(DeviceInfo) == 0x8, "DeviceInfo is an invalid size"); 623static_assert(sizeof(DeviceInfo) == 0xC, "DeviceInfo is an invalid size");
586 624
587struct MotionStatus { 625struct MotionStatus {
588 bool is_enabled; 626 bool is_enabled;
@@ -598,6 +636,53 @@ struct RingStatus {
598 s16 min_value; 636 s16 min_value;
599}; 637};
600 638
639struct VibrationPacket {
640 OutputReport output_report;
641 u8 packet_counter;
642 std::array<u8, 0x8> vibration_data;
643};
644static_assert(sizeof(VibrationPacket) == 0xA, "VibrationPacket is an invalid size");
645
646struct SubCommandPacket {
647 OutputReport output_report;
648 u8 packet_counter;
649 INSERT_PADDING_BYTES(0x8); // This contains vibration data
650 SubCommand sub_command;
651 std::array<u8, 0x26> command_data;
652};
653static_assert(sizeof(SubCommandPacket) == 0x31, "SubCommandPacket is an invalid size");
654
655#pragma pack(push, 1)
656struct ReadSpiPacket {
657 SpiAddress spi_address;
658 INSERT_PADDING_BYTES(0x2);
659 u8 size;
660};
661static_assert(sizeof(ReadSpiPacket) == 0x5, "ReadSpiPacket is an invalid size");
662
663struct SubCommandResponse {
664 InputReportPassive input_report;
665 SubCommand sub_command;
666 union {
667 std::array<u8, 0x30> command_data;
668 SpiAddress spi_address; // Reply from SPI_FLASH_READ subcommand
669 ExternalDeviceId external_device_id; // Reply from GET_EXTERNAL_DEVICE_INFO subcommand
670 DeviceInfo device_info; // Reply from REQ_DEV_INFO subcommand
671 };
672 u8 crc; // This is never used
673};
674static_assert(sizeof(SubCommandResponse) == 0x40, "SubCommandResponse is an invalid size");
675#pragma pack(pop)
676
677struct MCUCommandResponse {
678 InputReportNfcIr input_report;
679 INSERT_PADDING_BYTES(0x8);
680 MCUReport mcu_report;
681 std::array<u8, 0x13D> mcu_data;
682 u8 crc;
683};
684static_assert(sizeof(MCUCommandResponse) == 0x170, "MCUCommandResponse is an invalid size");
685
601struct JoyconCallbacks { 686struct JoyconCallbacks {
602 std::function<void(Battery)> on_battery_data; 687 std::function<void(Battery)> on_battery_data;
603 std::function<void(Color)> on_color_data; 688 std::function<void(Color)> on_color_data;
diff --git a/src/input_common/helpers/joycon_protocol/nfc.cpp b/src/input_common/helpers/joycon_protocol/nfc.cpp
index 5c0f71722..eeba82986 100644
--- a/src/input_common/helpers/joycon_protocol/nfc.cpp
+++ b/src/input_common/helpers/joycon_protocol/nfc.cpp
@@ -110,7 +110,7 @@ bool NfcProtocol::HasAmiibo() {
110 110
111DriverResult NfcProtocol::WaitUntilNfcIsReady() { 111DriverResult NfcProtocol::WaitUntilNfcIsReady() {
112 constexpr std::size_t timeout_limit = 10; 112 constexpr std::size_t timeout_limit = 10;
113 std::vector<u8> output; 113 MCUCommandResponse output{};
114 std::size_t tries = 0; 114 std::size_t tries = 0;
115 115
116 do { 116 do {
@@ -122,8 +122,9 @@ DriverResult NfcProtocol::WaitUntilNfcIsReady() {
122 if (tries++ > timeout_limit) { 122 if (tries++ > timeout_limit) {
123 return DriverResult::Timeout; 123 return DriverResult::Timeout;
124 } 124 }
125 } while (output[49] != 0x2a || (output[51] << 8) + output[50] != 0x0500 || output[55] != 0x31 || 125 } while (output.mcu_report != MCUReport::NFCState ||
126 output[56] != 0x00); 126 (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
127 output.mcu_data[5] != 0x31 || output.mcu_data[6] != 0x00);
127 128
128 return DriverResult::Success; 129 return DriverResult::Success;
129} 130}
@@ -131,7 +132,7 @@ DriverResult NfcProtocol::WaitUntilNfcIsReady() {
131DriverResult NfcProtocol::StartPolling(TagFoundData& data) { 132DriverResult NfcProtocol::StartPolling(TagFoundData& data) {
132 LOG_DEBUG(Input, "Start Polling for tag"); 133 LOG_DEBUG(Input, "Start Polling for tag");
133 constexpr std::size_t timeout_limit = 7; 134 constexpr std::size_t timeout_limit = 7;
134 std::vector<u8> output; 135 MCUCommandResponse output{};
135 std::size_t tries = 0; 136 std::size_t tries = 0;
136 137
137 do { 138 do {
@@ -142,18 +143,20 @@ DriverResult NfcProtocol::StartPolling(TagFoundData& data) {
142 if (tries++ > timeout_limit) { 143 if (tries++ > timeout_limit) {
143 return DriverResult::Timeout; 144 return DriverResult::Timeout;
144 } 145 }
145 } while (output[49] != 0x2a || (output[51] << 8) + output[50] != 0x0500 || output[56] != 0x09); 146 } while (output.mcu_report != MCUReport::NFCState ||
147 (output.mcu_data[1] << 8) + output.mcu_data[0] != 0x0500 ||
148 output.mcu_data[6] != 0x09);
146 149
147 data.type = output[62]; 150 data.type = output.mcu_data[12];
148 data.uuid.resize(output[64]); 151 data.uuid.resize(output.mcu_data[14]);
149 memcpy(data.uuid.data(), output.data() + 65, data.uuid.size()); 152 memcpy(data.uuid.data(), output.mcu_data.data() + 15, data.uuid.size());
150 153
151 return DriverResult::Success; 154 return DriverResult::Success;
152} 155}
153 156
154DriverResult NfcProtocol::ReadTag(const TagFoundData& data) { 157DriverResult NfcProtocol::ReadTag(const TagFoundData& data) {
155 constexpr std::size_t timeout_limit = 10; 158 constexpr std::size_t timeout_limit = 10;
156 std::vector<u8> output; 159 MCUCommandResponse output{};
157 std::size_t tries = 0; 160 std::size_t tries = 0;
158 161
159 std::string uuid_string; 162 std::string uuid_string;
@@ -168,23 +171,24 @@ DriverResult NfcProtocol::ReadTag(const TagFoundData& data) {
168 // Read Tag data 171 // Read Tag data
169 while (true) { 172 while (true) {
170 auto result = SendReadAmiiboRequest(output, ntag_pages); 173 auto result = SendReadAmiiboRequest(output, ntag_pages);
171 const auto mcu_report = static_cast<MCUReport>(output[49]); 174 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
172 const auto nfc_status = static_cast<NFCStatus>(output[56]);
173 175
174 if (result != DriverResult::Success) { 176 if (result != DriverResult::Success) {
175 return result; 177 return result;
176 } 178 }
177 179
178 if ((mcu_report == MCUReport::NFCReadData || mcu_report == MCUReport::NFCState) && 180 if ((output.mcu_report == MCUReport::NFCReadData ||
181 output.mcu_report == MCUReport::NFCState) &&
179 nfc_status == NFCStatus::TagLost) { 182 nfc_status == NFCStatus::TagLost) {
180 return DriverResult::ErrorReadingData; 183 return DriverResult::ErrorReadingData;
181 } 184 }
182 185
183 if (mcu_report == MCUReport::NFCReadData && output[51] == 0x07 && output[52] == 0x01) { 186 if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07 &&
187 output.mcu_data[2] == 0x01) {
184 if (data.type != 2) { 188 if (data.type != 2) {
185 continue; 189 continue;
186 } 190 }
187 switch (output[74]) { 191 switch (output.mcu_data[24]) {
188 case 0: 192 case 0:
189 ntag_pages = NFCPages::Block135; 193 ntag_pages = NFCPages::Block135;
190 break; 194 break;
@@ -200,14 +204,14 @@ DriverResult NfcProtocol::ReadTag(const TagFoundData& data) {
200 continue; 204 continue;
201 } 205 }
202 206
203 if (mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) { 207 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
204 // finished 208 // finished
205 SendStopPollingRequest(output); 209 SendStopPollingRequest(output);
206 return DriverResult::Success; 210 return DriverResult::Success;
207 } 211 }
208 212
209 // Ignore other state reports 213 // Ignore other state reports
210 if (mcu_report == MCUReport::NFCState) { 214 if (output.mcu_report == MCUReport::NFCState) {
211 continue; 215 continue;
212 } 216 }
213 217
@@ -221,7 +225,7 @@ DriverResult NfcProtocol::ReadTag(const TagFoundData& data) {
221 225
222DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) { 226DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
223 constexpr std::size_t timeout_limit = 10; 227 constexpr std::size_t timeout_limit = 10;
224 std::vector<u8> output; 228 MCUCommandResponse output{};
225 std::size_t tries = 0; 229 std::size_t tries = 0;
226 230
227 NFCPages ntag_pages = NFCPages::Block135; 231 NFCPages ntag_pages = NFCPages::Block135;
@@ -229,36 +233,38 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
229 // Read Tag data 233 // Read Tag data
230 while (true) { 234 while (true) {
231 auto result = SendReadAmiiboRequest(output, ntag_pages); 235 auto result = SendReadAmiiboRequest(output, ntag_pages);
232 const auto mcu_report = static_cast<MCUReport>(output[49]); 236 const auto nfc_status = static_cast<NFCStatus>(output.mcu_data[6]);
233 const auto nfc_status = static_cast<NFCStatus>(output[56]);
234 237
235 if (result != DriverResult::Success) { 238 if (result != DriverResult::Success) {
236 return result; 239 return result;
237 } 240 }
238 241
239 if ((mcu_report == MCUReport::NFCReadData || mcu_report == MCUReport::NFCState) && 242 if ((output.mcu_report == MCUReport::NFCReadData ||
243 output.mcu_report == MCUReport::NFCState) &&
240 nfc_status == NFCStatus::TagLost) { 244 nfc_status == NFCStatus::TagLost) {
241 return DriverResult::ErrorReadingData; 245 return DriverResult::ErrorReadingData;
242 } 246 }
243 247
244 if (mcu_report == MCUReport::NFCReadData && output[51] == 0x07) { 248 if (output.mcu_report == MCUReport::NFCReadData && output.mcu_data[1] == 0x07) {
245 std::size_t payload_size = (output[54] << 8 | output[55]) & 0x7FF; 249 std::size_t payload_size = (output.mcu_data[4] << 8 | output.mcu_data[5]) & 0x7FF;
246 if (output[52] == 0x01) { 250 if (output.mcu_data[2] == 0x01) {
247 memcpy(ntag_data.data() + ntag_buffer_pos, output.data() + 116, payload_size - 60); 251 memcpy(ntag_data.data() + ntag_buffer_pos, output.mcu_data.data() + 66,
252 payload_size - 60);
248 ntag_buffer_pos += payload_size - 60; 253 ntag_buffer_pos += payload_size - 60;
249 } else { 254 } else {
250 memcpy(ntag_data.data() + ntag_buffer_pos, output.data() + 56, payload_size); 255 memcpy(ntag_data.data() + ntag_buffer_pos, output.mcu_data.data() + 6,
256 payload_size);
251 } 257 }
252 continue; 258 continue;
253 } 259 }
254 260
255 if (mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) { 261 if (output.mcu_report == MCUReport::NFCState && nfc_status == NFCStatus::LastPackage) {
256 LOG_INFO(Input, "Finished reading amiibo"); 262 LOG_INFO(Input, "Finished reading amiibo");
257 return DriverResult::Success; 263 return DriverResult::Success;
258 } 264 }
259 265
260 // Ignore other state reports 266 // Ignore other state reports
261 if (mcu_report == MCUReport::NFCState) { 267 if (output.mcu_report == MCUReport::NFCState) {
262 continue; 268 continue;
263 } 269 }
264 270
@@ -270,7 +276,7 @@ DriverResult NfcProtocol::GetAmiiboData(std::vector<u8>& ntag_data) {
270 return DriverResult::Success; 276 return DriverResult::Success;
271} 277}
272 278
273DriverResult NfcProtocol::SendStartPollingRequest(std::vector<u8>& output) { 279DriverResult NfcProtocol::SendStartPollingRequest(MCUCommandResponse& output) {
274 NFCRequestState request{ 280 NFCRequestState request{
275 .sub_command = MCUSubCommand::ReadDeviceMode, 281 .sub_command = MCUSubCommand::ReadDeviceMode,
276 .command_argument = NFCReadCommand::StartPolling, 282 .command_argument = NFCReadCommand::StartPolling,
@@ -294,7 +300,7 @@ DriverResult NfcProtocol::SendStartPollingRequest(std::vector<u8>& output) {
294 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); 300 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
295} 301}
296 302
297DriverResult NfcProtocol::SendStopPollingRequest(std::vector<u8>& output) { 303DriverResult NfcProtocol::SendStopPollingRequest(MCUCommandResponse& output) {
298 NFCRequestState request{ 304 NFCRequestState request{
299 .sub_command = MCUSubCommand::ReadDeviceMode, 305 .sub_command = MCUSubCommand::ReadDeviceMode,
300 .command_argument = NFCReadCommand::StopPolling, 306 .command_argument = NFCReadCommand::StopPolling,
@@ -311,7 +317,7 @@ DriverResult NfcProtocol::SendStopPollingRequest(std::vector<u8>& output) {
311 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); 317 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
312} 318}
313 319
314DriverResult NfcProtocol::SendStartWaitingRecieveRequest(std::vector<u8>& output) { 320DriverResult NfcProtocol::SendStartWaitingRecieveRequest(MCUCommandResponse& output) {
315 NFCRequestState request{ 321 NFCRequestState request{
316 .sub_command = MCUSubCommand::ReadDeviceMode, 322 .sub_command = MCUSubCommand::ReadDeviceMode,
317 .command_argument = NFCReadCommand::StartWaitingRecieve, 323 .command_argument = NFCReadCommand::StartWaitingRecieve,
@@ -328,7 +334,7 @@ DriverResult NfcProtocol::SendStartWaitingRecieveRequest(std::vector<u8>& output
328 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output); 334 return SendMCUData(ReportMode::NFC_IR_MODE_60HZ, SubCommand::STATE, request_data, output);
329} 335}
330 336
331DriverResult NfcProtocol::SendReadAmiiboRequest(std::vector<u8>& output, NFCPages ntag_pages) { 337DriverResult NfcProtocol::SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages) {
332 NFCRequestState request{ 338 NFCRequestState request{
333 .sub_command = MCUSubCommand::ReadDeviceMode, 339 .sub_command = MCUSubCommand::ReadDeviceMode,
334 .command_argument = NFCReadCommand::Ntag, 340 .command_argument = NFCReadCommand::Ntag,
diff --git a/src/input_common/helpers/joycon_protocol/nfc.h b/src/input_common/helpers/joycon_protocol/nfc.h
index e63665aa9..11e263e07 100644
--- a/src/input_common/helpers/joycon_protocol/nfc.h
+++ b/src/input_common/helpers/joycon_protocol/nfc.h
@@ -45,13 +45,13 @@ private:
45 45
46 DriverResult GetAmiiboData(std::vector<u8>& data); 46 DriverResult GetAmiiboData(std::vector<u8>& data);
47 47
48 DriverResult SendStartPollingRequest(std::vector<u8>& output); 48 DriverResult SendStartPollingRequest(MCUCommandResponse& output);
49 49
50 DriverResult SendStopPollingRequest(std::vector<u8>& output); 50 DriverResult SendStopPollingRequest(MCUCommandResponse& output);
51 51
52 DriverResult SendStartWaitingRecieveRequest(std::vector<u8>& output); 52 DriverResult SendStartWaitingRecieveRequest(MCUCommandResponse& output);
53 53
54 DriverResult SendReadAmiiboRequest(std::vector<u8>& output, NFCPages ntag_pages); 54 DriverResult SendReadAmiiboRequest(MCUCommandResponse& output, NFCPages ntag_pages);
55 55
56 NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const; 56 NFCReadBlockCommand GetReadBlockCommand(NFCPages pages) const;
57 57
diff --git a/src/input_common/helpers/joycon_protocol/poller.cpp b/src/input_common/helpers/joycon_protocol/poller.cpp
index 7f8e093fa..9bb15e935 100644
--- a/src/input_common/helpers/joycon_protocol/poller.cpp
+++ b/src/input_common/helpers/joycon_protocol/poller.cpp
@@ -31,9 +31,7 @@ void JoyconPoller::ReadActiveMode(std::span<u8> buffer, const MotionStatus& moti
31 case Joycon::ControllerType::Pro: 31 case Joycon::ControllerType::Pro:
32 UpdateActiveProPadInput(data, motion_status); 32 UpdateActiveProPadInput(data, motion_status);
33 break; 33 break;
34 case Joycon::ControllerType::Grip: 34 default:
35 case Joycon::ControllerType::Dual:
36 case Joycon::ControllerType::None:
37 break; 35 break;
38 } 36 }
39 37
@@ -58,9 +56,7 @@ void JoyconPoller::ReadPassiveMode(std::span<u8> buffer) {
58 case Joycon::ControllerType::Pro: 56 case Joycon::ControllerType::Pro:
59 UpdatePasiveProPadInput(data); 57 UpdatePasiveProPadInput(data);
60 break; 58 break;
61 case Joycon::ControllerType::Grip: 59 default:
62 case Joycon::ControllerType::Dual:
63 case Joycon::ControllerType::None:
64 break; 60 break;
65 } 61 }
66} 62}
diff --git a/src/input_common/helpers/joycon_protocol/ringcon.cpp b/src/input_common/helpers/joycon_protocol/ringcon.cpp
index 12f81309e..190cef812 100644
--- a/src/input_common/helpers/joycon_protocol/ringcon.cpp
+++ b/src/input_common/helpers/joycon_protocol/ringcon.cpp
@@ -70,14 +70,12 @@ DriverResult RingConProtocol::StartRingconPolling() {
70DriverResult RingConProtocol::IsRingConnected(bool& is_connected) { 70DriverResult RingConProtocol::IsRingConnected(bool& is_connected) {
71 LOG_DEBUG(Input, "IsRingConnected"); 71 LOG_DEBUG(Input, "IsRingConnected");
72 constexpr std::size_t max_tries = 28; 72 constexpr std::size_t max_tries = 28;
73 constexpr u8 ring_controller_id = 0x20; 73 SubCommandResponse output{};
74 std::vector<u8> output;
75 std::size_t tries = 0; 74 std::size_t tries = 0;
76 is_connected = false; 75 is_connected = false;
77 76
78 do { 77 do {
79 std::array<u8, 1> empty_data{}; 78 const auto result = SendSubCommand(SubCommand::GET_EXTERNAL_DEVICE_INFO, {}, output);
80 const auto result = SendSubCommand(SubCommand::UNKNOWN_RINGCON, empty_data, output);
81 79
82 if (result != DriverResult::Success) { 80 if (result != DriverResult::Success) {
83 return result; 81 return result;
@@ -86,7 +84,7 @@ DriverResult RingConProtocol::IsRingConnected(bool& is_connected) {
86 if (tries++ >= max_tries) { 84 if (tries++ >= max_tries) {
87 return DriverResult::NoDeviceDetected; 85 return DriverResult::NoDeviceDetected;
88 } 86 }
89 } while (output[16] != ring_controller_id); 87 } while (output.external_device_id != ExternalDeviceId::RingController);
90 88
91 is_connected = true; 89 is_connected = true;
92 return DriverResult::Success; 90 return DriverResult::Success;
@@ -100,14 +98,14 @@ DriverResult RingConProtocol::ConfigureRing() {
100 0x00, 0x00, 0x00, 0x0A, 0x64, 0x0B, 0xE6, 0xA9, 0x22, 0x00, 0x00, 0x04, 0x00, 98 0x00, 0x00, 0x00, 0x0A, 0x64, 0x0B, 0xE6, 0xA9, 0x22, 0x00, 0x00, 0x04, 0x00,
101 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xA8, 0xE1, 0x34, 0x36}; 99 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0xA8, 0xE1, 0x34, 0x36};
102 100
103 const DriverResult result = SendSubCommand(SubCommand::UNKNOWN_RINGCON3, ring_config); 101 const DriverResult result = SendSubCommand(SubCommand::SET_EXTERNAL_FORMAT_CONFIG, ring_config);
104 102
105 if (result != DriverResult::Success) { 103 if (result != DriverResult::Success) {
106 return result; 104 return result;
107 } 105 }
108 106
109 static constexpr std::array<u8, 4> ringcon_data{0x04, 0x01, 0x01, 0x02}; 107 static constexpr std::array<u8, 4> ringcon_data{0x04, 0x01, 0x01, 0x02};
110 return SendSubCommand(SubCommand::UNKNOWN_RINGCON2, ringcon_data); 108 return SendSubCommand(SubCommand::ENABLE_EXTERNAL_POLLING, ringcon_data);
111} 109}
112 110
113bool RingConProtocol::IsEnabled() const { 111bool RingConProtocol::IsEnabled() const {
diff --git a/src/input_common/helpers/stick_from_buttons.cpp b/src/input_common/helpers/stick_from_buttons.cpp
index 096c23b07..a6be6dac1 100644
--- a/src/input_common/helpers/stick_from_buttons.cpp
+++ b/src/input_common/helpers/stick_from_buttons.cpp
@@ -15,6 +15,9 @@ public:
15 // do not play nicely with the theoretical maximum range. 15 // do not play nicely with the theoretical maximum range.
16 // Using a value one lower from the maximum emulates real stick behavior. 16 // Using a value one lower from the maximum emulates real stick behavior.
17 static constexpr float MAX_RANGE = 32766.0f / 32767.0f; 17 static constexpr float MAX_RANGE = 32766.0f / 32767.0f;
18 static constexpr float TAU = Common::PI * 2.0f;
19 // Use wider angle to ease the transition.
20 static constexpr float APERTURE = TAU * 0.15f;
18 21
19 using Button = std::unique_ptr<Common::Input::InputDevice>; 22 using Button = std::unique_ptr<Common::Input::InputDevice>;
20 23
@@ -61,30 +64,23 @@ public:
61 } 64 }
62 65
63 bool IsAngleGreater(float old_angle, float new_angle) const { 66 bool IsAngleGreater(float old_angle, float new_angle) const {
64 constexpr float TAU = Common::PI * 2.0f; 67 const float top_limit = new_angle + APERTURE;
65 // Use wider angle to ease the transition.
66 constexpr float aperture = TAU * 0.15f;
67 const float top_limit = new_angle + aperture;
68 return (old_angle > new_angle && old_angle <= top_limit) || 68 return (old_angle > new_angle && old_angle <= top_limit) ||
69 (old_angle + TAU > new_angle && old_angle + TAU <= top_limit); 69 (old_angle + TAU > new_angle && old_angle + TAU <= top_limit);
70 } 70 }
71 71
72 bool IsAngleSmaller(float old_angle, float new_angle) const { 72 bool IsAngleSmaller(float old_angle, float new_angle) const {
73 constexpr float TAU = Common::PI * 2.0f; 73 const float bottom_limit = new_angle - APERTURE;
74 // Use wider angle to ease the transition.
75 constexpr float aperture = TAU * 0.15f;
76 const float bottom_limit = new_angle - aperture;
77 return (old_angle >= bottom_limit && old_angle < new_angle) || 74 return (old_angle >= bottom_limit && old_angle < new_angle) ||
78 (old_angle - TAU >= bottom_limit && old_angle - TAU < new_angle); 75 (old_angle - TAU >= bottom_limit && old_angle - TAU < new_angle);
79 } 76 }
80 77
81 float GetAngle(std::chrono::time_point<std::chrono::steady_clock> now) const { 78 float GetAngle(std::chrono::time_point<std::chrono::steady_clock> now) const {
82 constexpr float TAU = Common::PI * 2.0f;
83 float new_angle = angle; 79 float new_angle = angle;
84 80
85 auto time_difference = static_cast<float>( 81 auto time_difference = static_cast<float>(
86 std::chrono::duration_cast<std::chrono::microseconds>(now - last_update).count()); 82 std::chrono::duration_cast<std::chrono::milliseconds>(now - last_update).count());
87 time_difference /= 1000.0f * 1000.0f; 83 time_difference /= 1000.0f;
88 if (time_difference > 0.5f) { 84 if (time_difference > 0.5f) {
89 time_difference = 0.5f; 85 time_difference = 0.5f;
90 } 86 }
@@ -201,8 +197,6 @@ public:
201 } 197 }
202 198
203 void UpdateStatus() { 199 void UpdateStatus() {
204 const float coef = modifier_status.value ? modifier_scale : MAX_RANGE;
205
206 bool r = right_status; 200 bool r = right_status;
207 bool l = left_status; 201 bool l = left_status;
208 bool u = up_status; 202 bool u = up_status;
@@ -220,7 +214,7 @@ public:
220 214
221 // Move if a key is pressed 215 // Move if a key is pressed
222 if (r || l || u || d) { 216 if (r || l || u || d) {
223 amplitude = coef; 217 amplitude = modifier_status.value ? modifier_scale : MAX_RANGE;
224 } else { 218 } else {
225 amplitude = 0; 219 amplitude = 0;
226 } 220 }
@@ -274,30 +268,17 @@ public:
274 Common::Input::StickStatus status{}; 268 Common::Input::StickStatus status{};
275 status.x.properties = properties; 269 status.x.properties = properties;
276 status.y.properties = properties; 270 status.y.properties = properties;
271
277 if (Settings::values.emulate_analog_keyboard) { 272 if (Settings::values.emulate_analog_keyboard) {
278 const auto now = std::chrono::steady_clock::now(); 273 const auto now = std::chrono::steady_clock::now();
279 float angle_ = GetAngle(now); 274 const float angle_ = GetAngle(now);
280 status.x.raw_value = std::cos(angle_) * amplitude; 275 status.x.raw_value = std::cos(angle_) * amplitude;
281 status.y.raw_value = std::sin(angle_) * amplitude; 276 status.y.raw_value = std::sin(angle_) * amplitude;
282 return status; 277 return status;
283 } 278 }
284 constexpr float SQRT_HALF = 0.707106781f; 279
285 int x = 0, y = 0; 280 status.x.raw_value = std::cos(goal_angle) * amplitude;
286 if (right_status) { 281 status.y.raw_value = std::sin(goal_angle) * amplitude;
287 ++x;
288 }
289 if (left_status) {
290 --x;
291 }
292 if (up_status) {
293 ++y;
294 }
295 if (down_status) {
296 --y;
297 }
298 const float coef = modifier_status.value ? modifier_scale : MAX_RANGE;
299 status.x.raw_value = static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF);
300 status.y.raw_value = static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF);
301 return status; 282 return status;
302 } 283 }
303 284
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp
index 15cbf7e5f..8c6a6521a 100644
--- a/src/input_common/input_poller.cpp
+++ b/src/input_common/input_poller.cpp
@@ -16,10 +16,10 @@ public:
16 16
17class InputFromButton final : public Common::Input::InputDevice { 17class InputFromButton final : public Common::Input::InputDevice {
18public: 18public:
19 explicit InputFromButton(PadIdentifier identifier_, int button_, bool toggle_, bool inverted_, 19 explicit InputFromButton(PadIdentifier identifier_, int button_, bool turbo_, bool toggle_,
20 InputEngine* input_engine_) 20 bool inverted_, InputEngine* input_engine_)
21 : identifier(identifier_), button(button_), toggle(toggle_), inverted(inverted_), 21 : identifier(identifier_), button(button_), turbo(turbo_), toggle(toggle_),
22 input_engine(input_engine_) { 22 inverted(inverted_), input_engine(input_engine_) {
23 UpdateCallback engine_callback{[this]() { OnChange(); }}; 23 UpdateCallback engine_callback{[this]() { OnChange(); }};
24 const InputIdentifier input_identifier{ 24 const InputIdentifier input_identifier{
25 .identifier = identifier, 25 .identifier = identifier,
@@ -40,6 +40,7 @@ public:
40 .value = input_engine->GetButton(identifier, button), 40 .value = input_engine->GetButton(identifier, button),
41 .inverted = inverted, 41 .inverted = inverted,
42 .toggle = toggle, 42 .toggle = toggle,
43 .turbo = turbo,
43 }; 44 };
44 } 45 }
45 46
@@ -68,6 +69,7 @@ public:
68private: 69private:
69 const PadIdentifier identifier; 70 const PadIdentifier identifier;
70 const int button; 71 const int button;
72 const bool turbo;
71 const bool toggle; 73 const bool toggle;
72 const bool inverted; 74 const bool inverted;
73 int callback_key; 75 int callback_key;
@@ -77,10 +79,10 @@ private:
77 79
78class InputFromHatButton final : public Common::Input::InputDevice { 80class InputFromHatButton final : public Common::Input::InputDevice {
79public: 81public:
80 explicit InputFromHatButton(PadIdentifier identifier_, int button_, u8 direction_, bool toggle_, 82 explicit InputFromHatButton(PadIdentifier identifier_, int button_, u8 direction_, bool turbo_,
81 bool inverted_, InputEngine* input_engine_) 83 bool toggle_, bool inverted_, InputEngine* input_engine_)
82 : identifier(identifier_), button(button_), direction(direction_), toggle(toggle_), 84 : identifier(identifier_), button(button_), direction(direction_), turbo(turbo_),
83 inverted(inverted_), input_engine(input_engine_) { 85 toggle(toggle_), inverted(inverted_), input_engine(input_engine_) {
84 UpdateCallback engine_callback{[this]() { OnChange(); }}; 86 UpdateCallback engine_callback{[this]() { OnChange(); }};
85 const InputIdentifier input_identifier{ 87 const InputIdentifier input_identifier{
86 .identifier = identifier, 88 .identifier = identifier,
@@ -101,6 +103,7 @@ public:
101 .value = input_engine->GetHatButton(identifier, button, direction), 103 .value = input_engine->GetHatButton(identifier, button, direction),
102 .inverted = inverted, 104 .inverted = inverted,
103 .toggle = toggle, 105 .toggle = toggle,
106 .turbo = turbo,
104 }; 107 };
105 } 108 }
106 109
@@ -130,6 +133,7 @@ private:
130 const PadIdentifier identifier; 133 const PadIdentifier identifier;
131 const int button; 134 const int button;
132 const u8 direction; 135 const u8 direction;
136 const bool turbo;
133 const bool toggle; 137 const bool toggle;
134 const bool inverted; 138 const bool inverted;
135 int callback_key; 139 int callback_key;
@@ -853,14 +857,15 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateButtonDevice(
853 const auto keyboard_key = params.Get("code", 0); 857 const auto keyboard_key = params.Get("code", 0);
854 const auto toggle = params.Get("toggle", false) != 0; 858 const auto toggle = params.Get("toggle", false) != 0;
855 const auto inverted = params.Get("inverted", false) != 0; 859 const auto inverted = params.Get("inverted", false) != 0;
860 const auto turbo = params.Get("turbo", false) != 0;
856 input_engine->PreSetController(identifier); 861 input_engine->PreSetController(identifier);
857 input_engine->PreSetButton(identifier, button_id); 862 input_engine->PreSetButton(identifier, button_id);
858 input_engine->PreSetButton(identifier, keyboard_key); 863 input_engine->PreSetButton(identifier, keyboard_key);
859 if (keyboard_key != 0) { 864 if (keyboard_key != 0) {
860 return std::make_unique<InputFromButton>(identifier, keyboard_key, toggle, inverted, 865 return std::make_unique<InputFromButton>(identifier, keyboard_key, turbo, toggle, inverted,
861 input_engine.get()); 866 input_engine.get());
862 } 867 }
863 return std::make_unique<InputFromButton>(identifier, button_id, toggle, inverted, 868 return std::make_unique<InputFromButton>(identifier, button_id, turbo, toggle, inverted,
864 input_engine.get()); 869 input_engine.get());
865} 870}
866 871
@@ -876,11 +881,12 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateHatButtonDevice(
876 const auto direction = input_engine->GetHatButtonId(params.Get("direction", "")); 881 const auto direction = input_engine->GetHatButtonId(params.Get("direction", ""));
877 const auto toggle = params.Get("toggle", false) != 0; 882 const auto toggle = params.Get("toggle", false) != 0;
878 const auto inverted = params.Get("inverted", false) != 0; 883 const auto inverted = params.Get("inverted", false) != 0;
884 const auto turbo = params.Get("turbo", false) != 0;
879 885
880 input_engine->PreSetController(identifier); 886 input_engine->PreSetController(identifier);
881 input_engine->PreSetHatButton(identifier, button_id); 887 input_engine->PreSetHatButton(identifier, button_id);
882 return std::make_unique<InputFromHatButton>(identifier, button_id, direction, toggle, inverted, 888 return std::make_unique<InputFromHatButton>(identifier, button_id, direction, turbo, toggle,
883 input_engine.get()); 889 inverted, input_engine.get());
884} 890}
885 891
886std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateStickDevice( 892std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateStickDevice(