diff options
| -rw-r--r-- | src/core/hid/emulated_controller.cpp | 21 | ||||
| -rw-r--r-- | src/core/hid/emulated_controller.h | 5 | ||||
| -rw-r--r-- | src/core/hle/service/hid/irs.cpp | 11 | ||||
| -rw-r--r-- | src/input_common/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/input_common/drivers/joycon.cpp | 29 | ||||
| -rw-r--r-- | src/input_common/drivers/joycon.h | 5 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_driver.cpp | 55 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_driver.h | 4 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_protocol/common_protocol.cpp | 13 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_protocol/common_protocol.h | 7 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_protocol/irs.cpp | 300 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_protocol/irs.h | 63 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_protocol/joycon_types.h | 107 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_protocol/poller.cpp | 6 | ||||
| -rw-r--r-- | src/input_common/helpers/joycon_protocol/poller.h | 3 | 
15 files changed, 608 insertions, 23 deletions
| diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp index 915ffa490..faf9e7c4e 100644 --- a/src/core/hid/emulated_controller.cpp +++ b/src/core/hid/emulated_controller.cpp @@ -145,7 +145,9 @@ void EmulatedController::LoadDevices() {      battery_params[LeftIndex].Set("battery", true);      battery_params[RightIndex].Set("battery", true); -    camera_params = Common::ParamPackage{"engine:camera,camera:1"}; +    camera_params[0] = right_joycon; +    camera_params[0].Set("camera", true); +    camera_params[1] = Common::ParamPackage{"engine:camera,camera:1"};      ring_params[1] = Common::ParamPackage{"engine:joycon,axis_x:100,axis_y:101"};      nfc_params[0] = Common::ParamPackage{"engine:virtual_amiibo,nfc:1"};      nfc_params[1] = right_joycon; @@ -153,7 +155,7 @@ void EmulatedController::LoadDevices() {      output_params[LeftIndex] = left_joycon;      output_params[RightIndex] = right_joycon; -    output_params[2] = camera_params; +    output_params[2] = camera_params[1];      output_params[3] = nfc_params[0];      output_params[LeftIndex].Set("output", true);      output_params[RightIndex].Set("output", true); @@ -171,7 +173,7 @@ void EmulatedController::LoadDevices() {      std::ranges::transform(battery_params, battery_devices.begin(),                             Common::Input::CreateInputDevice);      std::ranges::transform(color_params, color_devices.begin(), Common::Input::CreateInputDevice); -    camera_devices = Common::Input::CreateInputDevice(camera_params); +    std::ranges::transform(camera_params, camera_devices.begin(), Common::Input::CreateInputDevice);      std::ranges::transform(ring_params, ring_analog_devices.begin(),                             Common::Input::CreateInputDevice);      std::ranges::transform(nfc_params, nfc_devices.begin(), Common::Input::CreateInputDevice); @@ -362,12 +364,15 @@ void EmulatedController::ReloadInput() {          motion_devices[index]->ForceUpdate();      } -    if (camera_devices) { -        camera_devices->SetCallback({ +    for (std::size_t index = 0; index < camera_devices.size(); ++index) { +        if (!camera_devices[index]) { +            continue; +        } +        camera_devices[index]->SetCallback({              .on_change =                  [this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); },          }); -        camera_devices->ForceUpdate(); +        camera_devices[index]->ForceUpdate();      }      for (std::size_t index = 0; index < ring_analog_devices.size(); ++index) { @@ -477,7 +482,9 @@ void EmulatedController::UnloadInput() {      for (auto& stick : virtual_stick_devices) {          stick.reset();      } -    camera_devices.reset(); +    for (auto& camera : camera_devices) { +        camera.reset(); +    }      for (auto& ring : ring_analog_devices) {          ring.reset();      } diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h index fb931fc0a..edebfc15c 100644 --- a/src/core/hid/emulated_controller.h +++ b/src/core/hid/emulated_controller.h @@ -39,7 +39,8 @@ using ColorDevices =      std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;  using BatteryDevices =      std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>; -using CameraDevices = std::unique_ptr<Common::Input::InputDevice>; +using CameraDevices = +    std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;  using RingAnalogDevices =      std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;  using NfcDevices = @@ -52,7 +53,7 @@ using ControllerMotionParams = std::array<Common::ParamPackage, Settings::Native  using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;  using ColorParams = std::array<Common::ParamPackage, max_emulated_controllers>;  using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>; -using CameraParams = Common::ParamPackage; +using CameraParams = std::array<Common::ParamPackage, max_emulated_controllers>;  using RingAnalogParams = std::array<Common::ParamPackage, max_emulated_controllers>;  using NfcParams = std::array<Common::ParamPackage, max_emulated_controllers>;  using OutputParams = std::array<Common::ParamPackage, output_devices_size>; diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index 6a3453457..3c1fa2274 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp @@ -74,6 +74,8 @@ void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) {      LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",                  applet_resource_user_id); +    npad_device->SetPollingMode(Common::Input::PollingMode::Active); +      IPC::ResponseBuilder rb{ctx, 2};      rb.Push(ResultSuccess);  } @@ -108,6 +110,7 @@ void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {      auto result = IsIrCameraHandleValid(parameters.camera_handle);      if (result.IsSuccess()) {          // TODO: Stop Image processor +        npad_device->SetPollingMode(Common::Input::PollingMode::Active);          result = ResultSuccess;      } @@ -139,6 +142,7 @@ void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {          MakeProcessor<MomentProcessor>(parameters.camera_handle, device);          auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle);          image_transfer_processor.SetConfig(parameters.processor_config); +        npad_device->SetPollingMode(Common::Input::PollingMode::IR);      }      IPC::ResponseBuilder rb{ctx, 2}; @@ -170,6 +174,7 @@ void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {          auto& image_transfer_processor =              GetProcessor<ClusteringProcessor>(parameters.camera_handle);          image_transfer_processor.SetConfig(parameters.processor_config); +        npad_device->SetPollingMode(Common::Input::PollingMode::IR);      }      IPC::ResponseBuilder rb{ctx, 2}; @@ -219,6 +224,7 @@ void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {              GetProcessor<ImageTransferProcessor>(parameters.camera_handle);          image_transfer_processor.SetConfig(parameters.processor_config);          image_transfer_processor.SetTransferMemoryPointer(transfer_memory); +        npad_device->SetPollingMode(Common::Input::PollingMode::IR);      }      IPC::ResponseBuilder rb{ctx, 2}; @@ -294,6 +300,7 @@ void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {          auto& image_transfer_processor =              GetProcessor<TeraPluginProcessor>(parameters.camera_handle);          image_transfer_processor.SetConfig(parameters.processor_config); +        npad_device->SetPollingMode(Common::Input::PollingMode::IR);      }      IPC::ResponseBuilder rb{ctx, 2}; @@ -343,6 +350,7 @@ void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {          MakeProcessor<PointingProcessor>(camera_handle, device);          auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle);          image_transfer_processor.SetConfig(processor_config); +        npad_device->SetPollingMode(Common::Input::PollingMode::IR);      }      IPC::ResponseBuilder rb{ctx, 2}; @@ -453,6 +461,7 @@ void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {              GetProcessor<ImageTransferProcessor>(parameters.camera_handle);          image_transfer_processor.SetConfig(parameters.processor_config);          image_transfer_processor.SetTransferMemoryPointer(transfer_memory); +        npad_device->SetPollingMode(Common::Input::PollingMode::IR);      }      IPC::ResponseBuilder rb{ctx, 2}; @@ -479,6 +488,7 @@ void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {          MakeProcessor<IrLedProcessor>(camera_handle, device);          auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle);          image_transfer_processor.SetConfig(processor_config); +        npad_device->SetPollingMode(Common::Input::PollingMode::IR);      }      IPC::ResponseBuilder rb{ctx, 2}; @@ -504,6 +514,7 @@ void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {      auto result = IsIrCameraHandleValid(parameters.camera_handle);      if (result.IsSuccess()) {          // TODO: Stop image processor async +        npad_device->SetPollingMode(Common::Input::PollingMode::IR);          result = ResultSuccess;      } diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 9c901af2a..e3b627e4f 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt @@ -64,6 +64,8 @@ if (ENABLE_SDL2)          helpers/joycon_protocol/generic_functions.cpp          helpers/joycon_protocol/generic_functions.h          helpers/joycon_protocol/joycon_types.h +        helpers/joycon_protocol/irs.cpp +        helpers/joycon_protocol/irs.h          helpers/joycon_protocol/nfc.cpp          helpers/joycon_protocol/nfc.h          helpers/joycon_protocol/poller.cpp diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp index cf54f1b53..6c03e0953 100644 --- a/src/input_common/drivers/joycon.cpp +++ b/src/input_common/drivers/joycon.cpp @@ -191,6 +191,10 @@ void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) {              .on_amiibo_data = {[this, port](const std::vector<u8>& amiibo_data) {                  OnAmiiboUpdate(port, amiibo_data);              }}, +            .on_camera_data = {[this, port](const std::vector<u8>& camera_data, +                                            Joycon::IrsResolution format) { +                OnCameraUpdate(port, camera_data, format); +            }},          };          handle->InitializeDevice(); @@ -265,9 +269,14 @@ Common::Input::DriverResult Joycons::SetLeds(const PadIdentifier& identifier,          handle->SetLedConfig(static_cast<u8>(led_config)));  } -Common::Input::DriverResult Joycons::SetCameraFormat(const PadIdentifier& identifier_, +Common::Input::DriverResult Joycons::SetCameraFormat(const PadIdentifier& identifier,                                                       Common::Input::CameraFormat camera_format) { -    return Common::Input::DriverResult::NotSupported; +    auto handle = GetHandle(identifier); +    if (handle == nullptr) { +        return Common::Input::DriverResult::InvalidHandle; +    } +    return static_cast<Common::Input::DriverResult>(handle->SetIrsConfig( +        Joycon::IrsMode::ImageTransfer, static_cast<Joycon::IrsResolution>(camera_format)));  };  Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) const { @@ -288,18 +297,16 @@ Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identif      }      switch (polling_mode) { -    case Common::Input::PollingMode::NFC: -        return static_cast<Common::Input::DriverResult>(handle->SetNfcMode()); -        break;      case Common::Input::PollingMode::Active:          return static_cast<Common::Input::DriverResult>(handle->SetActiveMode()); -        break;      case Common::Input::PollingMode::Pasive:          return static_cast<Common::Input::DriverResult>(handle->SetPasiveMode()); -        break; +    case Common::Input::PollingMode::IR: +        return static_cast<Common::Input::DriverResult>(handle->SetIrMode()); +    case Common::Input::PollingMode::NFC: +        return static_cast<Common::Input::DriverResult>(handle->SetNfcMode());      case Common::Input::PollingMode::Ring:          return static_cast<Common::Input::DriverResult>(handle->SetRingConMode()); -        break;      default:          return Common::Input::DriverResult::NotSupported;      } @@ -390,6 +397,12 @@ void Joycons::OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_dat      SetNfc(identifier, {nfc_state, amiibo_data});  } +void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data, +                             Joycon::IrsResolution format) { +    const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right); +    SetCamera(identifier, {static_cast<Common::Input::CameraFormat>(format), camera_data}); +} +  std::shared_ptr<Joycon::JoyconDriver> Joycons::GetHandle(PadIdentifier identifier) const {      auto is_handle_active = [&](std::shared_ptr<Joycon::JoyconDriver> device) {          if (!device) { diff --git a/src/input_common/drivers/joycon.h b/src/input_common/drivers/joycon.h index 1a04c19fd..f180b7478 100644 --- a/src/input_common/drivers/joycon.h +++ b/src/input_common/drivers/joycon.h @@ -17,6 +17,7 @@ struct Color;  struct MotionData;  enum class ControllerType;  enum class DriverResult; +enum class IrsResolution;  class JoyconDriver;  } // namespace InputCommon::Joycon @@ -35,7 +36,7 @@ public:      Common::Input::DriverResult SetLeds(const PadIdentifier& identifier,                                          const Common::Input::LedStatus& led_status) override; -    Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier_, +    Common::Input::DriverResult SetCameraFormat(const PadIdentifier& identifier,                                                  Common::Input::CameraFormat camera_format) override;      Common::Input::NfcState SupportsNfc(const PadIdentifier& identifier_) const override; @@ -81,6 +82,8 @@ private:                          const Joycon::MotionData& value);      void OnRingConUpdate(f32 ring_data);      void OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_data); +    void OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data, +                        Joycon::IrsResolution format);      /// Returns a JoyconHandle corresponding to a PadIdentifier      std::shared_ptr<Joycon::JoyconDriver> GetHandle(PadIdentifier identifier) const; diff --git a/src/input_common/helpers/joycon_driver.cpp b/src/input_common/helpers/joycon_driver.cpp index 8217ba7f6..040832a4b 100644 --- a/src/input_common/helpers/joycon_driver.cpp +++ b/src/input_common/helpers/joycon_driver.cpp @@ -7,6 +7,7 @@  #include "input_common/helpers/joycon_driver.h"  #include "input_common/helpers/joycon_protocol/calibration.h"  #include "input_common/helpers/joycon_protocol/generic_functions.h" +#include "input_common/helpers/joycon_protocol/irs.h"  #include "input_common/helpers/joycon_protocol/nfc.h"  #include "input_common/helpers/joycon_protocol/poller.h"  #include "input_common/helpers/joycon_protocol/ringcon.h" @@ -78,6 +79,7 @@ DriverResult JoyconDriver::InitializeDevice() {      // Initialize HW Protocols      calibration_protocol = std::make_unique<CalibrationProtocol>(hidapi_handle);      generic_protocol = std::make_unique<GenericProtocol>(hidapi_handle); +    irs_protocol = std::make_unique<IrsProtocol>(hidapi_handle);      nfc_protocol = std::make_unique<NfcProtocol>(hidapi_handle);      ring_protocol = std::make_unique<RingConProtocol>(hidapi_handle);      rumble_protocol = std::make_unique<RumbleProtocol>(hidapi_handle); @@ -200,10 +202,15 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {          .min_value = ring_calibration.min_value,      }; +    if (irs_protocol->IsEnabled()) { +        irs_protocol->RequestImage(buffer); +        joycon_poller->UpdateCamera(irs_protocol->GetImage(), irs_protocol->GetIrsFormat()); +    } +      if (nfc_protocol->IsEnabled()) {          if (amiibo_detected) {              if (!nfc_protocol->HasAmiibo()) { -                joycon_poller->updateAmiibo({}); +                joycon_poller->UpdateAmiibo({});                  amiibo_detected = false;                  return;              } @@ -213,7 +220,7 @@ void JoyconDriver::OnNewData(std::span<u8> buffer) {              std::vector<u8> data(0x21C);              const auto result = nfc_protocol->ScanAmiibo(data);              if (result == DriverResult::Success) { -                joycon_poller->updateAmiibo(data); +                joycon_poller->UpdateAmiibo(data);                  amiibo_detected = true;              }          } @@ -251,6 +258,20 @@ DriverResult JoyconDriver::SetPollingMode() {          generic_protocol->EnableImu(false);      } +    if (irs_protocol->IsEnabled()) { +        irs_protocol->DisableIrs(); +    } + +    if (irs_enabled && supported_features.irs) { +        auto result = irs_protocol->EnableIrs(); +        if (result == DriverResult::Success) { +            disable_input_thread = false; +            return result; +        } +        irs_protocol->DisableIrs(); +        LOG_ERROR(Input, "Error enabling IRS"); +    } +      if (nfc_protocol->IsEnabled()) {          amiibo_detected = false;          nfc_protocol->DisableNfc(); @@ -375,12 +396,24 @@ DriverResult JoyconDriver::SetLedConfig(u8 led_pattern) {      return generic_protocol->SetLedPattern(led_pattern);  } +DriverResult JoyconDriver::SetIrsConfig(IrsMode mode_, IrsResolution format_) { +    std::scoped_lock lock{mutex}; +    if (disable_input_thread) { +        return DriverResult::HandleInUse; +    } +    disable_input_thread = true; +    const auto result = irs_protocol->SetIrsConfig(mode_, format_); +    disable_input_thread = false; +    return result; +} +  DriverResult JoyconDriver::SetPasiveMode() {      std::scoped_lock lock{mutex};      motion_enabled = false;      hidbus_enabled = false;      nfc_enabled = false;      passive_enabled = true; +    irs_enabled = false;      return SetPollingMode();  } @@ -390,6 +423,22 @@ DriverResult JoyconDriver::SetActiveMode() {      hidbus_enabled = false;      nfc_enabled = false;      passive_enabled = false; +    irs_enabled = false; +    return SetPollingMode(); +} + +DriverResult JoyconDriver::SetIrMode() { +    std::scoped_lock lock{mutex}; + +    if (!supported_features.irs) { +        return DriverResult::NotSupported; +    } + +    motion_enabled = false; +    hidbus_enabled = false; +    nfc_enabled = false; +    passive_enabled = false; +    irs_enabled = true;      return SetPollingMode();  } @@ -404,6 +453,7 @@ DriverResult JoyconDriver::SetNfcMode() {      hidbus_enabled = false;      nfc_enabled = true;      passive_enabled = false; +    irs_enabled = false;      return SetPollingMode();  } @@ -418,6 +468,7 @@ DriverResult JoyconDriver::SetRingConMode() {      hidbus_enabled = true;      nfc_enabled = false;      passive_enabled = false; +    irs_enabled = false;      const auto result = SetPollingMode(); diff --git a/src/input_common/helpers/joycon_driver.h b/src/input_common/helpers/joycon_driver.h index 5ff15c784..61ecf4a6c 100644 --- a/src/input_common/helpers/joycon_driver.h +++ b/src/input_common/helpers/joycon_driver.h @@ -13,6 +13,7 @@  namespace InputCommon::Joycon {  class CalibrationProtocol;  class GenericProtocol; +class IrsProtocol;  class NfcProtocol;  class JoyconPoller;  class RingConProtocol; @@ -41,8 +42,10 @@ public:      DriverResult SetVibration(const VibrationValue& vibration);      DriverResult SetLedConfig(u8 led_pattern); +    DriverResult SetIrsConfig(IrsMode mode_, IrsResolution format_);      DriverResult SetPasiveMode();      DriverResult SetActiveMode(); +    DriverResult SetIrMode();      DriverResult SetNfcMode();      DriverResult SetRingConMode(); @@ -87,6 +90,7 @@ private:      // Protocol Features      std::unique_ptr<CalibrationProtocol> calibration_protocol;      std::unique_ptr<GenericProtocol> generic_protocol; +    std::unique_ptr<IrsProtocol> irs_protocol;      std::unique_ptr<NfcProtocol> nfc_protocol;      std::unique_ptr<JoyconPoller> joycon_poller;      std::unique_ptr<RingConProtocol> ring_protocol; diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.cpp b/src/input_common/helpers/joycon_protocol/common_protocol.cpp index a4d08fdaf..a329db107 100644 --- a/src/input_common/helpers/joycon_protocol/common_protocol.cpp +++ b/src/input_common/helpers/joycon_protocol/common_protocol.cpp @@ -120,6 +120,19 @@ DriverResult JoyconCommonProtocol::SendSubCommand(SubCommand sc, std::span<const      return DriverResult::Success;  } +DriverResult JoyconCommonProtocol::SendMcuCommand(SubCommand sc, std::span<const u8> buffer) { +    std::vector<u8> local_buffer(MaxResponseSize); + +    local_buffer[0] = static_cast<u8>(OutputReport::MCU_DATA); +    local_buffer[1] = GetCounter(); +    local_buffer[10] = static_cast<u8>(sc); +    for (std::size_t i = 0; i < buffer.size(); ++i) { +        local_buffer[11 + i] = buffer[i]; +    } + +    return SendData(local_buffer); +} +  DriverResult JoyconCommonProtocol::SendVibrationReport(std::span<const u8> buffer) {      std::vector<u8> local_buffer(MaxResponseSize); diff --git a/src/input_common/helpers/joycon_protocol/common_protocol.h b/src/input_common/helpers/joycon_protocol/common_protocol.h index a65e4aa76..2a3feaf59 100644 --- a/src/input_common/helpers/joycon_protocol/common_protocol.h +++ b/src/input_common/helpers/joycon_protocol/common_protocol.h @@ -75,6 +75,13 @@ public:      DriverResult SendSubCommand(SubCommand sc, std::span<const u8> buffer, std::vector<u8>& output);      /** +     * Sends a mcu command to the device +     * @param sc sub command to be send +     * @param buffer data to be send +     */ +    DriverResult SendMcuCommand(SubCommand sc, std::span<const u8> buffer); + +    /**       * Sends vibration data to the joycon       * @param buffer data to be send       */ 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 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <thread> +#include "common/logging/log.h" +#include "input_common/helpers/joycon_protocol/irs.h" + +namespace InputCommon::Joycon { + +IrsProtocol::IrsProtocol(std::shared_ptr<JoyconHandle> handle) +    : JoyconCommonProtocol(std::move(handle)) {} + +DriverResult IrsProtocol::EnableIrs() { +    LOG_INFO(Input, "Enable IRS"); +    DriverResult result{DriverResult::Success}; +    SetBlocking(); + +    if (result == DriverResult::Success) { +        result = SetReportMode(ReportMode::NFC_IR_MODE_60HZ); +    } +    if (result == DriverResult::Success) { +        result = EnableMCU(true); +    } +    if (result == DriverResult::Success) { +        result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::Standby); +    } +    if (result == DriverResult::Success) { +        const MCUConfig config{ +            .command = MCUCommand::ConfigureMCU, +            .sub_command = MCUSubCommand::SetMCUMode, +            .mode = MCUMode::IR, +            .crc = {}, +        }; + +        result = ConfigureMCU(config); +    } +    if (result == DriverResult::Success) { +        result = WaitSetMCUMode(ReportMode::NFC_IR_MODE_60HZ, MCUMode::IR); +    } +    if (result == DriverResult::Success) { +        result = ConfigureIrs(); +    } +    if (result == DriverResult::Success) { +        result = WriteRegistersStep1(); +    } +    if (result == DriverResult::Success) { +        result = WriteRegistersStep2(); +    } + +    is_enabled = true; + +    SetNonBlocking(); +    return result; +} + +DriverResult IrsProtocol::DisableIrs() { +    LOG_DEBUG(Input, "Disable IRS"); +    DriverResult result{DriverResult::Success}; +    SetBlocking(); + +    if (result == DriverResult::Success) { +        result = EnableMCU(false); +    } + +    is_enabled = false; + +    SetNonBlocking(); +    return result; +} + +DriverResult IrsProtocol::SetIrsConfig(IrsMode mode, IrsResolution format) { +    irs_mode = mode; +    switch (format) { +    case IrsResolution::Size320x240: +        resolution_code = IrsResolutionCode::Size320x240; +        fragments = IrsFragments::Size320x240; +        resolution = IrsResolution::Size320x240; +        break; +    case IrsResolution::Size160x120: +        resolution_code = IrsResolutionCode::Size160x120; +        fragments = IrsFragments::Size160x120; +        resolution = IrsResolution::Size160x120; +        break; +    case IrsResolution::Size80x60: +        resolution_code = IrsResolutionCode::Size80x60; +        fragments = IrsFragments::Size80x60; +        resolution = IrsResolution::Size80x60; +        break; +    case IrsResolution::Size20x15: +        resolution_code = IrsResolutionCode::Size20x15; +        fragments = IrsFragments::Size20x15; +        resolution = IrsResolution::Size20x15; +        break; +    case IrsResolution::Size40x30: +    default: +        resolution_code = IrsResolutionCode::Size40x30; +        fragments = IrsFragments::Size40x30; +        resolution = IrsResolution::Size40x30; +        break; +    } + +    // Restart feature +    if (is_enabled) { +        DisableIrs(); +        return EnableIrs(); +    } + +    return DriverResult::Success; +} + +DriverResult IrsProtocol::RequestImage(std::span<u8> buffer) { +    const u8 next_packet_fragment = +        static_cast<u8>((packet_fragment + 1) % (static_cast<u8>(fragments) + 1)); + +    if (buffer[0] == 0x31 && buffer[49] == 0x03) { +        u8 new_packet_fragment = buffer[52]; +        if (new_packet_fragment == next_packet_fragment) { +            packet_fragment = next_packet_fragment; +            memcpy(buf_image.data() + (300 * packet_fragment), buffer.data() + 59, 300); + +            return RequestFrame(packet_fragment); +        } + +        if (new_packet_fragment == packet_fragment) { +            return RequestFrame(packet_fragment); +        } + +        return ResendFrame(next_packet_fragment); +    } + +    return RequestFrame(packet_fragment); +} + +DriverResult IrsProtocol::ConfigureIrs() { +    LOG_DEBUG(Input, "Configure IRS"); +    constexpr std::size_t max_tries = 28; +    std::vector<u8> output; +    std::size_t tries = 0; + +    const IrsConfigure irs_configuration{ +        .command = MCUCommand::ConfigureIR, +        .sub_command = MCUSubCommand::SetDeviceMode, +        .irs_mode = IrsMode::ImageTransfer, +        .number_of_fragments = fragments, +        .mcu_major_version = 0x0500, +        .mcu_minor_version = 0x1800, +        .crc = {}, +    }; +    buf_image.resize((static_cast<u8>(fragments) + 1) * 300); + +    std::vector<u8> request_data(sizeof(IrsConfigure)); +    memcpy(request_data.data(), &irs_configuration, sizeof(IrsConfigure)); +    request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); +    do { +        const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output); + +        if (result != DriverResult::Success) { +            return result; +        } +        if (tries++ >= max_tries) { +            return DriverResult::WrongReply; +        } +    } while (output[15] != 0x0b); + +    return DriverResult::Success; +} + +DriverResult IrsProtocol::WriteRegistersStep1() { +    LOG_DEBUG(Input, "WriteRegistersStep1"); +    DriverResult result{DriverResult::Success}; +    constexpr std::size_t max_tries = 28; +    std::vector<u8> output; +    std::size_t tries = 0; + +    const IrsWriteRegisters irs_registers{ +        .command = MCUCommand::ConfigureIR, +        .sub_command = MCUSubCommand::WriteDeviceRegisters, +        .number_of_registers = 0x9, +        .registers = +            { +                IrsRegister{IrRegistersAddress::Resolution, static_cast<u8>(resolution_code)}, +                {IrRegistersAddress::ExposureLSB, static_cast<u8>(exposure & 0xff)}, +                {IrRegistersAddress::ExposureMSB, static_cast<u8>(exposure >> 8)}, +                {IrRegistersAddress::ExposureTime, 0x00}, +                {IrRegistersAddress::Leds, static_cast<u8>(leds)}, +                {IrRegistersAddress::DigitalGainLSB, static_cast<u8>((digital_gain & 0x0f) << 4)}, +                {IrRegistersAddress::DigitalGainMSB, static_cast<u8>((digital_gain & 0xf0) >> 4)}, +                {IrRegistersAddress::LedFilter, static_cast<u8>(led_filter)}, +                {IrRegistersAddress::WhitePixelThreshold, 0xc8}, +            }, +        .crc = {}, +    }; + +    std::vector<u8> request_data(sizeof(IrsWriteRegisters)); +    memcpy(request_data.data(), &irs_registers, sizeof(IrsWriteRegisters)); +    request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); + +    std::array<u8, 38> mcu_request{0x02}; +    mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36); +    mcu_request[37] = 0xFF; + +    if (result != DriverResult::Success) { +        return result; +    } + +    do { +        result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output); + +        // First time we need to set the report mode +        if (result == DriverResult::Success && tries == 0) { +            result = SendMcuCommand(SubCommand::SET_REPORT_MODE, mcu_request); +        } +        if (result == DriverResult::Success && tries == 0) { +            GetSubCommandResponse(SubCommand::SET_MCU_CONFIG, output); +        } + +        if (result != DriverResult::Success) { +            return result; +        } +        if (tries++ >= max_tries) { +            return DriverResult::WrongReply; +        } +    } while (!(output[15] == 0x13 && output[17] == 0x07) && output[15] != 0x23); + +    return DriverResult::Success; +} + +DriverResult IrsProtocol::WriteRegistersStep2() { +    LOG_DEBUG(Input, "WriteRegistersStep2"); +    constexpr std::size_t max_tries = 28; +    std::vector<u8> output; +    std::size_t tries = 0; + +    const IrsWriteRegisters irs_registers{ +        .command = MCUCommand::ConfigureIR, +        .sub_command = MCUSubCommand::WriteDeviceRegisters, +        .number_of_registers = 0x8, +        .registers = +            { +                IrsRegister{IrRegistersAddress::LedIntensitiyMSB, +                            static_cast<u8>(led_intensity >> 8)}, +                {IrRegistersAddress::LedIntensitiyLSB, static_cast<u8>(led_intensity & 0xff)}, +                {IrRegistersAddress::ImageFlip, static_cast<u8>(image_flip)}, +                {IrRegistersAddress::DenoiseSmoothing, static_cast<u8>((denoise >> 16) & 0xff)}, +                {IrRegistersAddress::DenoiseEdge, static_cast<u8>((denoise >> 8) & 0xff)}, +                {IrRegistersAddress::DenoiseColor, static_cast<u8>(denoise & 0xff)}, +                {IrRegistersAddress::UpdateTime, 0x2d}, +                {IrRegistersAddress::FinalizeConfig, 0x01}, +            }, +        .crc = {}, +    }; + +    std::vector<u8> request_data(sizeof(IrsWriteRegisters)); +    memcpy(request_data.data(), &irs_registers, sizeof(IrsWriteRegisters)); +    request_data[37] = CalculateMCU_CRC8(request_data.data() + 1, 36); +    do { +        const auto result = SendSubCommand(SubCommand::SET_MCU_CONFIG, request_data, output); + +        if (result != DriverResult::Success) { +            return result; +        } +        if (tries++ >= max_tries) { +            return DriverResult::WrongReply; +        } +    } while (output[15] != 0x13 && output[15] != 0x23); + +    return DriverResult::Success; +} + +DriverResult IrsProtocol::RequestFrame(u8 frame) { +    std::array<u8, 38> mcu_request{}; +    mcu_request[3] = frame; +    mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36); +    mcu_request[37] = 0xFF; +    return SendMcuCommand(SubCommand::SET_REPORT_MODE, mcu_request); +} + +DriverResult IrsProtocol::ResendFrame(u8 frame) { +    std::array<u8, 38> mcu_request{}; +    mcu_request[1] = 0x1; +    mcu_request[2] = frame; +    mcu_request[3] = 0x0; +    mcu_request[36] = CalculateMCU_CRC8(mcu_request.data(), 36); +    mcu_request[37] = 0xFF; +    return SendMcuCommand(SubCommand::SET_REPORT_MODE, mcu_request); +} + +std::vector<u8> IrsProtocol::GetImage() const { +    return buf_image; +} + +IrsResolution IrsProtocol::GetIrsFormat() const { +    return resolution; +} + +bool IrsProtocol::IsEnabled() const { +    return is_enabled; +} + +} // namespace InputCommon::Joycon diff --git a/src/input_common/helpers/joycon_protocol/irs.h b/src/input_common/helpers/joycon_protocol/irs.h new file mode 100644 index 000000000..76dfa02ea --- /dev/null +++ b/src/input_common/helpers/joycon_protocol/irs.h @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +// Based on dkms-hid-nintendo implementation, CTCaer joycon toolkit and dekuNukem reverse +// engineering https://github.com/nicman23/dkms-hid-nintendo/blob/master/src/hid-nintendo.c +// https://github.com/CTCaer/jc_toolkit +// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering + +#pragma once + +#include <vector> + +#include "input_common/helpers/joycon_protocol/common_protocol.h" +#include "input_common/helpers/joycon_protocol/joycon_types.h" + +namespace InputCommon::Joycon { + +class IrsProtocol final : private JoyconCommonProtocol { +public: +    explicit IrsProtocol(std::shared_ptr<JoyconHandle> handle); + +    DriverResult EnableIrs(); + +    DriverResult DisableIrs(); + +    DriverResult SetIrsConfig(IrsMode mode, IrsResolution format); + +    DriverResult RequestImage(std::span<u8> buffer); + +    std::vector<u8> GetImage() const; + +    IrsResolution GetIrsFormat() const; + +    bool IsEnabled() const; + +private: +    DriverResult ConfigureIrs(); + +    DriverResult WriteRegistersStep1(); +    DriverResult WriteRegistersStep2(); + +    DriverResult RequestFrame(u8 frame); +    DriverResult ResendFrame(u8 frame); + +    IrsMode irs_mode{IrsMode::ImageTransfer}; +    IrsResolution resolution{IrsResolution::Size40x30}; +    IrsResolutionCode resolution_code{IrsResolutionCode::Size40x30}; +    IrsFragments fragments{IrsFragments::Size40x30}; +    IrLeds leds{IrLeds::BrightAndDim}; +    IrExLedFilter led_filter{IrExLedFilter::Enabled}; +    IrImageFlip image_flip{IrImageFlip::Normal}; +    u8 digital_gain{0x01}; +    u16 exposure{0x2490}; +    u16 led_intensity{0x0f10}; +    u32 denoise{0x012344}; + +    u8 packet_fragment{}; +    std::vector<u8> buf_image; // 8bpp greyscale image. + +    bool is_enabled{}; +}; + +} // namespace InputCommon::Joycon diff --git a/src/input_common/helpers/joycon_protocol/joycon_types.h b/src/input_common/helpers/joycon_protocol/joycon_types.h index 36c00a8d7..273c8d07d 100644 --- a/src/input_common/helpers/joycon_protocol/joycon_types.h +++ b/src/input_common/helpers/joycon_protocol/joycon_types.h @@ -18,7 +18,7 @@  namespace InputCommon::Joycon {  constexpr u32 MaxErrorCount = 50; -constexpr u32 MaxBufferSize = 60; +constexpr u32 MaxBufferSize = 368;  constexpr u32 MaxResponseSize = 49;  constexpr u32 MaxSubCommandResponseSize = 64;  constexpr std::array<u8, 8> DefaultVibrationBuffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x1, 0x40, 0x40}; @@ -273,6 +273,80 @@ enum class NFCTagType : u8 {      Ntag215 = 0x01,  }; +enum class IrsMode : u8 { +    None = 0x02, +    Moment = 0x03, +    Dpd = 0x04, +    Clustering = 0x06, +    ImageTransfer = 0x07, +    Silhouette = 0x08, +    TeraImage = 0x09, +    SilhouetteTeraImage = 0x0A, +}; + +enum class IrsResolution { +    Size320x240, +    Size160x120, +    Size80x60, +    Size40x30, +    Size20x15, +    None, +}; + +enum class IrsResolutionCode : u8 { +    Size320x240 = 0x00, // Full pixel array +    Size160x120 = 0x50, // Sensor Binning [2 X 2] +    Size80x60 = 0x64,   // Sensor Binning [4 x 2] and Skipping [1 x 2] +    Size40x30 = 0x69,   // Sensor Binning [4 x 2] and Skipping [2 x 4] +    Size20x15 = 0x6A,   // Sensor Binning [4 x 2] and Skipping [4 x 4] +}; + +// Size of image divided by 300 +enum class IrsFragments : u8 { +    Size20x15 = 0x00, +    Size40x30 = 0x03, +    Size80x60 = 0x0f, +    Size160x120 = 0x3f, +    Size320x240 = 0xFF, +}; + +enum class IrLeds : u8 { +    BrightAndDim = 0x00, +    Bright = 0x20, +    Dim = 0x10, +    None = 0x30, +}; + +enum class IrExLedFilter : u8 { +    Disabled = 0x00, +    Enabled = 0x03, +}; + +enum class IrImageFlip : u8 { +    Normal = 0x00, +    Inverted = 0x02, +}; + +enum class IrRegistersAddress : u16 { +    UpdateTime = 0x0400, +    FinalizeConfig = 0x0700, +    LedFilter = 0x0e00, +    Leds = 0x1000, +    LedIntensitiyMSB = 0x1100, +    LedIntensitiyLSB = 0x1200, +    ImageFlip = 0x2d00, +    Resolution = 0x2e00, +    DigitalGainLSB = 0x2e01, +    DigitalGainMSB = 0x2f01, +    ExposureLSB = 0x3001, +    ExposureMSB = 0x3101, +    ExposureTime = 0x3201, +    WhitePixelThreshold = 0x4301, +    DenoiseSmoothing = 0x6701, +    DenoiseEdge = 0x6801, +    DenoiseColor = 0x6901, +}; +  enum class DriverResult {      Success,      WrongReply, @@ -456,6 +530,36 @@ struct NFCRequestState {  };  static_assert(sizeof(NFCRequestState) == 0x26, "NFCRequestState is an invalid size"); +struct IrsConfigure { +    MCUCommand command; +    MCUSubCommand sub_command; +    IrsMode irs_mode; +    IrsFragments number_of_fragments; +    u16 mcu_major_version; +    u16 mcu_minor_version; +    INSERT_PADDING_BYTES(0x1D); +    u8 crc; +}; +static_assert(sizeof(IrsConfigure) == 0x26, "IrsConfigure is an invalid size"); + +#pragma pack(push, 1) +struct IrsRegister { +    IrRegistersAddress address; +    u8 value; +}; +static_assert(sizeof(IrsRegister) == 0x3, "IrsRegister is an invalid size"); + +struct IrsWriteRegisters { +    MCUCommand command; +    MCUSubCommand sub_command; +    u8 number_of_registers; +    std::array<IrsRegister, 9> registers; +    INSERT_PADDING_BYTES(0x7); +    u8 crc; +}; +static_assert(sizeof(IrsWriteRegisters) == 0x26, "IrsWriteRegisters is an invalid size"); +#pragma pack(pop) +  struct FirmwareVersion {      u8 major;      u8 minor; @@ -490,6 +594,7 @@ struct JoyconCallbacks {      std::function<void(int, const MotionData&)> on_motion_data;      std::function<void(f32)> on_ring_data;      std::function<void(const std::vector<u8>&)> on_amiibo_data; +    std::function<void(const std::vector<u8>&, IrsResolution)> on_camera_data;  };  } // namespace InputCommon::Joycon diff --git a/src/input_common/helpers/joycon_protocol/poller.cpp b/src/input_common/helpers/joycon_protocol/poller.cpp index fd05d98f3..940b20b7f 100644 --- a/src/input_common/helpers/joycon_protocol/poller.cpp +++ b/src/input_common/helpers/joycon_protocol/poller.cpp @@ -74,10 +74,14 @@ void JoyconPoller::UpdateColor(const Color& color) {      callbacks.on_color_data(color);  } -void JoyconPoller::updateAmiibo(const std::vector<u8>& amiibo_data) { +void JoyconPoller::UpdateAmiibo(const std::vector<u8>& amiibo_data) {      callbacks.on_amiibo_data(amiibo_data);  } +void JoyconPoller::UpdateCamera(const std::vector<u8>& camera_data, IrsResolution format) { +    callbacks.on_camera_data(camera_data, format); +} +  void JoyconPoller::UpdateRing(s16 value, const RingStatus& ring_status) {      float normalized_value = static_cast<float>(value - ring_status.default_value);      if (normalized_value > 0) { diff --git a/src/input_common/helpers/joycon_protocol/poller.h b/src/input_common/helpers/joycon_protocol/poller.h index c40fc7bca..354d41dad 100644 --- a/src/input_common/helpers/joycon_protocol/poller.h +++ b/src/input_common/helpers/joycon_protocol/poller.h @@ -36,7 +36,8 @@ public:      void UpdateColor(const Color& color);      void UpdateRing(s16 value, const RingStatus& ring_status); -    void updateAmiibo(const std::vector<u8>& amiibo_data); +    void UpdateAmiibo(const std::vector<u8>& amiibo_data); +    void UpdateCamera(const std::vector<u8>& amiibo_data, IrsResolution format);  private:      void UpdateActiveLeftPadInput(const InputReportActive& input, | 
