From eddc4a029cbdd79b06dd5a0d13c9a02db6c8b79b Mon Sep 17 00:00:00 2001 From: GPUCode <47210458+GPUCode@users.noreply.github.com> Date: Mon, 22 Jan 2024 09:32:46 +0200 Subject: [PATCH] cam: Ensure camera implementation is not null before using it (#7368) * cam: Use PopEnum and update result names * cam: Make sure impl is not null before using it --- src/core/hle/service/cam/cam.cpp | 183 ++++++++++++++++++------------- 1 file changed, 108 insertions(+), 75 deletions(-) diff --git a/src/core/hle/service/cam/cam.cpp b/src/core/hle/service/cam/cam.cpp index 37bcf758b..46ff98916 100644 --- a/src/core/hle/service/cam/cam.cpp +++ b/src/core/hle/service/cam/cam.cpp @@ -10,7 +10,6 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/frontend/camera/factory.h" -#include "core/hle/ipc.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/process.h" @@ -77,10 +76,10 @@ constexpr std::array LATENCY_BY_FRAME_RATE{{ 33, // Rate_30_To_10 }}; -const Result ERROR_INVALID_ENUM_VALUE(ErrorDescription::InvalidEnumValue, ErrorModule::CAM, - ErrorSummary::InvalidArgument, ErrorLevel::Usage); -const Result ERROR_OUT_OF_RANGE(ErrorDescription::OutOfRange, ErrorModule::CAM, - ErrorSummary::InvalidArgument, ErrorLevel::Usage); +constexpr Result ResultInvalidEnumValue(ErrorDescription::InvalidEnumValue, ErrorModule::CAM, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); +constexpr Result ResultOutOfRange(ErrorDescription::OutOfRange, ErrorModule::CAM, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); void Module::PortConfig::Clear() { completion_event->Clear(); @@ -261,27 +260,32 @@ void Module::Interface::StartCapture(Kernel::HLERequestContext& ctx) { if (port_select.IsValid()) { for (int i : port_select) { - if (!cam->ports[i].is_busy) { - if (!cam->ports[i].is_active) { - // This doesn't return an error, but seems to put the camera in an undefined - // state - LOG_ERROR(Service_CAM, "port {} hasn't been activated", i); - } else { - cam->cameras[cam->ports[i].camera_id].impl->StartCapture(); - cam->ports[i].is_busy = true; - if (cam->ports[i].is_pending_receiving) { - cam->ports[i].is_pending_receiving = false; - cam->StartReceiving(i); - } - } - } else { + auto& port = cam->ports[i]; + if (port.is_busy) { LOG_WARNING(Service_CAM, "port {} already started", i); + continue; + } + if (!port.is_active) { + // This doesn't return an error, but seems to put the camera in an undefined + // state + LOG_ERROR(Service_CAM, "port {} hasn't been activated", i); + continue; + } + auto& camera = cam->cameras[port.camera_id]; + if (!camera.impl) { + cam->LoadCameraImplementation(camera, port.camera_id); + } + camera.impl->StartCapture(); + port.is_busy = true; + if (port.is_pending_receiving) { + port.is_pending_receiving = false; + cam->StartReceiving(i); } } rb.Push(ResultSuccess); } else { LOG_ERROR(Service_CAM, "invalid port_select={}", port_select.m_val); - rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Push(ResultInvalidEnumValue); } LOG_DEBUG(Service_CAM, "called, port_select={}", port_select.m_val); @@ -306,7 +310,7 @@ void Module::Interface::StopCapture(Kernel::HLERequestContext& ctx) { rb.Push(ResultSuccess); } else { LOG_ERROR(Service_CAM, "invalid port_select={}", port_select.m_val); - rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Push(ResultInvalidEnumValue); } LOG_DEBUG(Service_CAM, "called, port_select={}", port_select.m_val); @@ -328,7 +332,7 @@ void Module::Interface::IsBusy(Kernel::HLERequestContext& ctx) { rb.Push(is_busy); } else { LOG_ERROR(Service_CAM, "invalid port_select={}", port_select.m_val); - rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Push(ResultInvalidEnumValue); rb.Skip(1, false); } @@ -356,7 +360,7 @@ void Module::Interface::GetVsyncInterruptEvent(Kernel::HLERequestContext& ctx) { rb.PushCopyObjects(cam->ports[port].vsync_interrupt_event); } else { LOG_ERROR(Service_CAM, "invalid port_select={}", port_select.m_val); - rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Push(ResultInvalidEnumValue); rb.PushCopyObjects(nullptr); } @@ -374,7 +378,7 @@ void Module::Interface::GetBufferErrorInterruptEvent(Kernel::HLERequestContext& rb.PushCopyObjects(cam->ports[port].buffer_error_interrupt_event); } else { LOG_ERROR(Service_CAM, "invalid port_select={}", port_select.m_val); - rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Push(ResultInvalidEnumValue); rb.PushCopyObjects(nullptr); } @@ -409,7 +413,7 @@ void Module::Interface::SetReceiving(Kernel::HLERequestContext& ctx) { rb.PushCopyObjects(port.completion_event); } else { LOG_ERROR(Service_CAM, "invalid port_select={}", port_select.m_val); - rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Push(ResultInvalidEnumValue); rb.PushCopyObjects(nullptr); } @@ -429,7 +433,7 @@ void Module::Interface::IsFinishedReceiving(Kernel::HLERequestContext& ctx) { rb.Push(!is_busy); } else { LOG_ERROR(Service_CAM, "invalid port_select={}", port_select.m_val); - rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Push(ResultInvalidEnumValue); rb.Skip(1, false); } @@ -451,7 +455,7 @@ void Module::Interface::SetTransferLines(Kernel::HLERequestContext& ctx) { rb.Push(ResultSuccess); } else { LOG_ERROR(Service_CAM, "invalid port_select={}", port_select.m_val); - rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Push(ResultInvalidEnumValue); } LOG_WARNING(Service_CAM, "(STUBBED) called, port_select={}, lines={}, width={}, height={}", @@ -469,7 +473,7 @@ void Module::Interface::GetMaxLines(Kernel::HLERequestContext& ctx) { constexpr u32 MIN_TRANSFER_UNIT = 256; constexpr u32 MAX_BUFFER_SIZE = 2560; if (width * height * 2 % MIN_TRANSFER_UNIT != 0) { - rb.Push(ERROR_OUT_OF_RANGE); + rb.Push(ResultOutOfRange); rb.Skip(1, false); } else { u32 lines = MAX_BUFFER_SIZE / width; @@ -480,7 +484,7 @@ void Module::Interface::GetMaxLines(Kernel::HLERequestContext& ctx) { while (height % lines != 0 || (lines * width * 2 % MIN_TRANSFER_UNIT != 0)) { --lines; if (lines == 0) { - result = ERROR_OUT_OF_RANGE; + result = ResultOutOfRange; break; } } @@ -506,7 +510,7 @@ void Module::Interface::SetTransferBytes(Kernel::HLERequestContext& ctx) { rb.Push(ResultSuccess); } else { LOG_ERROR(Service_CAM, "invalid port_select={}", port_select.m_val); - rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Push(ResultInvalidEnumValue); } LOG_WARNING(Service_CAM, "(STUBBED)called, port_select={}, bytes={}, width={}, height={}", @@ -524,7 +528,7 @@ void Module::Interface::GetTransferBytes(Kernel::HLERequestContext& ctx) { rb.Push(cam->ports[port].transfer_bytes); } else { LOG_ERROR(Service_CAM, "invalid port_select={}", port_select.m_val); - rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Push(ResultInvalidEnumValue); rb.Skip(1, false); } @@ -542,7 +546,7 @@ void Module::Interface::GetMaxBytes(Kernel::HLERequestContext& ctx) { constexpr u32 MIN_TRANSFER_UNIT = 256; constexpr u32 MAX_BUFFER_SIZE = 2560; if (width * height * 2 % MIN_TRANSFER_UNIT != 0) { - rb.Push(ERROR_OUT_OF_RANGE); + rb.Push(ResultOutOfRange); rb.Skip(1, false); } else { u32 bytes = MAX_BUFFER_SIZE; @@ -571,7 +575,7 @@ void Module::Interface::SetTrimming(Kernel::HLERequestContext& ctx) { rb.Push(ResultSuccess); } else { LOG_ERROR(Service_CAM, "invalid port_select={}", port_select.m_val); - rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Push(ResultInvalidEnumValue); } LOG_DEBUG(Service_CAM, "called, port_select={}, trim={}", port_select.m_val, trim); @@ -588,7 +592,7 @@ void Module::Interface::IsTrimming(Kernel::HLERequestContext& ctx) { rb.Push(cam->ports[port].is_trimming); } else { LOG_ERROR(Service_CAM, "invalid port_select={}", port_select.m_val); - rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Push(ResultInvalidEnumValue); rb.Skip(1, false); } @@ -614,7 +618,7 @@ void Module::Interface::SetTrimmingParams(Kernel::HLERequestContext& ctx) { rb.Push(ResultSuccess); } else { LOG_ERROR(Service_CAM, "invalid port_select={}", port_select.m_val); - rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Push(ResultInvalidEnumValue); } LOG_DEBUG(Service_CAM, "called, port_select={}, x0={}, y0={}, x1={}, y1={}", port_select.m_val, @@ -635,7 +639,7 @@ void Module::Interface::GetTrimmingParams(Kernel::HLERequestContext& ctx) { rb.Push(cam->ports[port].y1); } else { LOG_ERROR(Service_CAM, "invalid port_select={}", port_select.m_val); - rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Push(ResultInvalidEnumValue); rb.Skip(4, false); } @@ -661,7 +665,7 @@ void Module::Interface::SetTrimmingParamsCenter(Kernel::HLERequestContext& ctx) rb.Push(ResultSuccess); } else { LOG_ERROR(Service_CAM, "invalid port_select={}", port_select.m_val); - rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Push(ResultInvalidEnumValue); } LOG_DEBUG(Service_CAM, "called, port_select={}, trim_w={}, trim_h={}, cam_w={}, cam_h={}", @@ -687,7 +691,7 @@ void Module::Interface::Activate(Kernel::HLERequestContext& ctx) { rb.Push(ResultSuccess); } else if (camera_select[0] && camera_select[1]) { LOG_ERROR(Service_CAM, "camera 0 and 1 can't be both activated"); - rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Push(ResultInvalidEnumValue); } else { if (camera_select[0]) { cam->ActivatePort(0, 0); @@ -702,7 +706,7 @@ void Module::Interface::Activate(Kernel::HLERequestContext& ctx) { } } else { LOG_ERROR(Service_CAM, "invalid camera_select={}", camera_select.m_val); - rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Push(ResultInvalidEnumValue); } LOG_DEBUG(Service_CAM, "called, camera_select={}", camera_select.m_val); @@ -728,7 +732,7 @@ void Module::Interface::SwitchContext(Kernel::HLERequestContext& ctx) { } else { LOG_ERROR(Service_CAM, "invalid camera_select={}, context_select={}", camera_select.m_val, context_select.m_val); - rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Push(ResultInvalidEnumValue); } LOG_DEBUG(Service_CAM, "called, camera_select={}, context_select={}", camera_select.m_val, @@ -738,24 +742,29 @@ void Module::Interface::SwitchContext(Kernel::HLERequestContext& ctx) { void Module::Interface::FlipImage(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); const CameraSet camera_select(rp.Pop()); - const Flip flip = static_cast(rp.Pop()); + const Flip flip = rp.PopEnum(); const ContextSet context_select(rp.Pop()); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (camera_select.IsValid() && context_select.IsValid()) { - for (int camera : camera_select) { + for (int index : camera_select) { + auto& camera = cam->cameras[index]; for (int context : context_select) { - cam->cameras[camera].contexts[context].flip = flip; - if (cam->cameras[camera].current_context == context) { - cam->cameras[camera].impl->SetFlip(flip); + camera.contexts[context].flip = flip; + if (camera.current_context != context) { + continue; } + if (!camera.impl) { + cam->LoadCameraImplementation(camera, index); + } + camera.impl->SetFlip(flip); } } rb.Push(ResultSuccess); } else { LOG_ERROR(Service_CAM, "invalid camera_select={}, context_select={}", camera_select.m_val, context_select.m_val); - rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Push(ResultInvalidEnumValue); } LOG_DEBUG(Service_CAM, "called, camera_select={}, flip={}, context_select={}", @@ -776,19 +785,24 @@ void Module::Interface::SetDetailSize(Kernel::HLERequestContext& ctx) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (camera_select.IsValid() && context_select.IsValid()) { - for (int camera : camera_select) { + for (int index : camera_select) { + auto& camera = cam->cameras[index]; for (int context : context_select) { - cam->cameras[camera].contexts[context].resolution = resolution; - if (cam->cameras[camera].current_context == context) { - cam->cameras[camera].impl->SetResolution(resolution); + camera.contexts[context].resolution = resolution; + if (camera.current_context != context) { + continue; } + if (!camera.impl) { + cam->LoadCameraImplementation(camera, index); + } + camera.impl->SetResolution(resolution); } } rb.Push(ResultSuccess); } else { LOG_ERROR(Service_CAM, "invalid camera_select={}, context_select={}", camera_select.m_val, context_select.m_val); - rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Push(ResultInvalidEnumValue); } LOG_DEBUG(Service_CAM, @@ -806,19 +820,24 @@ void Module::Interface::SetSize(Kernel::HLERequestContext& ctx) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (camera_select.IsValid() && context_select.IsValid()) { - for (int camera : camera_select) { + for (int index : camera_select) { + auto& camera = cam->cameras[index]; for (int context : context_select) { - cam->cameras[camera].contexts[context].resolution = PRESET_RESOLUTION[size]; - if (cam->cameras[camera].current_context == context) { - cam->cameras[camera].impl->SetResolution(PRESET_RESOLUTION[size]); + camera.contexts[context].resolution = PRESET_RESOLUTION[size]; + if (camera.current_context != context) { + continue; } + if (!camera.impl) { + cam->LoadCameraImplementation(camera, index); + } + camera.impl->SetResolution(PRESET_RESOLUTION[size]); } } rb.Push(ResultSuccess); } else { LOG_ERROR(Service_CAM, "invalid camera_select={}, context_select={}", camera_select.m_val, context_select.m_val); - rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Push(ResultInvalidEnumValue); } LOG_DEBUG(Service_CAM, "called, camera_select={}, size={}, context_select={}", @@ -828,18 +847,22 @@ void Module::Interface::SetSize(Kernel::HLERequestContext& ctx) { void Module::Interface::SetFrameRate(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); const CameraSet camera_select(rp.Pop()); - const FrameRate frame_rate = static_cast(rp.Pop()); + const FrameRate frame_rate = rp.PopEnum(); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (camera_select.IsValid()) { - for (int camera : camera_select) { - cam->cameras[camera].frame_rate = frame_rate; - cam->cameras[camera].impl->SetFrameRate(frame_rate); + for (int index : camera_select) { + auto& camera = cam->cameras[index]; + camera.frame_rate = frame_rate; + if (!camera.impl) { + cam->LoadCameraImplementation(camera, index); + } + camera.impl->SetFrameRate(frame_rate); } rb.Push(ResultSuccess); } else { LOG_ERROR(Service_CAM, "invalid camera_select={}", camera_select.m_val); - rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Push(ResultInvalidEnumValue); } LOG_WARNING(Service_CAM, "(STUBBED) called, camera_select={}, frame_rate={}", @@ -849,24 +872,29 @@ void Module::Interface::SetFrameRate(Kernel::HLERequestContext& ctx) { void Module::Interface::SetEffect(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); const CameraSet camera_select(rp.Pop()); - const Effect effect = static_cast(rp.Pop()); + const Effect effect = rp.PopEnum(); const ContextSet context_select(rp.Pop()); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (camera_select.IsValid() && context_select.IsValid()) { - for (int camera : camera_select) { + for (int index : camera_select) { + auto& camera = cam->cameras[index]; for (int context : context_select) { - cam->cameras[camera].contexts[context].effect = effect; - if (cam->cameras[camera].current_context == context) { - cam->cameras[camera].impl->SetEffect(effect); + camera.contexts[context].effect = effect; + if (camera.current_context != context) { + continue; } + if (!camera.impl) { + cam->LoadCameraImplementation(camera, index); + } + camera.impl->SetEffect(effect); } } rb.Push(ResultSuccess); } else { LOG_ERROR(Service_CAM, "invalid camera_select={}, context_select={}", camera_select.m_val, context_select.m_val); - rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Push(ResultInvalidEnumValue); } LOG_DEBUG(Service_CAM, "called, camera_select={}, effect={}, context_select={}", @@ -876,24 +904,29 @@ void Module::Interface::SetEffect(Kernel::HLERequestContext& ctx) { void Module::Interface::SetOutputFormat(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx); const CameraSet camera_select(rp.Pop()); - const OutputFormat format = static_cast(rp.Pop()); + const OutputFormat format = rp.PopEnum(); const ContextSet context_select(rp.Pop()); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (camera_select.IsValid() && context_select.IsValid()) { - for (int camera : camera_select) { + for (int index : camera_select) { + auto& camera = cam->cameras[index]; for (int context : context_select) { - cam->cameras[camera].contexts[context].format = format; - if (cam->cameras[camera].current_context == context) { - cam->cameras[camera].impl->SetFormat(format); + camera.contexts[context].format = format; + if (camera.current_context != context) { + continue; } + if (!camera.impl) { + cam->LoadCameraImplementation(camera, index); + } + camera.impl->SetFormat(format); } } rb.Push(ResultSuccess); } else { LOG_ERROR(Service_CAM, "invalid camera_select={}, context_select={}", camera_select.m_val, context_select.m_val); - rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Push(ResultInvalidEnumValue); } LOG_DEBUG(Service_CAM, "called, camera_select={}, format={}, context_select={}", @@ -919,7 +952,7 @@ void Module::Interface::GetLatestVsyncTiming(Kernel::HLERequestContext& ctx) { if (!port_select.IsSingle() || count > MaxVsyncTimings) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); - rb.Push(ERROR_OUT_OF_RANGE); + rb.Push(ResultOutOfRange); rb.PushStaticBuffer({}, 0); return; } @@ -1003,7 +1036,7 @@ Result Module::SetPackageParameter(const PackageParameterType& package) { } else { LOG_ERROR(Service_CAM, "invalid camera_select={}, context_select={}", package.camera_select, package.context_select); - return ERROR_INVALID_ENUM_VALUE; + return ResultInvalidEnumValue; } }