diff options
Diffstat (limited to 'src/input_common/helpers/joycon_protocol/irs.cpp')
| -rw-r--r-- | src/input_common/helpers/joycon_protocol/irs.cpp | 300 |
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 | |||
| 8 | namespace InputCommon::Joycon { | ||
| 9 | |||
| 10 | IrsProtocol::IrsProtocol(std::shared_ptr<JoyconHandle> handle) | ||
| 11 | : JoyconCommonProtocol(std::move(handle)) {} | ||
| 12 | |||
| 13 | DriverResult 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 | |||
| 56 | DriverResult 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 | |||
| 71 | DriverResult 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 | |||
| 111 | DriverResult 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 | |||
| 134 | DriverResult 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 | |||
| 168 | DriverResult 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 | |||
| 228 | DriverResult 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 | |||
| 270 | DriverResult 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 | |||
| 278 | DriverResult 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 | |||
| 288 | std::vector<u8> IrsProtocol::GetImage() const { | ||
| 289 | return buf_image; | ||
| 290 | } | ||
| 291 | |||
| 292 | IrsResolution IrsProtocol::GetIrsFormat() const { | ||
| 293 | return resolution; | ||
| 294 | } | ||
| 295 | |||
| 296 | bool IrsProtocol::IsEnabled() const { | ||
| 297 | return is_enabled; | ||
| 298 | } | ||
| 299 | |||
| 300 | } // namespace InputCommon::Joycon | ||