summaryrefslogtreecommitdiff
path: root/src/input_common/helpers/joycon_protocol/irs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/input_common/helpers/joycon_protocol/irs.cpp')
-rw-r--r--src/input_common/helpers/joycon_protocol/irs.cpp298
1 files changed, 298 insertions, 0 deletions
diff --git a/src/input_common/helpers/joycon_protocol/irs.cpp b/src/input_common/helpers/joycon_protocol/irs.cpp
new file mode 100644
index 000000000..09e17bc5b
--- /dev/null
+++ b/src/input_common/helpers/joycon_protocol/irs.cpp
@@ -0,0 +1,298 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <thread>
5#include "common/logging/log.h"
6#include "input_common/helpers/joycon_protocol/irs.h"
7
8namespace InputCommon::Joycon {
9
10IrsProtocol::IrsProtocol(std::shared_ptr<JoyconHandle> handle)
11 : JoyconCommonProtocol(std::move(handle)) {}
12
13DriverResult IrsProtocol::EnableIrs() {
14 LOG_INFO(Input, "Enable IRS");
15 ScopedSetBlocking sb(this);
16 DriverResult result{DriverResult::Success};
17
18 if (result == DriverResult::Success) {
19 result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ);
20 }
21 if (result == DriverResult::Success) {
22 result = EnableMCU(true);
23 }
24 if (result == DriverResult::Success) {
25 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::Standby);
26 }
27 if (result == DriverResult::Success) {
28 const MCUConfig config{
29 .command = MCUCommand::ConfigureMCU,
30 .sub_command = MCUSubCommand::SetMCUMode,
31 .mode = MCUMode::IR,
32 .crc = {},
33 };
34
35 result = ConfigureMCU(config);
36 }
37 if (result == DriverResult::Success) {
38 result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::IR);
39 }
40 if (result == DriverResult::Success) {
41 result = ConfigureIrs();
42 }
43 if (result == DriverResult::Success) {
44 result = WriteRegistersStep1();
45 }
46 if (result == DriverResult::Success) {
47 result = WriteRegistersStep2();
48 }
49
50 is_enabled = true;
51
52 return result;
53}
54
55DriverResult IrsProtocol::DisableIrs() {
56 LOG_DEBUG(Input, "Disable IRS");
57 ScopedSetBlocking sb(this);
58 DriverResult result{DriverResult::Success};
59
60 if (result == DriverResult::Success) {
61 result = EnableMCU(false);
62 }
63
64 is_enabled = false;
65
66 return result;
67}
68
69DriverResult IrsProtocol::SetIrsConfig(IrsMode mode, IrsResolution format) {
70 irs_mode = mode;
71 switch (format) {
72 case IrsResolution::Size320x240:
73 resolution_code = IrsResolutionCode::Size320x240;
74 fragments = IrsFragments::Size320x240;
75 resolution = IrsResolution::Size320x240;
76 break;
77 case IrsResolution::Size160x120:
78 resolution_code = IrsResolutionCode::Size160x120;
79 fragments = IrsFragments::Size160x120;
80 resolution = IrsResolution::Size160x120;
81 break;
82 case IrsResolution::Size80x60:
83 resolution_code = IrsResolutionCode::Size80x60;
84 fragments = IrsFragments::Size80x60;
85 resolution = IrsResolution::Size80x60;
86 break;
87 case IrsResolution::Size20x15:
88 resolution_code = IrsResolutionCode::Size20x15;
89 fragments = IrsFragments::Size20x15;
90 resolution = IrsResolution::Size20x15;
91 break;
92 case IrsResolution::Size40x30:
93 default:
94 resolution_code = IrsResolutionCode::Size40x30;
95 fragments = IrsFragments::Size40x30;
96 resolution = IrsResolution::Size40x30;
97 break;
98 }
99
100 // Restart feature
101 if (is_enabled) {
102 DisableIrs();
103 return EnableIrs();
104 }
105
106 return DriverResult::Success;
107}
108
109DriverResult IrsProtocol::RequestImage(std::span<u8> buffer) {
110 const u8 next_packet_fragment =
111 static_cast<u8>((packet_fragment + 1) % (static_cast<u8>(fragments) + 1));
112
113 if (buffer[0] == 0x31 && buffer[49] == 0x03) {
114 u8 new_packet_fragment = buffer[52];
115 if (new_packet_fragment == next_packet_fragment) {
116 packet_fragment = next_packet_fragment;
117 memcpy(buf_image.data() + (300 * packet_fragment), buffer.data() + 59, 300);
118
119 return RequestFrame(packet_fragment);
120 }
121
122 if (new_packet_fragment == packet_fragment) {
123 return RequestFrame(packet_fragment);
124 }
125
126 return ResendFrame(next_packet_fragment);
127 }
128
129 return RequestFrame(packet_fragment);
130}
131
132DriverResult IrsProtocol::ConfigureIrs() {
133 LOG_DEBUG(Input, "Configure IRS");
134 constexpr std::size_t max_tries = 28;
135 std::vector<u8> output;
136 std::size_t tries = 0;
137
138 const IrsConfigure irs_configuration{
139 .command = MCUCommand::ConfigureIR,
140 .sub_command = MCUSubCommand::SetDeviceMode,
141 .irs_mode = IrsMode::ImageTransfer,
142 .number_of_fragments = fragments,
143 .mcu_major_version = 0x0500,
144 .mcu_minor_version = 0x1800,
145 .crc = {},
146 };
147 buf_image.resize((static_cast<u8>(fragments) + 1) * 300);
148
149 std::array<u8, sizeof(IrsConfigure)> request_data{};
150 memcpy(request_data.data(), &irs_configuration, sizeof(IrsConfigure));
151 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
152 do {
153 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output);
154
155 if (result != DriverResult::Success) {
156 return result;
157 }
158 if (tries++ >= max_tries) {
159 return DriverResult::WrongReply;
160 }
161 } while (output[15] != 0x0b);
162
163 return DriverResult::Success;
164}
165
166DriverResult IrsProtocol::WriteRegistersStep1() {
167 LOG_DEBUG(Input, "WriteRegistersStep1");
168 DriverResult result{DriverResult::Success};
169 constexpr std::size_t max_tries = 28;
170 std::vector<u8> output;
171 std::size_t tries = 0;
172
173 const IrsWriteRegisters irs_registers{
174 .command = MCUCommand::ConfigureIR,
175 .sub_command = MCUSubCommand::WriteDeviceRegisters,
176 .number_of_registers = 0x9,
177 .registers =
178 {
179 IrsRegister{IrRegistersAddress::Resolution, static_cast<u8>(resolution_code)},
180 {IrRegistersAddress::ExposureLSB, static_cast<u8>(exposure & 0xff)},
181 {IrRegistersAddress::ExposureMSB, static_cast<u8>(exposure >> 8)},
182 {IrRegistersAddress::ExposureTime, 0x00},
183 {IrRegistersAddress::Leds, static_cast<u8>(leds)},
184 {IrRegistersAddress::DigitalGainLSB, static_cast<u8>((digital_gain & 0x0f) << 4)},
185 {IrRegistersAddress::DigitalGainMSB, static_cast<u8>((digital_gain & 0xf0) >> 4)},
186 {IrRegistersAddress::LedFilter, static_cast<u8>(led_filter)},
187 {IrRegistersAddress::WhitePixelThreshold, 0xc8},
188 },
189 .crc = {},
190 };
191
192 std::array<u8, sizeof(IrsWriteRegisters)> request_data{};
193 memcpy(request_data.data(), &irs_registers, sizeof(IrsWriteRegisters));
194 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
195
196 std::array<u8, 38> mcu_request{0x02};
197 mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36);
198 mcu_request[37] = 0xFF;
199
200 if (result != DriverResult::Success) {
201 return result;
202 }
203
204 do {
205 result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output);
206
207 // First time we need to set the report mode
208 if (result == DriverResult::Success && tries == 0) {
209 result = SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request);
210 }
211 if (result == DriverResult::Success && tries == 0) {
212 GetSubCommandResponse(SubCommand::SET_MCU_CONFIG, output);
213 }
214
215 if (result != DriverResult::Success) {
216 return result;
217 }
218 if (tries++ >= max_tries) {
219 return DriverResult::WrongReply;
220 }
221 } while (!(output[15] == 0x13 && output[17] == 0x07) && output[15] != 0x23);
222
223 return DriverResult::Success;
224}
225
226DriverResult IrsProtocol::WriteRegistersStep2() {
227 LOG_DEBUG(Input, "WriteRegistersStep2");
228 constexpr std::size_t max_tries = 28;
229 std::vector<u8> output;
230 std::size_t tries = 0;
231
232 const IrsWriteRegisters irs_registers{
233 .command = MCUCommand::ConfigureIR,
234 .sub_command = MCUSubCommand::WriteDeviceRegisters,
235 .number_of_registers = 0x8,
236 .registers =
237 {
238 IrsRegister{IrRegistersAddress::LedIntensitiyMSB,
239 static_cast<u8>(led_intensity >> 8)},
240 {IrRegistersAddress::LedIntensitiyLSB, static_cast<u8>(led_intensity & 0xff)},
241 {IrRegistersAddress::ImageFlip, static_cast<u8>(image_flip)},
242 {IrRegistersAddress::DenoiseSmoothing, static_cast<u8>((denoise >> 16) & 0xff)},
243 {IrRegistersAddress::DenoiseEdge, static_cast<u8>((denoise >> 8) & 0xff)},
244 {IrRegistersAddress::DenoiseColor, static_cast<u8>(denoise & 0xff)},
245 {IrRegistersAddress::UpdateTime, 0x2d},
246 {IrRegistersAddress::FinalizeConfig, 0x01},
247 },
248 .crc = {},
249 };
250
251 std::array<u8, sizeof(IrsWriteRegisters)> request_data{};
252 memcpy(request_data.data(), &irs_registers, sizeof(IrsWriteRegisters));
253 request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36);
254 do {
255 const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output);
256
257 if (result != DriverResult::Success) {
258 return result;
259 }
260 if (tries++ >= max_tries) {
261 return DriverResult::WrongReply;
262 }
263 } while (output[15] != 0x13 && output[15] != 0x23);
264
265 return DriverResult::Success;
266}
267
268DriverResult IrsProtocol::RequestFrame(u8 frame) {
269 std::array<u8, 38> mcu_request{};
270 mcu_request[3] = frame;
271 mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36);
272 mcu_request[37] = 0xFF;
273 return SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request);
274}
275
276DriverResult IrsProtocol::ResendFrame(u8 frame) {
277 std::array<u8, 38> mcu_request{};
278 mcu_request[1] = 0x1;
279 mcu_request[2] = frame;
280 mcu_request[3] = 0x0;
281 mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36);
282 mcu_request[37] = 0xFF;
283 return SendMCUCommand(SubCommand::SET_REPORT_MODE, mcu_request);
284}
285
286std::vector<u8> IrsProtocol::GetImage() const {
287 return buf_image;
288}
289
290IrsResolution IrsProtocol::GetIrsFormat() const {
291 return resolution;
292}
293
294bool IrsProtocol::IsEnabled() const {
295 return is_enabled;
296}
297
298} // namespace InputCommon::Joycon