input_common: Implement joycon ir camera
This commit is contained in:
		| @@ -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(); | ||||
|     } | ||||
|   | ||||
| @@ -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>; | ||||
|   | ||||
| @@ -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; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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(); | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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); | ||||
|  | ||||
|   | ||||
| @@ -74,6 +74,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 | ||||
|   | ||||
							
								
								
									
										300
									
								
								src/input_common/helpers/joycon_protocol/irs.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										300
									
								
								src/input_common/helpers/joycon_protocol/irs.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
							
								
								
									
										63
									
								
								src/input_common/helpers/joycon_protocol/irs.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/input_common/helpers/joycon_protocol/irs.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user