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