diff options
Diffstat (limited to 'src/core/hle')
39 files changed, 2252 insertions, 517 deletions
diff --git a/src/core/hle/applets/applet.cpp b/src/core/hle/applets/applet.cpp index 645b2d5fe..9c43ed2fd 100644 --- a/src/core/hle/applets/applet.cpp +++ b/src/core/hle/applets/applet.cpp @@ -12,6 +12,7 @@ #include "core/hle/applets/applet.h" #include "core/hle/applets/erreula.h" #include "core/hle/applets/mii_selector.h" +#include "core/hle/applets/mint.h" #include "core/hle/applets/swkbd.h" #include "core/hle/result.h" #include "core/hle/service/apt/apt.h" @@ -56,6 +57,10 @@ ResultCode Applet::Create(Service::APT::AppletId id) { case Service::APT::AppletId::Error2: applets[id] = std::make_shared<ErrEula>(id); break; + case Service::APT::AppletId::Mint: + case Service::APT::AppletId::Mint2: + applets[id] = std::make_shared<Mint>(id); + break; default: LOG_ERROR(Service_APT, "Could not create applet %u", id); // TODO(Subv): Find the right error code diff --git a/src/core/hle/applets/mint.cpp b/src/core/hle/applets/mint.cpp new file mode 100644 index 000000000..31a79ea17 --- /dev/null +++ b/src/core/hle/applets/mint.cpp @@ -0,0 +1,72 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/string_util.h" +#include "core/hle/applets/mint.h" +#include "core/hle/service/apt/apt.h" + +namespace HLE { +namespace Applets { + +ResultCode Mint::ReceiveParameter(const Service::APT::MessageParameter& parameter) { + if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) { + LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); + UNIMPLEMENTED(); + // TODO(Subv): Find the right error code + return ResultCode(-1); + } + + // The Request message contains a buffer with the size of the framebuffer shared + // memory. + // Create the SharedMemory that will hold the framebuffer data + Service::APT::CaptureBufferInfo capture_info; + ASSERT(sizeof(capture_info) == parameter.buffer.size()); + + memcpy(&capture_info, parameter.buffer.data(), sizeof(capture_info)); + + // TODO: allocated memory never released + using Kernel::MemoryPermission; + // Allocate a heap block of the required size for this applet. + heap_memory = std::make_shared<std::vector<u8>>(capture_info.size); + // Create a SharedMemory that directly points to this heap block. + framebuffer_memory = Kernel::SharedMemory::CreateForApplet( + heap_memory, 0, heap_memory->size(), MemoryPermission::ReadWrite, + MemoryPermission::ReadWrite, "Mint Memory"); + + // Send the response message with the newly created SharedMemory + Service::APT::MessageParameter result; + result.signal = static_cast<u32>(Service::APT::SignalType::Response); + result.buffer.clear(); + result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); + result.sender_id = static_cast<u32>(id); + result.object = framebuffer_memory; + + Service::APT::SendParameter(result); + return RESULT_SUCCESS; +} + +ResultCode Mint::StartImpl(const Service::APT::AppletStartupParameter& parameter) { + is_running = true; + + // TODO(Subv): Set the expected fields in the response buffer before resending it to the + // application. + // TODO(Subv): Reverse the parameter format for the Mint applet + + // Let the application know that we're closing + Service::APT::MessageParameter message; + message.buffer.resize(parameter.buffer.size()); + std::fill(message.buffer.begin(), message.buffer.end(), 0); + message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit); + message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); + message.sender_id = static_cast<u32>(id); + Service::APT::SendParameter(message); + + is_running = false; + return RESULT_SUCCESS; +} + +void Mint::Update() {} + +} // namespace Applets +} // namespace HLE diff --git a/src/core/hle/applets/mint.h b/src/core/hle/applets/mint.h new file mode 100644 index 000000000..d23dc40f9 --- /dev/null +++ b/src/core/hle/applets/mint.h @@ -0,0 +1,29 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/applets/applet.h" +#include "core/hle/kernel/shared_memory.h" + +namespace HLE { +namespace Applets { + +class Mint final : public Applet { +public: + explicit Mint(Service::APT::AppletId id) : Applet(id) {} + + ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override; + ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override; + void Update() override; + +private: + /// This SharedMemory will be created when we receive the Request message. + /// It holds the framebuffer info retrieved by the application with + /// GSPGPU::ImportDisplayCaptureInfo + Kernel::SharedPtr<Kernel::SharedMemory> framebuffer_memory; +}; + +} // namespace Applets +} // namespace HLE diff --git a/src/core/hle/config_mem.cpp b/src/core/hle/config_mem.cpp index ccd73cfcb..e386ccdc6 100644 --- a/src/core/hle/config_mem.cpp +++ b/src/core/hle/config_mem.cpp @@ -14,15 +14,18 @@ ConfigMemDef config_mem; void Init() { std::memset(&config_mem, 0, sizeof(config_mem)); - config_mem.update_flag = 0; // No update + // Values extracted from firmware 11.2.0-35E + config_mem.kernel_version_min = 0x34; + config_mem.kernel_version_maj = 0x2; + config_mem.ns_tid = 0x0004013000008002; config_mem.sys_core_ver = 0x2; config_mem.unit_info = 0x1; // Bit 0 set for Retail - config_mem.prev_firm = 0; - config_mem.firm_unk = 0; - config_mem.firm_version_rev = 0; - config_mem.firm_version_min = 0x40; + config_mem.prev_firm = 0x1; + config_mem.ctr_sdk_ver = 0x0000F297; + config_mem.firm_version_min = 0x34; config_mem.firm_version_maj = 0x2; config_mem.firm_sys_core_ver = 0x2; + config_mem.firm_ctr_sdk_ver = 0x0000F297; } } // namespace diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index 4e094faa7..cd9a5863d 100644 --- a/src/core/hle/ipc.h +++ b/src/core/hle/ipc.h @@ -18,12 +18,26 @@ static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of * the thread's TLS to an intermediate buffer in kernel memory, and then copied again to * the service handler process' memory. * @param offset Optional offset into command buffer + * @param offset Optional offset into command buffer (in bytes) * @return Pointer to command buffer */ inline u32* GetCommandBuffer(const int offset = 0) { return (u32*)Memory::GetPointer(GetCurrentThread()->GetTLSAddress() + kCommandHeaderOffset + offset); } + +static const int kStaticBuffersOffset = + 0x100; ///< Offset into static buffers, relative to command buffer header + +/** + * Returns a pointer to the static buffers area in the current thread's TLS + * TODO(Subv): cf. GetCommandBuffer + * @param offset Optional offset into static buffers area (in bytes) + * @return Pointer to static buffers area + */ +inline u32* GetStaticBuffers(const int offset = 0) { + return GetCommandBuffer(kStaticBuffersOffset + offset); +} } namespace IPC { @@ -40,10 +54,17 @@ enum DescriptorType : u32 { CallingPid = 0x20, }; +union Header { + u32 raw; + BitField<0, 6, u32> translate_params_size; + BitField<6, 6, u32> normal_params_size; + BitField<16, 16, u32> command_id; +}; + /** * @brief Creates a command header to be used for IPC * @param command_id ID of the command to create a header for. - * @param normal_params Size of the normal parameters in words. Up to 63. + * @param normal_params_size Size of the normal parameters in words. Up to 63. * @param translate_params_size Size of the translate parameters in words. Up to 63. * @return The created IPC header. * @@ -51,24 +72,16 @@ enum DescriptorType : u32 { * through modifications and checks by the kernel. * The translate parameters are described by headers generated with the IPC::*Desc functions. * - * @note While #normal_params is equivalent to the number of normal parameters, - * #translate_params_size includes the size occupied by the translate parameters headers. + * @note While @p normal_params_size is equivalent to the number of normal parameters, + * @p translate_params_size includes the size occupied by the translate parameters headers. */ -constexpr u32 MakeHeader(u16 command_id, unsigned int normal_params, - unsigned int translate_params_size) { - return (u32(command_id) << 16) | ((u32(normal_params) & 0x3F) << 6) | - (u32(translate_params_size) & 0x3F); -} - -union Header { - u32 raw; - BitField<0, 6, u32> translate_params_size; - BitField<6, 6, u32> normal_params; - BitField<16, 16, u32> command_id; -}; - -inline Header ParseHeader(u32 header) { - return {header}; +inline u32 MakeHeader(u16 command_id, unsigned int normal_params_size, + unsigned int translate_params_size) { + Header header{}; + header.command_id.Assign(command_id); + header.normal_params_size.Assign(normal_params_size); + header.translate_params_size.Assign(translate_params_size); + return header.raw; } constexpr u32 MoveHandleDesc(u32 num_handles = 1) { @@ -83,7 +96,7 @@ constexpr u32 CallingPidDesc() { return CallingPid; } -constexpr bool isHandleDescriptor(u32 descriptor) { +constexpr bool IsHandleDescriptor(u32 descriptor) { return (descriptor & 0xF) == 0x0; } @@ -91,18 +104,19 @@ constexpr u32 HandleNumberFromDesc(u32 handle_descriptor) { return (handle_descriptor >> 26) + 1; } -constexpr u32 StaticBufferDesc(u32 size, u8 buffer_id) { - return StaticBuffer | (size << 14) | ((buffer_id & 0xF) << 10); -} - union StaticBufferDescInfo { u32 raw; + BitField<0, 4, u32> descriptor_type; BitField<10, 4, u32> buffer_id; BitField<14, 18, u32> size; }; -inline StaticBufferDescInfo ParseStaticBufferDesc(const u32 desc) { - return {desc}; +inline u32 StaticBufferDesc(u32 size, u8 buffer_id) { + StaticBufferDescInfo info{}; + info.descriptor_type.Assign(StaticBuffer); + info.buffer_id.Assign(buffer_id); + info.size.Assign(size); + return info.raw; } /** @@ -122,29 +136,30 @@ inline u32 PXIBufferDesc(u32 size, unsigned buffer_id, bool is_read_only) { return type | (size << 8) | ((buffer_id & 0xF) << 4); } -enum MappedBufferPermissions { +enum MappedBufferPermissions : u32 { R = 1, W = 2, RW = R | W, }; -constexpr u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) { - return MappedBuffer | (size << 4) | (u32(perms) << 1); -} - union MappedBufferDescInfo { u32 raw; - BitField<4, 28, u32> size; + BitField<0, 4, u32> flags; BitField<1, 2, MappedBufferPermissions> perms; + BitField<4, 28, u32> size; }; -inline MappedBufferDescInfo ParseMappedBufferDesc(const u32 desc) { - return {desc}; +inline u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) { + MappedBufferDescInfo info{}; + info.flags.Assign(MappedBuffer); + info.perms.Assign(perms); + info.size.Assign(size); + return info.raw; } inline DescriptorType GetDescriptorType(u32 descriptor) { // Note: Those checks must be done in this order - if (isHandleDescriptor(descriptor)) + if (IsHandleDescriptor(descriptor)) return (DescriptorType)(descriptor & 0x30); // handle the fact that the following descriptors can have rights diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h new file mode 100644 index 000000000..323158bb5 --- /dev/null +++ b/src/core/hle/ipc_helpers.h @@ -0,0 +1,275 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once +#include "core/hle/ipc.h" +#include "core/hle/kernel/kernel.h" + +namespace IPC { + +class RequestHelperBase { +protected: + u32* cmdbuf; + ptrdiff_t index = 1; + Header header; + +public: + RequestHelperBase(u32* command_buffer, Header command_header) + : cmdbuf(command_buffer), header(command_header) {} + + /// Returns the total size of the request in words + size_t TotalSize() const { + return 1 /* command header */ + header.normal_params_size + header.translate_params_size; + } + + void ValidateHeader() { + DEBUG_ASSERT_MSG(index == TotalSize(), "Operations do not match the header (cmd 0x%x)", + header.raw); + } +}; + +class RequestBuilder : public RequestHelperBase { +public: + RequestBuilder(u32* command_buffer, Header command_header) + : RequestHelperBase(command_buffer, command_header) { + cmdbuf[0] = header.raw; + } + explicit RequestBuilder(u32* command_buffer, u32 command_header) + : RequestBuilder(command_buffer, Header{command_header}) {} + RequestBuilder(u32* command_buffer, u16 command_id, unsigned normal_params_size, + unsigned translate_params_size) + : RequestBuilder(command_buffer, + MakeHeader(command_id, normal_params_size, translate_params_size)) {} + + // Validate on destruction, as there shouldn't be any case where we don't want it + ~RequestBuilder() { + ValidateHeader(); + } + + template <typename T> + void Push(T value); + + void Push(u32 value) { + cmdbuf[index++] = value; + } + template <typename First, typename... Other> + void Push(const First& first_value, const Other&... other_values) { + Push(first_value); + Push(other_values...); + } + + /** + * @brief Copies the content of the given trivially copyable class to the buffer as a normal + * param + * @note: The input class must be correctly packed/padded to fit hardware layout. + */ + template <typename T> + void PushRaw(const T& value) { + static_assert(std::is_trivially_copyable<T>(), "Raw types should be trivially copyable"); + std::memcpy(cmdbuf + index, &value, sizeof(T)); + index += (sizeof(T) + 3) / 4; // round up to word length + } + + // TODO : ensure that translate params are added after all regular params + template <typename... H> + void PushCopyHandles(H... handles) { + Push(CopyHandleDesc(sizeof...(H))); + Push(static_cast<Kernel::Handle>(handles)...); + } + + template <typename... H> + void PushMoveHandles(H... handles) { + Push(MoveHandleDesc(sizeof...(H))); + Push(static_cast<Kernel::Handle>(handles)...); + } + + void PushCurrentPIDHandle() { + Push(CallingPidDesc()); + Push(u32(0)); + } + + void PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id) { + Push(StaticBufferDesc(size, buffer_id)); + Push(buffer_vaddr); + } + + void PushMappedBuffer(VAddr buffer_vaddr, u32 size, MappedBufferPermissions perms) { + Push(MappedBufferDesc(size, perms)); + Push(buffer_vaddr); + } +}; + +/// Push /// + +template <> +inline void RequestBuilder::Push<u32>(u32 value) { + Push(value); +} + +template <> +inline void RequestBuilder::Push<u64>(u64 value) { + Push(static_cast<u32>(value)); + Push(static_cast<u32>(value >> 32)); +} + +template <> +inline void RequestBuilder::Push<ResultCode>(ResultCode value) { + Push(value.raw); +} + +class RequestParser : public RequestHelperBase { +public: + RequestParser(u32* command_buffer, Header command_header) + : RequestHelperBase(command_buffer, command_header) {} + explicit RequestParser(u32* command_buffer, u32 command_header) + : RequestParser(command_buffer, Header{command_header}) {} + RequestParser(u32* command_buffer, u16 command_id, unsigned normal_params_size, + unsigned translate_params_size) + : RequestParser(command_buffer, + MakeHeader(command_id, normal_params_size, translate_params_size)) {} + + RequestBuilder MakeBuilder(u32 normal_params_size, u32 translate_params_size, + bool validateHeader = true) { + if (validateHeader) + ValidateHeader(); + Header builderHeader{ + MakeHeader(header.command_id, normal_params_size, translate_params_size)}; + return {cmdbuf, builderHeader}; + } + + template <typename T> + T Pop(); + + template <typename T> + void Pop(T& value); + + template <typename First, typename... Other> + void Pop(First& first_value, Other&... other_values); + + Kernel::Handle PopHandle(); + + template <typename... H> + void PopHandles(H&... handles); + + /** + * @brief Pops the static buffer vaddr + * @return The virtual address of the buffer + * @param[out] data_size If non-null, the pointed value will be set to the size of the data + * @param[out] useStaticBuffersToGetVaddr Indicates if we should read the vaddr from the static + * buffers (which is the correct thing to do, but no service presently implement it) instead of + * using the same value as the process who sent the request + * given by the source process + * + * Static buffers must be set up before any IPC request using those is sent. + * It is the duty of the process (usually services) to allocate and set up the receiving static + * buffer information + * Please note that the setup uses virtual addresses. + */ + VAddr PopStaticBuffer(size_t* data_size = nullptr, bool useStaticBuffersToGetVaddr = false); + + /** + * @brief Pops the mapped buffer vaddr + * @return The virtual address of the buffer + * @param[out] data_size If non-null, the pointed value will be set to the size of the data + * given by the source process + * @param[out] buffer_perms If non-null, the pointed value will be set to the permissions of the + * buffer + */ + VAddr PopMappedBuffer(size_t* data_size = nullptr, + MappedBufferPermissions* buffer_perms = nullptr); + + /** + * @brief Reads the next normal parameters as a struct, by copying it + * @note: The output class must be correctly packed/padded to fit hardware layout. + */ + template <typename T> + void PopRaw(T& value); +}; + +/// Pop /// + +template <> +inline u32 RequestParser::Pop<u32>() { + return cmdbuf[index++]; +} + +template <> +inline u64 RequestParser::Pop<u64>() { + const u64 lsw = Pop<u32>(); + const u64 msw = Pop<u32>(); + return msw << 32 | lsw; +} + +template <> +inline ResultCode RequestParser::Pop<ResultCode>() { + return ResultCode{Pop<u32>()}; +} + +template <typename T> +void RequestParser::Pop(T& value) { + value = Pop<T>(); +} + +template <typename First, typename... Other> +void RequestParser::Pop(First& first_value, Other&... other_values) { + first_value = Pop<First>(); + Pop(other_values...); +} + +inline Kernel::Handle RequestParser::PopHandle() { + const u32 handle_descriptor = Pop<u32>(); + DEBUG_ASSERT_MSG(IsHandleDescriptor(handle_descriptor), + "Tried to pop handle(s) but the descriptor is not a handle descriptor"); + DEBUG_ASSERT_MSG(HandleNumberFromDesc(handle_descriptor) == 1, + "Descriptor indicates that there isn't exactly one handle"); + return Pop<Kernel::Handle>(); +} + +template <typename... H> +void RequestParser::PopHandles(H&... handles) { + const u32 handle_descriptor = Pop<u32>(); + const int handles_number = sizeof...(H); + DEBUG_ASSERT_MSG(IsHandleDescriptor(handle_descriptor), + "Tried to pop handle(s) but the descriptor is not a handle descriptor"); + DEBUG_ASSERT_MSG(handles_number == HandleNumberFromDesc(handle_descriptor), + "Number of handles doesn't match the descriptor"); + Pop(static_cast<Kernel::Handle&>(handles)...); +} + +inline VAddr RequestParser::PopStaticBuffer(size_t* data_size, bool useStaticBuffersToGetVaddr) { + const u32 sbuffer_descriptor = Pop<u32>(); + StaticBufferDescInfo bufferInfo{sbuffer_descriptor}; + if (data_size != nullptr) + *data_size = bufferInfo.size; + if (!useStaticBuffersToGetVaddr) + return Pop<VAddr>(); + else { + ASSERT_MSG(0, "remove the assert if multiprocess/IPC translation are implemented."); + // The buffer has already been copied to the static buffer by the kernel during + // translation + Pop<VAddr>(); // Pop the calling process buffer address + // and get the vaddr from the static buffers + return cmdbuf[(0x100 >> 2) + bufferInfo.buffer_id * 2 + 1]; + } +} + +inline VAddr RequestParser::PopMappedBuffer(size_t* data_size, + MappedBufferPermissions* buffer_perms) { + const u32 sbuffer_descriptor = Pop<u32>(); + MappedBufferDescInfo bufferInfo{sbuffer_descriptor}; + if (data_size != nullptr) + *data_size = bufferInfo.size; + if (buffer_perms != nullptr) + *buffer_perms = bufferInfo.perms; + return Pop<VAddr>(); +} + +template <typename T> +void RequestParser::PopRaw(T& value) { + static_assert(std::is_trivially_copyable<T>(), "Raw types should be trivially copyable"); + std::memcpy(&value, cmdbuf + index, sizeof(T)); + index += (sizeof(T) + 3) / 4; // round up to word length +} + +} // namespace IPC diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index c088b9a19..761fc4781 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -4,6 +4,7 @@ #pragma once +#include <memory> #include <string> #include "common/assert.h" #include "common/common_types.h" @@ -44,7 +45,8 @@ public: /** * Creates a pair of ServerSession and an associated ClientSession. - * @param name Optional name of the ports + * @param name Optional name of the ports. + * @param hle_handler Optional HLE handler for this server session. * @return The created session tuple */ static SessionPair CreateSessionPair( diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index c557a2279..6ab31c70b 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -11,7 +11,6 @@ #include <boost/container/flat_set.hpp> #include "common/common_types.h" #include "core/arm/arm_interface.h" -#include "core/core.h" #include "core/hle/kernel/kernel.h" #include "core/hle/result.h" diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index 60537f355..a00c75679 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -52,9 +52,14 @@ void Timer::Set(s64 initial, s64 interval) { initial_delay = initial; interval_delay = interval; - u64 initial_microseconds = initial / 1000; - CoreTiming::ScheduleEvent(usToCycles(initial_microseconds), timer_callback_event_type, - callback_handle); + if (initial == 0) { + // Immediately invoke the callback + Signal(0); + } else { + u64 initial_microseconds = initial / 1000; + CoreTiming::ScheduleEvent(usToCycles(initial_microseconds), timer_callback_event_type, + callback_handle); + } } void Timer::Cancel() { @@ -72,6 +77,22 @@ void Timer::WakeupAllWaitingThreads() { signaled = false; } +void Timer::Signal(int cycles_late) { + LOG_TRACE(Kernel, "Timer %u fired", GetObjectId()); + + signaled = true; + + // Resume all waiting threads + WakeupAllWaitingThreads(); + + if (interval_delay != 0) { + // Reschedule the timer with the interval delay + u64 interval_microseconds = interval_delay / 1000; + CoreTiming::ScheduleEvent(usToCycles(interval_microseconds) - cycles_late, + timer_callback_event_type, callback_handle); + } +} + /// The timer callback event, called when a timer is fired static void TimerCallback(u64 timer_handle, int cycles_late) { SharedPtr<Timer> timer = @@ -82,19 +103,7 @@ static void TimerCallback(u64 timer_handle, int cycles_late) { return; } - LOG_TRACE(Kernel, "Timer %08" PRIx64 " fired", timer_handle); - - timer->signaled = true; - - // Resume all waiting threads - timer->WakeupAllWaitingThreads(); - - if (timer->interval_delay != 0) { - // Reschedule the timer with the interval delay - u64 interval_microseconds = timer->interval_delay / 1000; - CoreTiming::ScheduleEvent(usToCycles(interval_microseconds) - cycles_late, - timer_callback_event_type, timer_handle); - } + timer->Signal(cycles_late); } void TimersInit() { diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h index c174f5664..b0f818933 100644 --- a/src/core/hle/kernel/timer.h +++ b/src/core/hle/kernel/timer.h @@ -54,6 +54,14 @@ public: void Cancel(); void Clear(); + /** + * Signals the timer, waking up any waiting threads and rescheduling it + * for the next interval. + * This method should not be called from outside the timer callback handler, + * lest multiple callback events get scheduled. + */ + void Signal(int cycles_late); + private: Timer(); ~Timer() override; diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 53864a3a7..cfefbbc64 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -20,6 +20,7 @@ enum class ErrorDescription : u32 { OS_InvalidBufferDescriptor = 48, MaxConnectionsReached = 52, WrongAddress = 53, + FS_RomFSNotFound = 100, FS_ArchiveNotMounted = 101, FS_FileNotFound = 112, FS_PathNotFound = 113, @@ -35,10 +36,13 @@ enum class ErrorDescription : u32 { OutofRangeOrMisalignedAddress = 513, // TODO(purpasmart): Check if this name fits its actual usage GPU_FirstInitialization = 519, + FS_ExeFSSectionNotFound = 567, + FS_CommandNotAllowed = 630, FS_InvalidReadFlag = 700, FS_InvalidPath = 702, FS_WriteBeyondEnd = 705, FS_UnsupportedOpenFlags = 760, + FS_IncorrectExeFSReadSize = 761, FS_UnexpectedFileOrDirectory = 770, InvalidSection = 1000, TooLarge = 1001, diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 615fe31ea..1517d3a2f 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -18,6 +18,8 @@ #include "core/hle/service/fs/archive.h" #include "core/hle/service/ptm/ptm.h" #include "core/hle/service/service.h" +#include "core/hw/aes/ccm.h" +#include "core/hw/aes/key.h" namespace Service { namespace APT { @@ -470,6 +472,107 @@ void GetStartupArgument(Service::Interface* self) { cmd_buff[2] = 0; } +void Wrap(Service::Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x46, 4, 4); + const u32 output_size = rp.Pop<u32>(); + const u32 input_size = rp.Pop<u32>(); + const u32 nonce_offset = rp.Pop<u32>(); + u32 nonce_size = rp.Pop<u32>(); + size_t desc_size; + IPC::MappedBufferPermissions desc_permission; + const VAddr input = rp.PopMappedBuffer(&desc_size, &desc_permission); + ASSERT(desc_size == input_size && desc_permission == IPC::MappedBufferPermissions::R); + const VAddr output = rp.PopMappedBuffer(&desc_size, &desc_permission); + ASSERT(desc_size == output_size && desc_permission == IPC::MappedBufferPermissions::W); + + // Note: real 3DS still returns SUCCESS when the sizes don't match. It seems that it doesn't + // check the buffer size and writes data with potential overflow. + ASSERT_MSG(output_size == input_size + HW::AES::CCM_MAC_SIZE, + "input_size (%d) doesn't match to output_size (%d)", input_size, output_size); + + LOG_DEBUG(Service_APT, "called, output_size=%u, input_size=%u, nonce_offset=%u, nonce_size=%u", + output_size, input_size, nonce_offset, nonce_size); + + // Note: This weird nonce size modification is verified against real 3DS + nonce_size = std::min<u32>(nonce_size & ~3, HW::AES::CCM_NONCE_SIZE); + + // Reads nonce and concatenates the rest of the input as plaintext + HW::AES::CCMNonce nonce{}; + Memory::ReadBlock(input + nonce_offset, nonce.data(), nonce_size); + u32 pdata_size = input_size - nonce_size; + std::vector<u8> pdata(pdata_size); + Memory::ReadBlock(input, pdata.data(), nonce_offset); + Memory::ReadBlock(input + nonce_offset + nonce_size, pdata.data() + nonce_offset, + pdata_size - nonce_offset); + + // Encrypts the plaintext using AES-CCM + auto cipher = HW::AES::EncryptSignCCM(pdata, nonce, HW::AES::KeySlotID::APTWrap); + + // Puts the nonce to the beginning of the output, with ciphertext followed + Memory::WriteBlock(output, nonce.data(), nonce_size); + Memory::WriteBlock(output + nonce_size, cipher.data(), cipher.size()); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 4); + rb.Push(RESULT_SUCCESS); + + // Unmap buffer + rb.PushMappedBuffer(input, input_size, IPC::MappedBufferPermissions::R); + rb.PushMappedBuffer(output, output_size, IPC::MappedBufferPermissions::W); +} + +void Unwrap(Service::Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x47, 4, 4); + const u32 output_size = rp.Pop<u32>(); + const u32 input_size = rp.Pop<u32>(); + const u32 nonce_offset = rp.Pop<u32>(); + u32 nonce_size = rp.Pop<u32>(); + size_t desc_size; + IPC::MappedBufferPermissions desc_permission; + const VAddr input = rp.PopMappedBuffer(&desc_size, &desc_permission); + ASSERT(desc_size == input_size && desc_permission == IPC::MappedBufferPermissions::R); + const VAddr output = rp.PopMappedBuffer(&desc_size, &desc_permission); + ASSERT(desc_size == output_size && desc_permission == IPC::MappedBufferPermissions::W); + + // Note: real 3DS still returns SUCCESS when the sizes don't match. It seems that it doesn't + // check the buffer size and writes data with potential overflow. + ASSERT_MSG(output_size == input_size - HW::AES::CCM_MAC_SIZE, + "input_size (%d) doesn't match to output_size (%d)", input_size, output_size); + + LOG_DEBUG(Service_APT, "called, output_size=%u, input_size=%u, nonce_offset=%u, nonce_size=%u", + output_size, input_size, nonce_offset, nonce_size); + + // Note: This weird nonce size modification is verified against real 3DS + nonce_size = std::min<u32>(nonce_size & ~3, HW::AES::CCM_NONCE_SIZE); + + // Reads nonce and cipher text + HW::AES::CCMNonce nonce{}; + Memory::ReadBlock(input, nonce.data(), nonce_size); + u32 cipher_size = input_size - nonce_size; + std::vector<u8> cipher(cipher_size); + Memory::ReadBlock(input + nonce_size, cipher.data(), cipher_size); + + // Decrypts the ciphertext using AES-CCM + auto pdata = HW::AES::DecryptVerifyCCM(cipher, nonce, HW::AES::KeySlotID::APTWrap); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 4); + if (!pdata.empty()) { + // Splits the plaintext and put the nonce in between + Memory::WriteBlock(output, pdata.data(), nonce_offset); + Memory::WriteBlock(output + nonce_offset, nonce.data(), nonce_size); + Memory::WriteBlock(output + nonce_offset + nonce_size, pdata.data() + nonce_offset, + pdata.size() - nonce_offset); + rb.Push(RESULT_SUCCESS); + } else { + LOG_ERROR(Service_APT, "Failed to decrypt data"); + rb.Push(ResultCode(static_cast<ErrorDescription>(1), ErrorModule::PS, + ErrorSummary::WrongArgument, ErrorLevel::Status)); + } + + // Unmap buffer + rb.PushMappedBuffer(input, input_size, IPC::MappedBufferPermissions::R); + rb.PushMappedBuffer(output, output_size, IPC::MappedBufferPermissions::W); +} + void CheckNew3DSApp(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index 80325361f..e63b61450 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -137,6 +137,46 @@ void Initialize(Service::Interface* self); void GetSharedFont(Service::Interface* self); /** + * APT::Wrap service function + * Inputs: + * 1 : Output buffer size + * 2 : Input buffer size + * 3 : Nonce offset to the input buffer + * 4 : Nonce size + * 5 : Buffer mapping descriptor ((input_buffer_size << 4) | 0xA) + * 6 : Input buffer address + * 7 : Buffer mapping descriptor ((input_buffer_size << 4) | 0xC) + * 8 : Output buffer address + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Buffer unmapping descriptor ((input_buffer_size << 4) | 0xA) + * 3 : Input buffer address + * 4 : Buffer unmapping descriptor ((input_buffer_size << 4) | 0xC) + * 5 : Output buffer address + */ +void Wrap(Service::Interface* self); + +/** + * APT::Unwrap service function + * Inputs: + * 1 : Output buffer size + * 2 : Input buffer size + * 3 : Nonce offset to the output buffer + * 4 : Nonce size + * 5 : Buffer mapping descriptor ((input_buffer_size << 4) | 0xA) + * 6 : Input buffer address + * 7 : Buffer mapping descriptor ((input_buffer_size << 4) | 0xC) + * 8 : Output buffer address + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Buffer unmapping descriptor ((input_buffer_size << 4) | 0xA) + * 3 : Input buffer address + * 4 : Buffer unmapping descriptor ((input_buffer_size << 4) | 0xC) + * 5 : Output buffer address + */ +void Unwrap(Service::Interface* self); + +/** * APT::NotifyToWait service function * Inputs: * 1 : AppID diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp index 62dc2d61d..c496cba8d 100644 --- a/src/core/hle/service/apt/apt_a.cpp +++ b/src/core/hle/service/apt/apt_a.cpp @@ -78,8 +78,8 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00430040, NotifyToWait, "NotifyToWait"}, {0x00440000, GetSharedFont, "GetSharedFont"}, {0x00450040, nullptr, "GetWirelessRebootInfo"}, - {0x00460104, nullptr, "Wrap"}, - {0x00470104, nullptr, "Unwrap"}, + {0x00460104, Wrap, "Wrap"}, + {0x00470104, Unwrap, "Unwrap"}, {0x00480100, nullptr, "GetProgramInfo"}, {0x00490180, nullptr, "Reboot"}, {0x004A0040, nullptr, "GetCaptureInfo"}, diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp index effd23dce..ec5668d05 100644 --- a/src/core/hle/service/apt/apt_s.cpp +++ b/src/core/hle/service/apt/apt_s.cpp @@ -78,8 +78,8 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00430040, NotifyToWait, "NotifyToWait"}, {0x00440000, GetSharedFont, "GetSharedFont"}, {0x00450040, nullptr, "GetWirelessRebootInfo"}, - {0x00460104, nullptr, "Wrap"}, - {0x00470104, nullptr, "Unwrap"}, + {0x00460104, Wrap, "Wrap"}, + {0x00470104, Unwrap, "Unwrap"}, {0x00480100, nullptr, "GetProgramInfo"}, {0x00490180, nullptr, "Reboot"}, {0x004A0040, nullptr, "GetCaptureInfo"}, diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp index e06084a1e..9dd002590 100644 --- a/src/core/hle/service/apt/apt_u.cpp +++ b/src/core/hle/service/apt/apt_u.cpp @@ -78,8 +78,8 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00430040, NotifyToWait, "NotifyToWait"}, {0x00440000, GetSharedFont, "GetSharedFont"}, {0x00450040, nullptr, "GetWirelessRebootInfo"}, - {0x00460104, nullptr, "Wrap"}, - {0x00470104, nullptr, "Unwrap"}, + {0x00460104, Wrap, "Wrap"}, + {0x00470104, Unwrap, "Unwrap"}, {0x00480100, nullptr, "GetProgramInfo"}, {0x00490180, nullptr, "Reboot"}, {0x004A0040, nullptr, "GetCaptureInfo"}, diff --git a/src/core/hle/service/cam/cam.cpp b/src/core/hle/service/cam/cam.cpp index 5594aedab..95665e754 100644 --- a/src/core/hle/service/cam/cam.cpp +++ b/src/core/hle/service/cam/cam.cpp @@ -2,7 +2,15 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> +#include <array> +#include <future> +#include <memory> +#include <vector> +#include "common/bit_set.h" #include "common/logging/log.h" +#include "core/core_timing.h" +#include "core/frontend/camera/factory.h" #include "core/hle/kernel/event.h" #include "core/hle/service/cam/cam.h" #include "core/hle/service/cam/cam_c.h" @@ -10,206 +18,924 @@ #include "core/hle/service/cam/cam_s.h" #include "core/hle/service/cam/cam_u.h" #include "core/hle/service/service.h" +#include "core/settings.h" namespace Service { namespace CAM { -static const u32 TRANSFER_BYTES = 5 * 1024; +namespace { + +struct ContextConfig { + Flip flip; + Effect effect; + OutputFormat format; + Resolution resolution; +}; + +struct CameraConfig { + std::unique_ptr<Camera::CameraInterface> impl; + std::array<ContextConfig, 2> contexts; + int current_context; + FrameRate frame_rate; +}; + +struct PortConfig { + int camera_id; + + bool is_active; // set when the port is activated by an Activate call. + bool is_pending_receiving; // set if SetReceiving is called when is_busy = false. When + // StartCapture is called then, this will trigger a receiving + // process and reset itself. + bool is_busy; // set when StartCapture is called and reset when StopCapture is called. + bool is_receiving; // set when there is an ongoing receiving process. + + bool is_trimming; + u16 x0; // x-coordinate of starting position for trimming + u16 y0; // y-coordinate of starting position for trimming + u16 x1; // x-coordinate of ending position for trimming + u16 y1; // y-coordinate of ending position for trimming + + u32 transfer_bytes; + + Kernel::SharedPtr<Kernel::Event> completion_event; + Kernel::SharedPtr<Kernel::Event> buffer_error_interrupt_event; + Kernel::SharedPtr<Kernel::Event> vsync_interrupt_event; + + std::future<std::vector<u16>> capture_result; // will hold the received frame. + VAddr dest; // the destination address of a receiving process + u32 dest_size; // the destination size of a receiving process + + void Clear() { + completion_event->Clear(); + buffer_error_interrupt_event->Clear(); + vsync_interrupt_event->Clear(); + is_receiving = false; + is_active = false; + is_pending_receiving = false; + is_busy = false; + is_trimming = false; + x0 = 0; + y0 = 0; + x1 = 0; + y1 = 0; + transfer_bytes = 256; + } +}; + +// built-in resolution parameters +constexpr std::array<Resolution, 8> PRESET_RESOLUTION{{ + {640, 480, 0, 0, 639, 479}, // VGA + {320, 240, 0, 0, 639, 479}, // QVGA + {160, 120, 0, 0, 639, 479}, // QQVGA + {352, 288, 26, 0, 613, 479}, // CIF + {176, 144, 26, 0, 613, 479}, // QCIF + {256, 192, 0, 0, 639, 479}, // DS_LCD + {512, 384, 0, 0, 639, 479}, // DS_LCDx4 + {400, 240, 0, 48, 639, 431}, // CTR_TOP_LCD +}}; + +// latency in ms for each frame rate option +constexpr std::array<int, 13> LATENCY_BY_FRAME_RATE{{ + 67, // Rate_15 + 67, // Rate_15_To_5 + 67, // Rate_15_To_2 + 100, // Rate_10 + 118, // Rate_8_5 + 200, // Rate_5 + 50, // Rate_20 + 50, // Rate_20_To_5 + 33, // Rate_30 + 33, // Rate_30_To_5 + 67, // Rate_15_To_10 + 50, // Rate_20_To_10 + 33, // Rate_30_To_10 +}}; + +std::array<CameraConfig, NumCameras> cameras; +std::array<PortConfig, 2> ports; +int completion_event_callback; + +const ResultCode ERROR_INVALID_ENUM_VALUE(ErrorDescription::InvalidEnumValue, ErrorModule::CAM, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); +const ResultCode ERROR_OUT_OF_RANGE(ErrorDescription::OutOfRange, ErrorModule::CAM, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); + +void CompletionEventCallBack(u64 port_id, int) { + PortConfig& port = ports[port_id]; + const CameraConfig& camera = cameras[port.camera_id]; + const auto buffer = port.capture_result.get(); + + if (port.is_trimming) { + u32 trim_width; + u32 trim_height; + const int original_width = camera.contexts[camera.current_context].resolution.width; + const int original_height = camera.contexts[camera.current_context].resolution.height; + if (port.x1 <= port.x0 || port.y1 <= port.y0 || port.x1 > original_width || + port.y1 > original_height) { + LOG_ERROR(Service_CAM, "Invalid trimming coordinates x0=%u, y0=%u, x1=%u, y1=%u", + port.x0, port.y0, port.x1, port.y1); + trim_width = 0; + trim_height = 0; + } else { + trim_width = port.x1 - port.x0; + trim_height = port.y1 - port.y0; + } + + u32 trim_size = (port.x1 - port.x0) * (port.y1 - port.y0) * 2; + if (port.dest_size != trim_size) { + LOG_ERROR(Service_CAM, "The destination size (%u) doesn't match the source (%u)!", + port.dest_size, trim_size); + } + + const u32 src_offset = port.y0 * original_width + port.x0; + const u16* src_ptr = buffer.data() + src_offset; + // Note: src_size_left is int because it can be negative if the buffer size doesn't match. + int src_size_left = static_cast<int>((buffer.size() - src_offset) * sizeof(u16)); + VAddr dest_ptr = port.dest; + // Note: dest_size_left and line_bytes are int to match the type of src_size_left. + int dest_size_left = static_cast<int>(port.dest_size); + const int line_bytes = static_cast<int>(trim_width * sizeof(u16)); + + for (u32 y = 0; y < trim_height; ++y) { + int copy_length = std::min({line_bytes, dest_size_left, src_size_left}); + if (copy_length <= 0) { + break; + } + Memory::WriteBlock(dest_ptr, src_ptr, copy_length); + dest_ptr += copy_length; + dest_size_left -= copy_length; + src_ptr += original_width; + src_size_left -= original_width * sizeof(u16); + } + } else { + std::size_t buffer_size = buffer.size() * sizeof(u16); + if (port.dest_size != buffer_size) { + LOG_ERROR(Service_CAM, "The destination size (%u) doesn't match the source (%zu)!", + port.dest_size, buffer_size); + } + Memory::WriteBlock(port.dest, buffer.data(), std::min<u32>(port.dest_size, buffer_size)); + } + + port.is_receiving = false; + port.completion_event->Signal(); +} + +// Starts a receiving process on the specified port. This can only be called when is_busy = true and +// is_receiving = false. +void StartReceiving(int port_id) { + PortConfig& port = ports[port_id]; + port.is_receiving = true; + + // launches a capture task asynchronously + const CameraConfig& camera = cameras[port.camera_id]; + port.capture_result = + std::async(std::launch::async, &Camera::CameraInterface::ReceiveFrame, camera.impl.get()); + + // schedules a completion event according to the frame rate. The event will block on the + // capture task if it is not finished within the expected time + CoreTiming::ScheduleEvent( + msToCycles(LATENCY_BY_FRAME_RATE[static_cast<int>(camera.frame_rate)]), + completion_event_callback, port_id); +} + +// Cancels any ongoing receiving processes at the specified port. This is used by functions that +// stop capturing. +// TODO: what is the exact behaviour on real 3DS when stopping capture during an ongoing process? +// Will the completion event still be signaled? +void CancelReceiving(int port_id) { + if (!ports[port_id].is_receiving) + return; + LOG_WARNING(Service_CAM, "tries to cancel an ongoing receiving process."); + CoreTiming::UnscheduleEvent(completion_event_callback, port_id); + ports[port_id].capture_result.wait(); + ports[port_id].is_receiving = false; +} + +// Activates the specified port with the specfied camera. +static void ActivatePort(int port_id, int camera_id) { + if (ports[port_id].is_busy && ports[port_id].camera_id != camera_id) { + CancelReceiving(port_id); + cameras[ports[port_id].camera_id].impl->StopCapture(); + ports[port_id].is_busy = false; + } + ports[port_id].is_active = true; + ports[port_id].camera_id = camera_id; +} + +template <int max_index> +class CommandParamBitSet : public BitSet8 { +public: + explicit CommandParamBitSet(u32 command_param) + : BitSet8(static_cast<u8>(command_param & 0xFF)) {} -static Kernel::SharedPtr<Kernel::Event> completion_event_cam1; -static Kernel::SharedPtr<Kernel::Event> completion_event_cam2; -static Kernel::SharedPtr<Kernel::Event> interrupt_error_event; -static Kernel::SharedPtr<Kernel::Event> vsync_interrupt_error_event; + bool IsValid() const { + return m_val < (1 << max_index); + } + + bool IsSingle() const { + return IsValid() && Count() == 1; + } +}; + +using PortSet = CommandParamBitSet<2>; +using ContextSet = CommandParamBitSet<2>; +using CameraSet = CommandParamBitSet<3>; + +} // namespace void StartCapture(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u8 port = cmd_buff[1] & 0xFF; + const PortSet port_select(cmd_buff[1]); + + if (port_select.IsValid()) { + for (int i : port_select) { + if (!ports[i].is_busy) { + if (!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 %u hasn't been activated", i); + } else { + cameras[ports[i].camera_id].impl->StartCapture(); + ports[i].is_busy = true; + if (ports[i].is_pending_receiving) { + ports[i].is_pending_receiving = false; + StartReceiving(i); + } + } + } else { + LOG_WARNING(Service_CAM, "port %u already started", i); + } + } + cmd_buff[1] = RESULT_SUCCESS.raw; + } else { + LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + } cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d", port); + LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val); } void StopCapture(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u8 port = cmd_buff[1] & 0xFF; + const PortSet port_select(cmd_buff[1]); + + if (port_select.IsValid()) { + for (int i : port_select) { + if (ports[i].is_busy) { + CancelReceiving(i); + cameras[ports[i].camera_id].impl->StopCapture(); + ports[i].is_busy = false; + } else { + LOG_WARNING(Service_CAM, "port %u already stopped", i); + } + } + cmd_buff[1] = RESULT_SUCCESS.raw; + } else { + LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + } cmd_buff[0] = IPC::MakeHeader(0x2, 1, 0); + + LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val); +} + +void IsBusy(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + const PortSet port_select(cmd_buff[1]); + + if (port_select.IsValid()) { + bool is_busy = true; + // Note: the behaviour on no or both ports selected are verified against real 3DS. + for (int i : port_select) { + is_busy &= ports[i].is_busy; + } + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = is_busy ? 1 : 0; + } else { + LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + } + + cmd_buff[0] = IPC::MakeHeader(0x3, 2, 0); + + LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val); +} + +void ClearBuffer(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + const PortSet port_select(cmd_buff[1]); + + cmd_buff[0] = IPC::MakeHeader(0x4, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d", port); + LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u", port_select.m_val); } void GetVsyncInterruptEvent(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u8 port = cmd_buff[1] & 0xFF; + const PortSet port_select(cmd_buff[1]); + + if (port_select.IsSingle()) { + int port = *port_select.begin(); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = IPC::CopyHandleDesc(); + cmd_buff[3] = Kernel::g_handle_table.Create(ports[port].vsync_interrupt_event).MoveFrom(); + } else { + LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + cmd_buff[2] = IPC::CopyHandleDesc(); + cmd_buff[2] = 0; + } cmd_buff[0] = IPC::MakeHeader(0x5, 1, 2); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = IPC::CopyHandleDesc(); - cmd_buff[3] = Kernel::g_handle_table.Create(vsync_interrupt_error_event).MoveFrom(); - LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d", port); + LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u", port_select.m_val); } void GetBufferErrorInterruptEvent(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u8 port = cmd_buff[1] & 0xFF; - - cmd_buff[0] = IPC::MakeHeader(0x6, 1, 2); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = IPC::CopyHandleDesc(); - cmd_buff[3] = Kernel::g_handle_table.Create(interrupt_error_event).MoveFrom(); - - LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d", port); + const PortSet port_select(cmd_buff[1]); + + if (port_select.IsSingle()) { + int port = *port_select.begin(); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = IPC::CopyHandleDesc(); + cmd_buff[3] = + Kernel::g_handle_table.Create(ports[port].buffer_error_interrupt_event).MoveFrom(); + } else { + LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + cmd_buff[2] = IPC::CopyHandleDesc(); + cmd_buff[2] = 0; + } + + LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u", port_select.m_val); } void SetReceiving(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - VAddr dest = cmd_buff[1]; - u8 port = cmd_buff[2] & 0xFF; - u32 image_size = cmd_buff[3]; - u16 trans_unit = cmd_buff[4] & 0xFFFF; + const VAddr dest = cmd_buff[1]; + const PortSet port_select(cmd_buff[2]); + const u32 image_size = cmd_buff[3]; + const u32 trans_unit = cmd_buff[4] & 0xFFFF; + + if (port_select.IsSingle()) { + int port_id = *port_select.begin(); + PortConfig& port = ports[port_id]; + CancelReceiving(port_id); + port.completion_event->Clear(); + port.dest = dest; + port.dest_size = image_size; + + if (port.is_busy) { + StartReceiving(port_id); + } else { + port.is_pending_receiving = true; + } + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = IPC::CopyHandleDesc(); + cmd_buff[3] = Kernel::g_handle_table.Create(port.completion_event).MoveFrom(); + } else { + LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + } + + cmd_buff[0] = IPC::MakeHeader(0x7, 1, 2); - Kernel::Event* completion_event = - (Port)port == Port::Cam2 ? completion_event_cam2.get() : completion_event_cam1.get(); + LOG_DEBUG(Service_CAM, "called, addr=0x%X, port_select=%u, image_size=%u, trans_unit=%u", dest, + port_select.m_val, image_size, trans_unit); +} - completion_event->Signal(); +void IsFinishedReceiving(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); - cmd_buff[0] = IPC::MakeHeader(0x7, 1, 2); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = IPC::CopyHandleDesc(); - cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).MoveFrom(); + const PortSet port_select(cmd_buff[1]); + + if (port_select.IsSingle()) { + int port = *port_select.begin(); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = (ports[port].is_receiving || ports[port].is_pending_receiving) ? 0 : 1; + } else { + LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + } - LOG_WARNING(Service_CAM, "(STUBBED) called, addr=0x%X, port=%d, image_size=%d, trans_unit=%d", - dest, port, image_size, trans_unit); + cmd_buff[0] = IPC::MakeHeader(0x8, 2, 0); + + LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val); } void SetTransferLines(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u8 port = cmd_buff[1] & 0xFF; - u16 transfer_lines = cmd_buff[2] & 0xFFFF; - u16 width = cmd_buff[3] & 0xFFFF; - u16 height = cmd_buff[4] & 0xFFFF; + const PortSet port_select(cmd_buff[1]); + const u32 transfer_lines = cmd_buff[2] & 0xFFFF; + const u32 width = cmd_buff[3] & 0xFFFF; + const u32 height = cmd_buff[4] & 0xFFFF; + + if (port_select.IsValid()) { + for (int i : port_select) { + ports[i].transfer_bytes = transfer_lines * width * 2; + } + cmd_buff[1] = RESULT_SUCCESS.raw; + } else { + LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + } cmd_buff[0] = IPC::MakeHeader(0x9, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d, lines=%d, width=%d, height=%d", port, - transfer_lines, width, height); + LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u, lines=%u, width=%u, height=%u", + port_select.m_val, transfer_lines, width, height); } void GetMaxLines(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u16 width = cmd_buff[1] & 0xFFFF; - u16 height = cmd_buff[2] & 0xFFFF; + const u32 width = cmd_buff[1] & 0xFFFF; + const u32 height = cmd_buff[2] & 0xFFFF; + + // Note: the result of the algorithm below are hwtested with width < 640 and with height < 480 + constexpr u32 MIN_TRANSFER_UNIT = 256; + constexpr u32 MAX_BUFFER_SIZE = 2560; + if (width * height * 2 % MIN_TRANSFER_UNIT != 0) { + cmd_buff[1] = ERROR_OUT_OF_RANGE.raw; + } else { + u32 lines = MAX_BUFFER_SIZE / width; + if (lines > height) { + lines = height; + } + cmd_buff[1] = RESULT_SUCCESS.raw; + while (height % lines != 0 || (lines * width * 2 % MIN_TRANSFER_UNIT != 0)) { + --lines; + if (lines == 0) { + cmd_buff[1] = ERROR_OUT_OF_RANGE.raw; + break; + } + } + cmd_buff[2] = lines; + } cmd_buff[0] = IPC::MakeHeader(0xA, 2, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = TRANSFER_BYTES / (2 * width); - LOG_WARNING(Service_CAM, "(STUBBED) called, width=%d, height=%d, lines = %d", width, height, - cmd_buff[2]); + LOG_DEBUG(Service_CAM, "called, width=%u, height=%u", width, height); +} + +void SetTransferBytes(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + const PortSet port_select(cmd_buff[1]); + const u32 transfer_bytes = cmd_buff[2] & 0xFFFF; + const u32 width = cmd_buff[3] & 0xFFFF; + const u32 height = cmd_buff[4] & 0xFFFF; + + if (port_select.IsValid()) { + for (int i : port_select) { + ports[i].transfer_bytes = transfer_bytes; + } + cmd_buff[1] = RESULT_SUCCESS.raw; + } else { + LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + } + + cmd_buff[0] = IPC::MakeHeader(0xB, 1, 0); + + LOG_WARNING(Service_CAM, "(STUBBED)called, port_select=%u, bytes=%u, width=%u, height=%u", + port_select.m_val, transfer_bytes, width, height); } void GetTransferBytes(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u8 port = cmd_buff[1] & 0xFF; + const PortSet port_select(cmd_buff[1]); + + if (port_select.IsSingle()) { + int port = *port_select.begin(); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = ports[port].transfer_bytes; + } else { + LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + } cmd_buff[0] = IPC::MakeHeader(0xC, 2, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = TRANSFER_BYTES; - LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d", port); + LOG_WARNING(Service_CAM, "(STUBBED)called, port_select=%u", port_select.m_val); +} + +void GetMaxBytes(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + const u32 width = cmd_buff[1] & 0xFFFF; + const u32 height = cmd_buff[2] & 0xFFFF; + + // Note: the result of the algorithm below are hwtested with width < 640 and with height < 480 + constexpr u32 MIN_TRANSFER_UNIT = 256; + constexpr u32 MAX_BUFFER_SIZE = 2560; + if (width * height * 2 % MIN_TRANSFER_UNIT != 0) { + cmd_buff[1] = ERROR_OUT_OF_RANGE.raw; + } else { + u32 bytes = MAX_BUFFER_SIZE; + + while (width * height * 2 % bytes != 0) { + bytes -= MIN_TRANSFER_UNIT; + } + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = bytes; + } + cmd_buff[0] = IPC::MakeHeader(0xD, 2, 0); + + LOG_DEBUG(Service_CAM, "called, width=%u, height=%u", width, height); } void SetTrimming(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u8 port = cmd_buff[1] & 0xFF; - bool trim = (cmd_buff[2] & 0xFF) != 0; + const PortSet port_select(cmd_buff[1]); + const bool trim = (cmd_buff[2] & 0xFF) != 0; + + if (port_select.IsValid()) { + for (int i : port_select) { + ports[i].is_trimming = trim; + } + cmd_buff[1] = RESULT_SUCCESS.raw; + } else { + LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + } cmd_buff[0] = IPC::MakeHeader(0xE, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d, trim=%d", port, trim); + LOG_DEBUG(Service_CAM, "called, port_select=%u, trim=%d", port_select.m_val, trim); +} + +void IsTrimming(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + const PortSet port_select(cmd_buff[1]); + + if (port_select.IsSingle()) { + int port = *port_select.begin(); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = ports[port].is_trimming; + } else { + LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + } + + cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0); + + LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val); +} + +void SetTrimmingParams(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + const PortSet port_select(cmd_buff[1]); + const u16 x0 = static_cast<u16>(cmd_buff[2] & 0xFFFF); + const u16 y0 = static_cast<u16>(cmd_buff[3] & 0xFFFF); + const u16 x1 = static_cast<u16>(cmd_buff[4] & 0xFFFF); + const u16 y1 = static_cast<u16>(cmd_buff[5] & 0xFFFF); + + if (port_select.IsValid()) { + for (int i : port_select) { + ports[i].x0 = x0; + ports[i].y0 = y0; + ports[i].x1 = x1; + ports[i].y1 = y1; + } + cmd_buff[1] = RESULT_SUCCESS.raw; + } else { + LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + } + + cmd_buff[0] = IPC::MakeHeader(0x10, 1, 0); + + LOG_DEBUG(Service_CAM, "called, port_select=%u, x0=%u, y0=%u, x1=%u, y1=%u", port_select.m_val, + x0, y0, x1, y1); +} + +void GetTrimmingParams(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + const PortSet port_select(cmd_buff[1]); + + if (port_select.IsSingle()) { + int port = *port_select.begin(); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = ports[port].x0; + cmd_buff[3] = ports[port].y0; + cmd_buff[4] = ports[port].x1; + cmd_buff[5] = ports[port].y1; + } else { + LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + } + + cmd_buff[0] = IPC::MakeHeader(0x11, 5, 0); + + LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val); } void SetTrimmingParamsCenter(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u8 port = cmd_buff[1] & 0xFF; - s16 trimW = cmd_buff[2] & 0xFFFF; - s16 trimH = cmd_buff[3] & 0xFFFF; - s16 camW = cmd_buff[4] & 0xFFFF; - s16 camH = cmd_buff[5] & 0xFFFF; + const PortSet port_select(cmd_buff[1]); + const u16 trim_w = static_cast<u16>(cmd_buff[2] & 0xFFFF); + const u16 trim_h = static_cast<u16>(cmd_buff[3] & 0xFFFF); + const u16 cam_w = static_cast<u16>(cmd_buff[4] & 0xFFFF); + const u16 cam_h = static_cast<u16>(cmd_buff[5] & 0xFFFF); + + if (port_select.IsValid()) { + for (int i : port_select) { + ports[i].x0 = (cam_w - trim_w) / 2; + ports[i].y0 = (cam_h - trim_h) / 2; + ports[i].x1 = ports[i].x0 + trim_w; + ports[i].y1 = ports[i].y0 + trim_h; + } + cmd_buff[1] = RESULT_SUCCESS.raw; + } else { + LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + } cmd_buff[0] = IPC::MakeHeader(0x12, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d, trimW=%d, trimH=%d, camW=%d, camH=%d", - port, trimW, trimH, camW, camH); + LOG_DEBUG(Service_CAM, "called, port_select=%u, trim_w=%u, trim_h=%u, cam_w=%u, cam_h=%u", + port_select.m_val, trim_w, trim_h, cam_w, cam_h); } void Activate(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u8 cam_select = cmd_buff[1] & 0xFF; + const CameraSet camera_select(cmd_buff[1]); + + if (camera_select.IsValid()) { + if (camera_select.m_val == 0) { // deactive all + for (int i = 0; i < 2; ++i) { + if (ports[i].is_busy) { + CancelReceiving(i); + cameras[ports[i].camera_id].impl->StopCapture(); + ports[i].is_busy = false; + } + ports[i].is_active = false; + } + cmd_buff[1] = RESULT_SUCCESS.raw; + } else if (camera_select[0] && camera_select[1]) { + LOG_ERROR(Service_CAM, "camera 0 and 1 can't be both activated"); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + } else { + if (camera_select[0]) { + ActivatePort(0, 0); + } else if (camera_select[1]) { + ActivatePort(0, 1); + } + + if (camera_select[2]) { + ActivatePort(1, 2); + } + cmd_buff[1] = RESULT_SUCCESS.raw; + } + } else { + LOG_ERROR(Service_CAM, "invalid camera_select=%u", camera_select.m_val); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + } cmd_buff[0] = IPC::MakeHeader(0x13, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_WARNING(Service_CAM, "(STUBBED) called, cam_select=%d", cam_select); + LOG_DEBUG(Service_CAM, "called, camera_select=%u", camera_select.m_val); +} + +void SwitchContext(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + const CameraSet camera_select(cmd_buff[1]); + const ContextSet context_select(cmd_buff[2]); + + if (camera_select.IsValid() && context_select.IsSingle()) { + int context = *context_select.begin(); + for (int camera : camera_select) { + cameras[camera].current_context = context; + const ContextConfig& context_config = cameras[camera].contexts[context]; + cameras[camera].impl->SetFlip(context_config.flip); + cameras[camera].impl->SetEffect(context_config.effect); + cameras[camera].impl->SetFormat(context_config.format); + cameras[camera].impl->SetResolution(context_config.resolution); + } + cmd_buff[1] = RESULT_SUCCESS.raw; + } else { + LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val, + context_select.m_val); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + } + + cmd_buff[0] = IPC::MakeHeader(0x14, 1, 0); + + LOG_DEBUG(Service_CAM, "called, camera_select=%u, context_select=%u", camera_select.m_val, + context_select.m_val); } void FlipImage(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u8 cam_select = cmd_buff[1] & 0xFF; - u8 flip = cmd_buff[2] & 0xFF; - u8 context = cmd_buff[3] & 0xFF; + const CameraSet camera_select(cmd_buff[1]); + const Flip flip = static_cast<Flip>(cmd_buff[2] & 0xFF); + const ContextSet context_select(cmd_buff[3]); + + if (camera_select.IsValid() && context_select.IsValid()) { + for (int camera : camera_select) { + for (int context : context_select) { + cameras[camera].contexts[context].flip = flip; + if (cameras[camera].current_context == context) { + cameras[camera].impl->SetFlip(flip); + } + } + } + cmd_buff[1] = RESULT_SUCCESS.raw; + } else { + LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val, + context_select.m_val); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + } cmd_buff[0] = IPC::MakeHeader(0x1D, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_WARNING(Service_CAM, "(STUBBED) called, cam_select=%d, flip=%d, context=%d", cam_select, - flip, context); + LOG_DEBUG(Service_CAM, "called, camera_select=%u, flip=%d, context_select=%u", + camera_select.m_val, static_cast<int>(flip), context_select.m_val); +} + +void SetDetailSize(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + const CameraSet camera_select(cmd_buff[1]); + Resolution resolution; + resolution.width = static_cast<u16>(cmd_buff[2] & 0xFFFF); + resolution.height = static_cast<u16>(cmd_buff[3] & 0xFFFF); + resolution.crop_x0 = static_cast<u16>(cmd_buff[4] & 0xFFFF); + resolution.crop_y0 = static_cast<u16>(cmd_buff[5] & 0xFFFF); + resolution.crop_x1 = static_cast<u16>(cmd_buff[6] & 0xFFFF); + resolution.crop_y1 = static_cast<u16>(cmd_buff[7] & 0xFFFF); + const ContextSet context_select(cmd_buff[8]); + + if (camera_select.IsValid() && context_select.IsValid()) { + for (int camera : camera_select) { + for (int context : context_select) { + cameras[camera].contexts[context].resolution = resolution; + if (cameras[camera].current_context == context) { + cameras[camera].impl->SetResolution(resolution); + } + } + } + cmd_buff[1] = RESULT_SUCCESS.raw; + } else { + LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val, + context_select.m_val); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + } + + cmd_buff[0] = IPC::MakeHeader(0x1E, 1, 0); + + LOG_DEBUG(Service_CAM, "called, camera_select=%u, width=%u, height=%u, crop_x0=%u, crop_y0=%u, " + "crop_x1=%u, crop_y1=%u, context_select=%u", + camera_select.m_val, resolution.width, resolution.height, resolution.crop_x0, + resolution.crop_y0, resolution.crop_x1, resolution.crop_y1, context_select.m_val); } void SetSize(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u8 cam_select = cmd_buff[1] & 0xFF; - u8 size = cmd_buff[2] & 0xFF; - u8 context = cmd_buff[3] & 0xFF; + const CameraSet camera_select(cmd_buff[1]); + const u32 size = cmd_buff[2] & 0xFF; + const ContextSet context_select(cmd_buff[3]); + + if (camera_select.IsValid() && context_select.IsValid()) { + for (int camera : camera_select) { + for (int context : context_select) { + cameras[camera].contexts[context].resolution = PRESET_RESOLUTION[size]; + if (cameras[camera].current_context == context) { + cameras[camera].impl->SetResolution(PRESET_RESOLUTION[size]); + } + } + } + cmd_buff[1] = RESULT_SUCCESS.raw; + } else { + LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val, + context_select.m_val); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + } cmd_buff[0] = IPC::MakeHeader(0x1F, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_WARNING(Service_CAM, "(STUBBED) called, cam_select=%d, size=%d, context=%d", cam_select, - size, context); + LOG_DEBUG(Service_CAM, "called, camera_select=%u, size=%u, context_select=%u", + camera_select.m_val, size, context_select.m_val); } void SetFrameRate(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u8 cam_select = cmd_buff[1] & 0xFF; - u8 frame_rate = cmd_buff[2] & 0xFF; + const CameraSet camera_select(cmd_buff[1]); + const FrameRate frame_rate = static_cast<FrameRate>(cmd_buff[2] & 0xFF); + + if (camera_select.IsValid()) { + for (int camera : camera_select) { + cameras[camera].frame_rate = frame_rate; + // TODO(wwylele): consider hinting the actual camera with the expected frame rate + } + cmd_buff[1] = RESULT_SUCCESS.raw; + } else { + LOG_ERROR(Service_CAM, "invalid camera_select=%u", camera_select.m_val); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + } cmd_buff[0] = IPC::MakeHeader(0x20, 1, 0); + + LOG_WARNING(Service_CAM, "(STUBBED) called, camera_select=%u, frame_rate=%d", + camera_select.m_val, static_cast<int>(frame_rate)); +} + +void SetEffect(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + const CameraSet camera_select(cmd_buff[1]); + const Effect effect = static_cast<Effect>(cmd_buff[2] & 0xFF); + const ContextSet context_select(cmd_buff[3]); + + if (camera_select.IsValid() && context_select.IsValid()) { + for (int camera : camera_select) { + for (int context : context_select) { + cameras[camera].contexts[context].effect = effect; + if (cameras[camera].current_context == context) { + cameras[camera].impl->SetEffect(effect); + } + } + } + cmd_buff[1] = RESULT_SUCCESS.raw; + } else { + LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val, + context_select.m_val); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + } + + cmd_buff[0] = IPC::MakeHeader(0x22, 1, 0); + + LOG_DEBUG(Service_CAM, "called, camera_select=%u, effect=%d, context_select=%u", + camera_select.m_val, static_cast<int>(effect), context_select.m_val); +} + +void SetOutputFormat(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + const CameraSet camera_select(cmd_buff[1]); + const OutputFormat format = static_cast<OutputFormat>(cmd_buff[2] & 0xFF); + const ContextSet context_select(cmd_buff[3]); + + if (camera_select.IsValid() && context_select.IsValid()) { + for (int camera : camera_select) { + for (int context : context_select) { + cameras[camera].contexts[context].format = format; + if (cameras[camera].current_context == context) { + cameras[camera].impl->SetFormat(format); + } + } + } + cmd_buff[1] = RESULT_SUCCESS.raw; + } else { + LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val, + context_select.m_val); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + } + + cmd_buff[0] = IPC::MakeHeader(0x25, 1, 0); + + LOG_DEBUG(Service_CAM, "called, camera_select=%u, format=%d, context_select=%u", + camera_select.m_val, static_cast<int>(format), context_select.m_val); +} + +void SynchronizeVsyncTiming(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + const u32 camera_select1 = cmd_buff[1] & 0xFF; + const u32 camera_select2 = cmd_buff[2] & 0xFF; + + cmd_buff[0] = IPC::MakeHeader(0x29, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_WARNING(Service_CAM, "(STUBBED) called, cam_select=%d, frame_rate=%d", cam_select, - frame_rate); + LOG_WARNING(Service_CAM, "(STUBBED) called, camera_select1=%u, camera_select2=%u", + camera_select1, camera_select2); } void GetStereoCameraCalibrationData(Service::Interface* self) { @@ -239,6 +965,67 @@ void GetStereoCameraCalibrationData(Service::Interface* self) { LOG_TRACE(Service_CAM, "called"); } +void SetPackageParameterWithoutContext(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + PackageParameterWithoutContext package; + std::memcpy(&package, cmd_buff + 1, sizeof(package)); + + cmd_buff[0] = IPC::MakeHeader(0x33, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_CAM, "(STUBBED) called"); +} + +template <typename PackageParameterType, int command_id> +static void SetPackageParameter() { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + PackageParameterType package; + std::memcpy(&package, cmd_buff + 1, sizeof(package)); + + const CameraSet camera_select(static_cast<u32>(package.camera_select)); + const ContextSet context_select(static_cast<u32>(package.context_select)); + + if (camera_select.IsValid() && context_select.IsValid()) { + for (int camera_id : camera_select) { + CameraConfig& camera = cameras[camera_id]; + for (int context_id : context_select) { + ContextConfig& context = camera.contexts[context_id]; + context.effect = package.effect; + context.flip = package.flip; + context.resolution = package.GetResolution(); + if (context_id == camera.current_context) { + camera.impl->SetEffect(context.effect); + camera.impl->SetFlip(context.flip); + camera.impl->SetResolution(context.resolution); + } + } + } + cmd_buff[1] = RESULT_SUCCESS.raw; + } else { + LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", package.camera_select, + package.context_select); + cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + } + + cmd_buff[0] = IPC::MakeHeader(command_id, 1, 0); + + LOG_DEBUG(Service_CAM, "called"); +} + +Resolution PackageParameterWithContext::GetResolution() { + return PRESET_RESOLUTION[static_cast<int>(size)]; +} + +void SetPackageParameterWithContext(Service::Interface* self) { + SetPackageParameter<PackageParameterWithContext, 0x34>(); +} + +void SetPackageParameterWithContextDetail(Service::Interface* self) { + SetPackageParameter<PackageParameterWithContextDetail, 0x35>(); +} + void GetSuitableY2rStandardCoefficient(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); @@ -263,24 +1050,50 @@ void PlayShutterSound(Service::Interface* self) { void DriverInitialize(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - completion_event_cam1->Clear(); - completion_event_cam2->Clear(); - interrupt_error_event->Clear(); - vsync_interrupt_error_event->Clear(); + for (int camera_id = 0; camera_id < NumCameras; ++camera_id) { + CameraConfig& camera = cameras[camera_id]; + camera.current_context = 0; + for (int context_id = 0; context_id < 2; ++context_id) { + // Note: the following default values are verified against real 3DS + ContextConfig& context = camera.contexts[context_id]; + context.flip = camera_id == 1 ? Flip::Horizontal : Flip::None; + context.effect = Effect::None; + context.format = OutputFormat::YUV422; + context.resolution = + context_id == 0 ? PRESET_RESOLUTION[5 /*DS_LCD*/] : PRESET_RESOLUTION[0 /*VGA*/]; + } + camera.impl = Camera::CreateCamera(Settings::values.camera_name[camera_id], + Settings::values.camera_config[camera_id]); + camera.impl->SetFlip(camera.contexts[0].flip); + camera.impl->SetEffect(camera.contexts[0].effect); + camera.impl->SetFormat(camera.contexts[0].format); + camera.impl->SetResolution(camera.contexts[0].resolution); + } + + for (PortConfig& port : ports) { + port.Clear(); + } cmd_buff[0] = IPC::MakeHeader(0x39, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_WARNING(Service_CAM, "(STUBBED) called"); + LOG_DEBUG(Service_CAM, "called"); } void DriverFinalize(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + CancelReceiving(0); + CancelReceiving(1); + + for (CameraConfig& camera : cameras) { + camera.impl = nullptr; + } + cmd_buff[0] = IPC::MakeHeader(0x3A, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_WARNING(Service_CAM, "(STUBBED) called"); + LOG_DEBUG(Service_CAM, "called"); } void Init() { @@ -291,21 +1104,28 @@ void Init() { AddService(new CAM_S_Interface); AddService(new CAM_U_Interface); - completion_event_cam1 = - Kernel::Event::Create(ResetType::OneShot, "CAM_U::completion_event_cam1"); - completion_event_cam2 = - Kernel::Event::Create(ResetType::OneShot, "CAM_U::completion_event_cam2"); - interrupt_error_event = - Kernel::Event::Create(ResetType::OneShot, "CAM_U::interrupt_error_event"); - vsync_interrupt_error_event = - Kernel::Event::Create(ResetType::OneShot, "CAM_U::vsync_interrupt_error_event"); + for (PortConfig& port : ports) { + port.completion_event = Event::Create(ResetType::Sticky, "CAM_U::completion_event"); + port.buffer_error_interrupt_event = + Event::Create(ResetType::OneShot, "CAM_U::buffer_error_interrupt_event"); + port.vsync_interrupt_event = + Event::Create(ResetType::OneShot, "CAM_U::vsync_interrupt_event"); + } + completion_event_callback = + CoreTiming::RegisterEvent("CAM_U::CompletionEventCallBack", CompletionEventCallBack); } void Shutdown() { - completion_event_cam1 = nullptr; - completion_event_cam2 = nullptr; - interrupt_error_event = nullptr; - vsync_interrupt_error_event = nullptr; + CancelReceiving(0); + CancelReceiving(1); + for (PortConfig& port : ports) { + port.completion_event = nullptr; + port.buffer_error_interrupt_event = nullptr; + port.vsync_interrupt_event = nullptr; + } + for (CameraConfig& camera : cameras) { + camera.impl = nullptr; + } } } // namespace CAM diff --git a/src/core/hle/service/cam/cam.h b/src/core/hle/service/cam/cam.h index c9b6f8acf..34a9c8479 100644 --- a/src/core/hle/service/cam/cam.h +++ b/src/core/hle/service/cam/cam.h @@ -13,17 +13,12 @@ namespace Service { namespace CAM { -enum class Port : u8 { None = 0, Cam1 = 1, Cam2 = 2, Both = Cam1 | Cam2 }; +enum CameraIndex { + OuterRightCamera = 0, + InnerCamera = 1, + OuterLeftCamera = 2, -enum class CameraSelect : u8 { - None = 0, - Out1 = 1, - In1 = 2, - Out2 = 4, - In1Out1 = Out1 | In1, - Out1Out2 = Out1 | Out2, - In1Out2 = In1 | Out2, - All = Out1 | In1 | Out2, + NumCameras = 3, }; enum class Effect : u8 { @@ -35,13 +30,6 @@ enum class Effect : u8 { Sepia01 = 5, }; -enum class Context : u8 { - None = 0, - A = 1, - B = 2, - Both = A | B, -}; - enum class Flip : u8 { None = 0, Horizontal = 1, @@ -160,8 +148,23 @@ struct StereoCameraCalibrationData { static_assert(sizeof(StereoCameraCalibrationData) == 64, "StereoCameraCalibrationData structure size is wrong"); -struct PackageParameterCameraSelect { - CameraSelect camera; +/** + * Resolution parameters for the camera. + * The native resolution of 3DS camera is 640 * 480. The captured image will be cropped in the + * region [crop_x0, crop_x1] * [crop_y0, crop_y1], and then scaled to size width * height as the + * output image. Note that all cropping coordinates are inclusive. + */ +struct Resolution { + u16 width; + u16 height; + u16 crop_x0; + u16 crop_y0; + u16 crop_x1; + u16 crop_y1; +}; + +struct PackageParameterWithoutContext { + u8 camera_select; s8 exposure; WhiteBalance white_balance; s8 sharpness; @@ -183,14 +186,43 @@ struct PackageParameterCameraSelect { s16 auto_white_balance_window_height; }; -static_assert(sizeof(PackageParameterCameraSelect) == 28, - "PackageParameterCameraSelect structure size is wrong"); +static_assert(sizeof(PackageParameterWithoutContext) == 28, + "PackageParameterCameraWithoutContext structure size is wrong"); + +struct PackageParameterWithContext { + u8 camera_select; + u8 context_select; + Flip flip; + Effect effect; + Size size; + INSERT_PADDING_BYTES(3); + + Resolution GetResolution(); +}; + +static_assert(sizeof(PackageParameterWithContext) == 8, + "PackageParameterWithContext structure size is wrong"); + +struct PackageParameterWithContextDetail { + u8 camera_select; + u8 context_select; + Flip flip; + Effect effect; + Resolution resolution; + + Resolution GetResolution() { + return resolution; + } +}; + +static_assert(sizeof(PackageParameterWithContextDetail) == 16, + "PackageParameterWithContextDetail structure size is wrong"); /** - * Unknown + * Starts capturing at the selected port. * Inputs: * 0: 0x00010040 - * 1: u8 Camera port (`Port` enum) + * 1: u8 selected port * Outputs: * 0: 0x00010040 * 1: ResultCode @@ -198,10 +230,10 @@ static_assert(sizeof(PackageParameterCameraSelect) == 28, void StartCapture(Service::Interface* self); /** - * Unknown + * Stops capturing from the selected port. * Inputs: * 0: 0x00020040 - * 1: u8 Camera port (`Port` enum) + * 1: u8 selected port * Outputs: * 0: 0x00020040 * 1: ResultCode @@ -209,10 +241,33 @@ void StartCapture(Service::Interface* self); void StopCapture(Service::Interface* self); /** + * Gets whether the selected port is currently capturing. + * Inputs: + * 0: 0x00030040 + * 1: u8 selected port + * Outputs: + * 0: 0x00030080 + * 1: ResultCode + * 2: 0 if not capturing, 1 if capturing + */ +void IsBusy(Service::Interface* self); + +/** + * Clears the buffer of selected ports. + * Inputs: + * 0: 0x00040040 + * 1: u8 selected port + * Outputs: + * 0: 0x00040040 + * 2: ResultCode + */ +void ClearBuffer(Service::Interface* self); + +/** * Unknown * Inputs: * 0: 0x00050040 - * 1: u8 Camera port (`Port` enum) + * 1: u8 selected port * Outputs: * 0: 0x00050042 * 1: ResultCode @@ -225,7 +280,7 @@ void GetVsyncInterruptEvent(Service::Interface* self); * Unknown * Inputs: * 0: 0x00060040 - * 1: u8 Camera port (`Port` enum) + * 1: u8 selected port * Outputs: * 0: 0x00060042 * 1: ResultCode @@ -241,9 +296,9 @@ void GetBufferErrorInterruptEvent(Service::Interface* self); * Inputs: * 0: 0x00070102 * 1: Destination address in calling process - * 2: u8 Camera port (`Port` enum) - * 3: Image size (in bytes?) - * 4: u16 Transfer unit size (in bytes?) + * 2: u8 selected port + * 3: Image size (in bytes) + * 4: u16 Transfer unit size (in bytes) * 5: Descriptor: Handle * 6: Handle to destination process * Outputs: @@ -255,21 +310,34 @@ void GetBufferErrorInterruptEvent(Service::Interface* self); void SetReceiving(Service::Interface* self); /** - * Unknown + * Gets whether the selected port finished receiving a frame. + * Inputs: + * 0: 0x00080040 + * 1: u8 selected port + * Outputs: + * 0: 0x00080080 + * 1: ResultCode + * 2: 0 if not finished, 1 if finished + */ +void IsFinishedReceiving(Service::Interface* self); + +/** + * Sets the number of lines the buffer contains. * Inputs: * 0: 0x00090100 - * 1: u8 Camera port (`Port` enum) + * 1: u8 selected port * 2: u16 Number of lines to transfer * 3: u16 Width * 4: u16 Height * Outputs: * 0: 0x00090040 * 1: ResultCode + * @todo figure out how the "buffer" actually works. */ void SetTransferLines(Service::Interface* self); /** - * Unknown + * Gets the maximum number of lines that fit in the buffer * Inputs: * 0: 0x000A0080 * 1: u16 Width @@ -277,27 +345,58 @@ void SetTransferLines(Service::Interface* self); * Outputs: * 0: 0x000A0080 * 1: ResultCode - * 2: Maximum number of lines that fit in the buffer(?) + * 2: Maximum number of lines that fit in the buffer + * @todo figure out how the "buffer" actually works. */ void GetMaxLines(Service::Interface* self); /** - * Unknown + * Sets the number of bytes the buffer contains. + * Inputs: + * 0: 0x000B0100 + * 1: u8 selected port + * 2: u16 Number of bytes to transfer + * 3: u16 Width + * 4: u16 Height + * Outputs: + * 0: 0x000B0040 + * 1: ResultCode + * @todo figure out how the "buffer" actually works. + */ +void SetTransferBytes(Service::Interface* self); + +/** + * Gets the number of bytes to the buffer contains. * Inputs: * 0: 0x000C0040 - * 1: u8 Camera port (`Port` enum) + * 1: u8 selected port * Outputs: * 0: 0x000C0080 * 1: ResultCode - * 2: Total number of bytes for each frame with current settings(?) + * 2: The number of bytes the buffer contains + * @todo figure out how the "buffer" actually works. */ void GetTransferBytes(Service::Interface* self); /** - * Unknown + * Gets the maximum number of bytes that fit in the buffer. + * Inputs: + * 0: 0x000D0080 + * 1: u16 Width + * 2: u16 Height + * Outputs: + * 0: 0x000D0080 + * 1: ResultCode + * 2: Maximum number of bytes that fit in the buffer + * @todo figure out how the "buffer" actually works. + */ +void GetMaxBytes(Service::Interface* self); + +/** + * Enables or disables trimming. * Inputs: * 0: 0x000E0080 - * 1: u8 Camera port (`Port` enum) + * 1: u8 selected port * 2: u8 bool Enable trimming if true * Outputs: * 0: 0x000E0040 @@ -306,14 +405,58 @@ void GetTransferBytes(Service::Interface* self); void SetTrimming(Service::Interface* self); /** - * Unknown + * Gets whether trimming is enabled. + * Inputs: + * 0: 0x000F0040 + * 1: u8 selected port + * Outputs: + * 0: 0x000F0080 + * 1: ResultCode + * 2: u8 bool Enable trimming if true + */ +void IsTrimming(Service::Interface* self); + +/** + * Sets the position to trim. + * Inputs: + * 0: 0x00100140 + * 1: u8 selected port + * 2: x start + * 3: y start + * 4: x end (exclusive) + * 5: y end (exclusive) + * Outputs: + * 0: 0x00100040 + * 1: ResultCode + */ +void SetTrimmingParams(Service::Interface* self); + +/** + * Gets the position to trim. + * Inputs: + * 0: 0x00110040 + * 1: u8 selected port + * + * Outputs: + * 0: 0x00110140 + * 1: ResultCode + * 2: x start + * 3: y start + * 4: x end (exclusive) + * 5: y end (exclusive) + */ +void GetTrimmingParams(Service::Interface* self); + +/** + * Sets the position to trim by giving the width and height. The trimming window is always at the + * center. * Inputs: * 0: 0x00120140 - * 1: u8 Camera port (`Port` enum) - * 2: s16 Trim width(?) - * 3: s16 Trim height(?) - * 4: s16 Camera width(?) - * 5: s16 Camera height(?) + * 1: u8 selected port + * 2: s16 Trim width + * 3: s16 Trim height + * 4: s16 Camera width + * 5: s16 Camera height * Outputs: * 0: 0x00120040 * 1: ResultCode @@ -324,7 +467,7 @@ void SetTrimmingParamsCenter(Service::Interface* self); * Selects up to two physical cameras to enable. * Inputs: * 0: 0x00130040 - * 1: u8 Cameras to activate (`CameraSelect` enum) + * 1: u8 selected camera * Outputs: * 0: 0x00130040 * 1: ResultCode @@ -332,12 +475,24 @@ void SetTrimmingParamsCenter(Service::Interface* self); void Activate(Service::Interface* self); /** - * Unknown + * Switches the context of camera settings. + * Inputs: + * 0: 0x00140080 + * 1: u8 selected camera + * 2: u8 selected context + * Outputs: + * 0: 0x00140040 + * 1: ResultCode + */ +void SwitchContext(Service::Interface* self); + +/** + * Sets flipping of images * Inputs: * 0: 0x001D00C0 - * 1: u8 Camera select (`CameraSelect` enum) + * 1: u8 selected camera * 2: u8 Type of flipping to perform (`Flip` enum) - * 3: u8 Context (`Context` enum) + * 3: u8 selected context * Outputs: * 0: 0x001D0040 * 1: ResultCode @@ -345,12 +500,30 @@ void Activate(Service::Interface* self); void FlipImage(Service::Interface* self); /** - * Unknown + * Sets camera resolution from custom parameters. For more details see the Resolution struct. + * Inputs: + * 0: 0x001E0200 + * 1: u8 selected camera + * 2: width + * 3: height + * 4: crop x0 + * 5: crop y0 + * 6: crop x1 + * 7: crop y1 + * 8: u8 selected context + * Outputs: + * 0: 0x001E0040 + * 1: ResultCode + */ +void SetDetailSize(Service::Interface* self); + +/** + * Sets camera resolution from preset resolution parameters. * Inputs: * 0: 0x001F00C0 - * 1: u8 Camera select (`CameraSelect` enum) + * 1: u8 selected camera * 2: u8 Camera frame resolution (`Size` enum) - * 3: u8 Context id (`Context` enum) + * 3: u8 selected context * Outputs: * 0: 0x001F0040 * 1: ResultCode @@ -358,10 +531,10 @@ void FlipImage(Service::Interface* self); void SetSize(Service::Interface* self); /** - * Unknown + * Sets camera framerate. * Inputs: * 0: 0x00200080 - * 1: u8 Camera select (`CameraSelect` enum) + * 1: u8 selected camera * 2: u8 Camera framerate (`FrameRate` enum) * Outputs: * 0: 0x00200040 @@ -370,6 +543,44 @@ void SetSize(Service::Interface* self); void SetFrameRate(Service::Interface* self); /** + * Sets effect on the output image + * Inputs: + * 0: 0x002200C0 + * 1: u8 selected camera + * 2: u8 image effect (`Effect` enum) + * 3: u8 selected context + * Outputs: + * 0: 0x00220040 + * 1: ResultCode + */ +void SetEffect(Service::Interface* self); + +/** + * Sets format of the output image + * Inputs: + * 0: 0x002500C0 + * 1: u8 selected camera + * 2: u8 image format (`OutputFormat` enum) + * 3: u8 selected context + * Outputs: + * 0: 0x00250040 + * 1: ResultCode + */ +void SetOutputFormat(Service::Interface* self); + +/** + * Synchronizes the V-Sync timing of two cameras. + * Inputs: + * 0: 0x00290080 + * 1: u8 selected camera 1 + * 2: u8 selected camera 2 + * Outputs: + * 0: 0x00280040 + * 1: ResultCode + */ +void SynchronizeVsyncTiming(Service::Interface* self); + +/** * Returns calibration data relating the outside cameras to eachother, for use in AR applications. * * Inputs: @@ -382,6 +593,45 @@ void SetFrameRate(Service::Interface* self); void GetStereoCameraCalibrationData(Service::Interface* self); /** + * Batch-configures context-free settings. + * + * Inputs: + * 0: 0x003302C0 + * 1-7: struct PachageParameterWithoutContext + * 8-11: unused + * Outputs: + * 0: 0x00330040 + * 1: ResultCode + */ +void SetPackageParameterWithoutContext(Service::Interface* self); + +/** + * Batch-configures context-related settings with preset resolution parameters. + * + * Inputs: + * 0: 0x00340140 + * 1-2: struct PackageParameterWithContext + * 3-5: unused + * Outputs: + * 0: 0x00340040 + * 1: ResultCode + */ +void SetPackageParameterWithContext(Service::Interface* self); + +/** + * Batch-configures context-related settings with custom resolution parameters + * + * Inputs: + * 0: 0x003501C0 + * 1-4: struct PackageParameterWithContextDetail + * 5-7: unused + * Outputs: + * 0: 0x00350040 + * 1: ResultCode + */ +void SetPackageParameterWithContextDetail(Service::Interface* self); + +/** * Unknown * Inputs: * 0: 0x00360000 diff --git a/src/core/hle/service/cam/cam_u.cpp b/src/core/hle/service/cam/cam_u.cpp index af2123e5b..251c1e6d4 100644 --- a/src/core/hle/service/cam/cam_u.cpp +++ b/src/core/hle/service/cam/cam_u.cpp @@ -11,24 +11,24 @@ namespace CAM { const Interface::FunctionInfo FunctionTable[] = { {0x00010040, StartCapture, "StartCapture"}, {0x00020040, StopCapture, "StopCapture"}, - {0x00030040, nullptr, "IsBusy"}, - {0x00040040, nullptr, "ClearBuffer"}, + {0x00030040, IsBusy, "IsBusy"}, + {0x00040040, ClearBuffer, "ClearBuffer"}, {0x00050040, GetVsyncInterruptEvent, "GetVsyncInterruptEvent"}, {0x00060040, GetBufferErrorInterruptEvent, "GetBufferErrorInterruptEvent"}, {0x00070102, SetReceiving, "SetReceiving"}, - {0x00080040, nullptr, "IsFinishedReceiving"}, + {0x00080040, IsFinishedReceiving, "IsFinishedReceiving"}, {0x00090100, SetTransferLines, "SetTransferLines"}, {0x000A0080, GetMaxLines, "GetMaxLines"}, - {0x000B0100, nullptr, "SetTransferBytes"}, + {0x000B0100, SetTransferBytes, "SetTransferBytes"}, {0x000C0040, GetTransferBytes, "GetTransferBytes"}, - {0x000D0080, nullptr, "GetMaxBytes"}, + {0x000D0080, GetMaxBytes, "GetMaxBytes"}, {0x000E0080, SetTrimming, "SetTrimming"}, - {0x000F0040, nullptr, "IsTrimming"}, - {0x00100140, nullptr, "SetTrimmingParams"}, - {0x00110040, nullptr, "GetTrimmingParams"}, + {0x000F0040, IsTrimming, "IsTrimming"}, + {0x00100140, SetTrimmingParams, "SetTrimmingParams"}, + {0x00110040, GetTrimmingParams, "GetTrimmingParams"}, {0x00120140, SetTrimmingParamsCenter, "SetTrimmingParamsCenter"}, {0x00130040, Activate, "Activate"}, - {0x00140080, nullptr, "SwitchContext"}, + {0x00140080, SwitchContext, "SwitchContext"}, {0x00150080, nullptr, "SetExposure"}, {0x00160080, nullptr, "SetWhiteBalance"}, {0x00170080, nullptr, "SetWhiteBalanceWithoutBaseUp"}, @@ -38,18 +38,18 @@ const Interface::FunctionInfo FunctionTable[] = { {0x001B0080, nullptr, "SetAutoWhiteBalance"}, {0x001C0040, nullptr, "IsAutoWhiteBalance"}, {0x001D00C0, FlipImage, "FlipImage"}, - {0x001E0200, nullptr, "SetDetailSize"}, + {0x001E0200, SetDetailSize, "SetDetailSize"}, {0x001F00C0, SetSize, "SetSize"}, {0x00200080, SetFrameRate, "SetFrameRate"}, {0x00210080, nullptr, "SetPhotoMode"}, - {0x002200C0, nullptr, "SetEffect"}, + {0x002200C0, SetEffect, "SetEffect"}, {0x00230080, nullptr, "SetContrast"}, {0x00240080, nullptr, "SetLensCorrection"}, - {0x002500C0, nullptr, "SetOutputFormat"}, + {0x002500C0, SetOutputFormat, "SetOutputFormat"}, {0x00260140, nullptr, "SetAutoExposureWindow"}, {0x00270140, nullptr, "SetAutoWhiteBalanceWindow"}, {0x00280080, nullptr, "SetNoiseFilter"}, - {0x00290080, nullptr, "SynchronizeVsyncTiming"}, + {0x00290080, SynchronizeVsyncTiming, "SynchronizeVsyncTiming"}, {0x002A0080, nullptr, "GetLatestVsyncTiming"}, {0x002B0000, GetStereoCameraCalibrationData, "GetStereoCameraCalibrationData"}, {0x002C0400, nullptr, "SetStereoCameraCalibrationData"}, @@ -59,9 +59,9 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00300080, nullptr, "ReadMcuVariableI2cExclusive"}, {0x00310180, nullptr, "SetImageQualityCalibrationData"}, {0x00320000, nullptr, "GetImageQualityCalibrationData"}, - {0x003302C0, nullptr, "SetPackageParameterWithoutContext"}, - {0x00340140, nullptr, "SetPackageParameterWithContext"}, - {0x003501C0, nullptr, "SetPackageParameterWithContextDetail"}, + {0x003302C0, SetPackageParameterWithoutContext, "SetPackageParameterWithoutContext"}, + {0x00340140, SetPackageParameterWithContext, "SetPackageParameterWithContext"}, + {0x003501C0, SetPackageParameterWithContextDetail, "SetPackageParameterWithContextDetail"}, {0x00360000, GetSuitableY2rStandardCoefficient, "GetSuitableY2rStandardCoefficient"}, {0x00370202, nullptr, "PlayShutterSoundWithWave"}, {0x00380040, PlayShutterSound, "PlayShutterSound"}, diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index 59dd6d1cd..4ddb1bc90 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -3,6 +3,8 @@ // Refer to the license.txt file included. #include <algorithm> +#include <array> +#include <cryptopp/sha.h> #include "common/file_util.h" #include "common/logging/log.h" #include "common/string_util.h" @@ -176,14 +178,29 @@ void SecureInfoGetRegion(Service::Interface* self) { } void GenHashConsoleUnique(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 app_id_salt = cmd_buff[1]; - - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = 0x33646D6F ^ (app_id_salt & 0xFFFFF); // 3dmoo hash - cmd_buff[3] = 0x6F534841 ^ (app_id_salt & 0xFFFFF); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x03, 1, 0); + const u32 app_id_salt = rp.Pop<u32>() & 0x000FFFFF; + + IPC::RequestBuilder rb = rp.MakeBuilder(3, 0); + + std::array<u8, 12> buffer; + const ResultCode result = GetConfigInfoBlock(ConsoleUniqueID2BlockID, 8, 2, buffer.data()); + rb.Push(result); + if (result.IsSuccess()) { + std::memcpy(&buffer[8], &app_id_salt, sizeof(u32)); + std::array<u8, CryptoPP::SHA256::DIGESTSIZE> hash; + CryptoPP::SHA256().CalculateDigest(hash.data(), buffer.data(), sizeof(buffer)); + u32 low, high; + memcpy(&low, &hash[hash.size() - 8], sizeof(u32)); + memcpy(&high, &hash[hash.size() - 4], sizeof(u32)); + rb.Push(low); + rb.Push(high); + } else { + rb.Push<u32>(0); + rb.Push<u32>(0); + } - LOG_WARNING(Service_CFG, "(STUBBED) called app_id_salt=0x%X", app_id_salt); + LOG_DEBUG(Service_CFG, "called app_id_salt=0x%X", app_id_salt); } void GetRegionCanadaUSA(Service::Interface* self) { @@ -322,47 +339,11 @@ static ResultVal<void*> GetConfigInfoBlockPointer(u32 block_id, u32 size, u32 fl return MakeResult<void*>(pointer); } -/// Checks if the language is available in the chosen region, and returns a proper one -static u8 AdjustLanguageInfoBlock(u32 region, u8 language) { - static const std::array<std::vector<u8>, 7> region_languages{{ - // JPN - {LANGUAGE_JP}, - // USA - {LANGUAGE_EN, LANGUAGE_FR, LANGUAGE_ES, LANGUAGE_PT}, - // EUR - {LANGUAGE_EN, LANGUAGE_FR, LANGUAGE_DE, LANGUAGE_IT, LANGUAGE_ES, LANGUAGE_NL, LANGUAGE_PT, - LANGUAGE_RU}, - // AUS - {LANGUAGE_EN, LANGUAGE_FR, LANGUAGE_DE, LANGUAGE_IT, LANGUAGE_ES, LANGUAGE_NL, LANGUAGE_PT, - LANGUAGE_RU}, - // CHN - {LANGUAGE_ZH}, - // KOR - {LANGUAGE_KO}, - // TWN - {LANGUAGE_TW}, - }}; - const auto& available = region_languages[region]; - if (std::find(available.begin(), available.end(), language) == available.end()) { - return available[0]; - } - return language; -} - ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, void* output) { void* pointer; CASCADE_RESULT(pointer, GetConfigInfoBlockPointer(block_id, size, flag)); memcpy(output, pointer, size); - // override the language setting if the region setting is auto - if (block_id == LanguageBlockID && - Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT) { - u8 language; - memcpy(&language, output, sizeof(u8)); - language = AdjustLanguageInfoBlock(preferred_region_code, language); - memcpy(output, &language, sizeof(u8)); - } - return RESULT_SUCCESS; } @@ -586,9 +567,47 @@ void Init() { void Shutdown() {} +/// Checks if the language is available in the chosen region, and returns a proper one +static SystemLanguage AdjustLanguageInfoBlock(u32 region, SystemLanguage language) { + static const std::array<std::vector<SystemLanguage>, 7> region_languages{{ + // JPN + {LANGUAGE_JP}, + // USA + {LANGUAGE_EN, LANGUAGE_FR, LANGUAGE_ES, LANGUAGE_PT}, + // EUR + {LANGUAGE_EN, LANGUAGE_FR, LANGUAGE_DE, LANGUAGE_IT, LANGUAGE_ES, LANGUAGE_NL, LANGUAGE_PT, + LANGUAGE_RU}, + // AUS + {LANGUAGE_EN, LANGUAGE_FR, LANGUAGE_DE, LANGUAGE_IT, LANGUAGE_ES, LANGUAGE_NL, LANGUAGE_PT, + LANGUAGE_RU}, + // CHN + {LANGUAGE_ZH}, + // KOR + {LANGUAGE_KO}, + // TWN + {LANGUAGE_TW}, + }}; + const auto& available = region_languages[region]; + if (std::find(available.begin(), available.end(), language) == available.end()) { + return available[0]; + } + return language; +} + void SetPreferredRegionCode(u32 region_code) { preferred_region_code = region_code; LOG_INFO(Service_CFG, "Preferred region code set to %u", preferred_region_code); + + if (Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT) { + const SystemLanguage current_language = GetSystemLanguage(); + const SystemLanguage adjusted_language = + AdjustLanguageInfoBlock(region_code, current_language); + if (current_language != adjusted_language) { + LOG_WARNING(Service_CFG, "System language %d does not fit the region. Adjusted to %d", + static_cast<int>(current_language), static_cast<int>(adjusted_language)); + SetSystemLanguage(adjusted_language); + } + } } void SetUsername(const std::u16string& name) { diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp index cd0a1a598..9da55f328 100644 --- a/src/core/hle/service/err_f.cpp +++ b/src/core/hle/service/err_f.cpp @@ -227,6 +227,8 @@ static void ThrowFatalError(Interface* self) { LOG_CRITICAL(Service_ERR, "FINST2: 0x%08X", errtype.exception_data.exception_info.fpinst2); break; + case ExceptionType::Undefined: + break; // Not logging exception_info for this case } LOG_CRITICAL(Service_ERR, "Datetime: %s", GetCurrentSystemTime().c_str()); break; diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index 519c1f3a9..2ea956e0b 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -26,7 +26,7 @@ namespace FS { /// Supported archive types enum class ArchiveIdCode : u32 { - RomFS = 0x00000003, + SelfNCCH = 0x00000003, SaveData = 0x00000004, ExtSaveData = 0x00000006, SharedExtSaveData = 0x00000007, diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 337da1387..33b290699 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -54,15 +54,17 @@ static void Initialize(Service::Interface* self) { * 3 : File handle */ static void OpenFile(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + // The helper should be passed by argument to the function + IPC::RequestParser rp(Kernel::GetCommandBuffer(), {0x080201C2}); + rp.Pop<u32>(); // Always 0 ? - ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); - auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); - u32 filename_size = cmd_buff[5]; + ArchiveHandle archive_handle = rp.Pop<u64>(); + auto filename_type = static_cast<FileSys::LowPathType>(rp.Pop<u32>()); + u32 filename_size = rp.Pop<u32>(); FileSys::Mode mode; - mode.hex = cmd_buff[6]; - u32 attributes = cmd_buff[7]; // TODO(Link Mauve): do something with those attributes. - u32 filename_ptr = cmd_buff[9]; + mode.hex = rp.Pop<u32>(); + u32 attributes = rp.Pop<u32>(); // TODO(Link Mauve): do something with those attributes. + VAddr filename_ptr = rp.PopStaticBuffer(); FileSys::Path file_path(filename_type, filename_size, filename_ptr); LOG_DEBUG(Service_FS, "path=%s, mode=%u attrs=%u", file_path.DebugStr().c_str(), mode.hex, @@ -70,16 +72,17 @@ static void OpenFile(Service::Interface* self) { ResultVal<std::shared_ptr<File>> file_res = OpenFileFromArchive(archive_handle, file_path, mode); - cmd_buff[1] = file_res.Code().raw; + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(file_res.Code()); if (file_res.Succeeded()) { std::shared_ptr<File> file = *file_res; auto sessions = ServerSession::CreateSessionPair(file->GetName(), file); file->ClientConnected(std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions)); - cmd_buff[3] = Kernel::g_handle_table - .Create(std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions)) - .MoveFrom(); + rb.PushMoveHandles(Kernel::g_handle_table + .Create(std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions)) + .MoveFrom()); } else { - cmd_buff[3] = 0; + rb.PushMoveHandles(0); LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); } } diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index a8c1331ed..a960778a7 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -4,6 +4,7 @@ #include "common/bit_field.h" #include "common/microprofile.h" +#include "core/core.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/result.h" @@ -118,10 +119,10 @@ static ResultCode WriteHWRegs(u32 base_address, u32 size_in_bytes, VAddr data_va * Updates sequential GSP GPU hardware registers using parallel arrays of source data and masks. * For each register, the value is updated only where the mask is high * - * @param base_address The address of the first register in the sequence + * @param base_address The address of the first register in the sequence * @param size_in_bytes The number of registers to update (size of data) - * @param data A pointer to the source data to use for updates - * @param masks A pointer to the masks + * @param data_vaddr A virtual address to the source data to use for updates + * @param masks_vaddr A virtual address to the masks * @return RESULT_SUCCESS if the parameters are valid, error code otherwise */ static ResultCode WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, VAddr data_vaddr, @@ -280,6 +281,7 @@ ResultCode SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { if (screen_id == 0) { MicroProfileFlip(); + Core::System::GetInstance().perf_stats.EndGameFrame(); } return RESULT_SUCCESS; @@ -705,6 +707,33 @@ static void ReleaseRight(Interface* self) { LOG_WARNING(Service_GSP, "called"); } +/** + * GSP_GPU::StoreDataCache service function + * + * This Function is a no-op, We aren't emulating the CPU cache any time soon. + * + * Inputs: + * 0 : Header code [0x001F0082] + * 1 : Address + * 2 : Size + * 3 : Value 0, some descriptor for the KProcess Handle + * 4 : KProcess handle + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void StoreDataCache(Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 address = cmd_buff[1]; + u32 size = cmd_buff[2]; + u32 process = cmd_buff[4]; + + cmd_buff[0] = IPC::MakeHeader(0x1F, 0x1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_DEBUG(Service_GSP, "(STUBBED) called address=0x%08X, size=0x%08X, process=0x%08X", address, + size, process); +} + const Interface::FunctionInfo FunctionTable[] = { {0x00010082, WriteHWRegs, "WriteHWRegs"}, {0x00020084, WriteHWRegsWithMask, "WriteHWRegsWithMask"}, @@ -736,7 +765,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x001C0040, nullptr, "SetLedForceOff"}, {0x001D0040, nullptr, "SetTestCommand"}, {0x001E0080, nullptr, "SetInternalPriorities"}, - {0x001F0082, nullptr, "StoreDataCache"}, + {0x001F0082, StoreDataCache, "StoreDataCache"}, }; GSP_GPU::GSP_GPU() { diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index f14ab3811..b19e831fe 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -2,10 +2,14 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> +#include <atomic> #include <cmath> +#include <memory> #include "common/logging/log.h" #include "core/core_timing.h" #include "core/frontend/emu_window.h" +#include "core/frontend/input.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/hid/hid.h" @@ -32,8 +36,8 @@ static u32 next_touch_index; static u32 next_accelerometer_index; static u32 next_gyroscope_index; -static int enable_accelerometer_count = 0; // positive means enabled -static int enable_gyroscope_count = 0; // positive means enabled +static int enable_accelerometer_count; // positive means enabled +static int enable_gyroscope_count; // positive means enabled static int pad_update_event; static int accelerometer_update_event; @@ -44,6 +48,11 @@ constexpr u64 pad_update_ticks = BASE_CLOCK_RATE_ARM11 / 234; constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104; constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101; +static std::atomic<bool> is_device_reload_pending; +static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> + buttons; +static std::unique_ptr<Input::AnalogDevice> circle_pad; + static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) { // 30 degree and 60 degree are angular thresholds for directions constexpr float TAN30 = 0.577350269f; @@ -74,14 +83,48 @@ static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) { return state; } +static void LoadInputDevices() { + std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, + Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END, + buttons.begin(), Input::CreateDevice<Input::ButtonDevice>); + circle_pad = Input::CreateDevice<Input::AnalogDevice>( + Settings::values.analogs[Settings::NativeAnalog::CirclePad]); +} + +static void UnloadInputDevices() { + for (auto& button : buttons) { + button.reset(); + } + circle_pad.reset(); +} + static void UpdatePadCallback(u64 userdata, int cycles_late) { SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer()); - PadState state = VideoCore::g_emu_window->GetPadState(); + if (is_device_reload_pending.exchange(false)) + LoadInputDevices(); + + PadState state; + using namespace Settings::NativeButton; + state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus()); + state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus()); + state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus()); + state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus()); + state.right.Assign(buttons[Right - BUTTON_HID_BEGIN]->GetStatus()); + state.left.Assign(buttons[Left - BUTTON_HID_BEGIN]->GetStatus()); + state.up.Assign(buttons[Up - BUTTON_HID_BEGIN]->GetStatus()); + state.down.Assign(buttons[Down - BUTTON_HID_BEGIN]->GetStatus()); + state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus()); + state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus()); + state.start.Assign(buttons[Start - BUTTON_HID_BEGIN]->GetStatus()); + state.select.Assign(buttons[Select - BUTTON_HID_BEGIN]->GetStatus()); // Get current circle pad position and update circle pad direction - s16 circle_pad_x, circle_pad_y; - std::tie(circle_pad_x, circle_pad_y) = VideoCore::g_emu_window->GetCirclePadState(); + float circle_pad_x_f, circle_pad_y_f; + std::tie(circle_pad_x_f, circle_pad_y_f) = circle_pad->GetStatus(); + constexpr int MAX_CIRCLEPAD_POS = 0x9C; // Max value for a circle pad position + s16 circle_pad_x = static_cast<s16>(circle_pad_x_f * MAX_CIRCLEPAD_POS); + s16 circle_pad_y = static_cast<s16>(circle_pad_y_f * MAX_CIRCLEPAD_POS); state.hex |= GetCirclePadDirectionState(circle_pad_x, circle_pad_y).hex; mem->pad.current_state.hex = state.hex; @@ -313,6 +356,8 @@ void Init() { AddService(new HID_U_Interface); AddService(new HID_SPVR_Interface); + is_device_reload_pending.store(true); + using Kernel::MemoryPermission; shared_mem = SharedMemory::Create(nullptr, 0x1000, MemoryPermission::ReadWrite, MemoryPermission::Read, @@ -323,6 +368,9 @@ void Init() { next_accelerometer_index = 0; next_gyroscope_index = 0; + enable_accelerometer_count = 0; + enable_gyroscope_count = 0; + // Create event handles event_pad_or_touch_1 = Event::Create(ResetType::OneShot, "HID:EventPadOrTouch1"); event_pad_or_touch_2 = Event::Create(ResetType::OneShot, "HID:EventPadOrTouch2"); @@ -347,6 +395,11 @@ void Shutdown() { event_accelerometer = nullptr; event_gyroscope = nullptr; event_debug_pad = nullptr; + UnloadInputDevices(); +} + +void ReloadInputDevices() { + is_device_reload_pending.store(true); } } // namespace HID diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 21e66dfe0..b505cdcd5 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -39,15 +39,6 @@ struct PadState { BitField<10, 1, u32> x; BitField<11, 1, u32> y; - BitField<14, 1, u32> zl; - BitField<15, 1, u32> zr; - - BitField<20, 1, u32> touch; - - BitField<24, 1, u32> c_right; - BitField<25, 1, u32> c_left; - BitField<26, 1, u32> c_up; - BitField<27, 1, u32> c_down; BitField<28, 1, u32> circle_right; BitField<29, 1, u32> circle_left; BitField<30, 1, u32> circle_up; @@ -185,35 +176,6 @@ ASSERT_REG_POSITION(touch.index_reset_ticks, 0x2A); #undef ASSERT_REG_POSITION #endif // !defined(_MSC_VER) -// Pre-defined PadStates for single button presses -const PadState PAD_NONE = {{0}}; -const PadState PAD_A = {{1u << 0}}; -const PadState PAD_B = {{1u << 1}}; -const PadState PAD_SELECT = {{1u << 2}}; -const PadState PAD_START = {{1u << 3}}; -const PadState PAD_RIGHT = {{1u << 4}}; -const PadState PAD_LEFT = {{1u << 5}}; -const PadState PAD_UP = {{1u << 6}}; -const PadState PAD_DOWN = {{1u << 7}}; -const PadState PAD_R = {{1u << 8}}; -const PadState PAD_L = {{1u << 9}}; -const PadState PAD_X = {{1u << 10}}; -const PadState PAD_Y = {{1u << 11}}; - -const PadState PAD_ZL = {{1u << 14}}; -const PadState PAD_ZR = {{1u << 15}}; - -const PadState PAD_TOUCH = {{1u << 20}}; - -const PadState PAD_C_RIGHT = {{1u << 24}}; -const PadState PAD_C_LEFT = {{1u << 25}}; -const PadState PAD_C_UP = {{1u << 26}}; -const PadState PAD_C_DOWN = {{1u << 27}}; -const PadState PAD_CIRCLE_RIGHT = {{1u << 28}}; -const PadState PAD_CIRCLE_LEFT = {{1u << 29}}; -const PadState PAD_CIRCLE_UP = {{1u << 30}}; -const PadState PAD_CIRCLE_DOWN = {{1u << 31}}; - /** * HID::GetIPCHandles service function * Inputs: @@ -301,5 +263,8 @@ void Init(); /// Shutdown HID service void Shutdown(); + +/// Reload input devices. Used when input configuration changed +void ReloadInputDevices(); } } diff --git a/src/core/hle/service/ir/ir.cpp b/src/core/hle/service/ir/ir.cpp index 7f1731a50..7ac34a990 100644 --- a/src/core/hle/service/ir/ir.cpp +++ b/src/core/hle/service/ir/ir.cpp @@ -2,9 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/kernel/event.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/shared_memory.h" #include "core/hle/service/ir/ir.h" #include "core/hle/service/ir/ir_rst.h" #include "core/hle/service/ir/ir_u.h" @@ -14,101 +11,18 @@ namespace Service { namespace IR { -static Kernel::SharedPtr<Kernel::Event> handle_event; -static Kernel::SharedPtr<Kernel::Event> conn_status_event; -static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory; -static Kernel::SharedPtr<Kernel::SharedMemory> transfer_shared_memory; - -void GetHandles(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = 0x4000000; - cmd_buff[3] = Kernel::g_handle_table.Create(Service::IR::shared_memory).MoveFrom(); - cmd_buff[4] = Kernel::g_handle_table.Create(Service::IR::handle_event).MoveFrom(); -} - -void InitializeIrNopShared(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - u32 transfer_buff_size = cmd_buff[1]; - u32 recv_buff_size = cmd_buff[2]; - u32 unk1 = cmd_buff[3]; - u32 send_buff_size = cmd_buff[4]; - u32 unk2 = cmd_buff[5]; - u8 baud_rate = cmd_buff[6] & 0xFF; - Kernel::Handle handle = cmd_buff[8]; - - if (Kernel::g_handle_table.IsValid(handle)) { - transfer_shared_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(handle); - transfer_shared_memory->name = "IR:TransferSharedMemory"; - } - - cmd_buff[1] = RESULT_SUCCESS.raw; - - LOG_WARNING(Service_IR, "(STUBBED) called, transfer_buff_size=%d, recv_buff_size=%d, " - "unk1=%d, send_buff_size=%d, unk2=%d, baud_rate=%u, handle=0x%08X", - transfer_buff_size, recv_buff_size, unk1, send_buff_size, unk2, baud_rate, handle); -} - -void RequireConnection(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - conn_status_event->Signal(); - - cmd_buff[1] = RESULT_SUCCESS.raw; - - LOG_WARNING(Service_IR, "(STUBBED) called"); -} - -void Disconnect(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - cmd_buff[1] = RESULT_SUCCESS.raw; - - LOG_WARNING(Service_IR, "(STUBBED) called"); -} - -void GetConnectionStatusEvent(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[3] = Kernel::g_handle_table.Create(Service::IR::conn_status_event).MoveFrom(); - - LOG_WARNING(Service_IR, "(STUBBED) called"); -} - -void FinalizeIrNop(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - cmd_buff[1] = RESULT_SUCCESS.raw; - - LOG_WARNING(Service_IR, "(STUBBED) called"); -} - void Init() { - using namespace Kernel; - AddService(new IR_RST_Interface); AddService(new IR_U_Interface); AddService(new IR_User_Interface); - using Kernel::MemoryPermission; - shared_memory = SharedMemory::Create(nullptr, 0x1000, Kernel::MemoryPermission::ReadWrite, - Kernel::MemoryPermission::ReadWrite, 0, - Kernel::MemoryRegion::BASE, "IR:SharedMemory"); - transfer_shared_memory = nullptr; - - // Create event handle(s) - handle_event = Event::Create(ResetType::OneShot, "IR:HandleEvent"); - conn_status_event = Event::Create(ResetType::OneShot, "IR:ConnectionStatusEvent"); + InitUser(); + InitRST(); } void Shutdown() { - transfer_shared_memory = nullptr; - shared_memory = nullptr; - handle_event = nullptr; - conn_status_event = nullptr; + ShutdownUser(); + ShutdownRST(); } } // namespace IR diff --git a/src/core/hle/service/ir/ir.h b/src/core/hle/service/ir/ir.h index 72d44ce60..c741498e2 100644 --- a/src/core/hle/service/ir/ir.h +++ b/src/core/hle/service/ir/ir.h @@ -10,63 +10,6 @@ class Interface; namespace IR { -/** - * IR::GetHandles service function - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Translate header, used by the ARM11-kernel - * 3 : Shared memory handle - * 4 : Event handle - */ -void GetHandles(Interface* self); - -/** - * IR::InitializeIrNopShared service function - * Inputs: - * 1 : Size of transfer buffer - * 2 : Recv buffer size - * 3 : unknown - * 4 : Send buffer size - * 5 : unknown - * 6 : BaudRate (u8) - * 7 : 0 - * 8 : Handle of transfer shared memory - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -void InitializeIrNopShared(Interface* self); - -/** - * IR::FinalizeIrNop service function - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -void FinalizeIrNop(Interface* self); - -/** - * IR::GetConnectionStatusEvent service function - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Connection Status Event handle - */ -void GetConnectionStatusEvent(Interface* self); - -/** - * IR::Disconnect service function - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -void Disconnect(Interface* self); - -/** - * IR::RequireConnection service function - * Inputs: - * 1 : unknown (u8), looks like always 1 - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -void RequireConnection(Interface* self); - /// Initialize IR service void Init(); diff --git a/src/core/hle/service/ir/ir_rst.cpp b/src/core/hle/service/ir/ir_rst.cpp index 1f10ebd3d..3f1275c53 100644 --- a/src/core/hle/service/ir/ir_rst.cpp +++ b/src/core/hle/service/ir/ir_rst.cpp @@ -2,12 +2,34 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/hle/kernel/event.h" +#include "core/hle/kernel/shared_memory.h" #include "core/hle/service/ir/ir.h" #include "core/hle/service/ir/ir_rst.h" namespace Service { namespace IR { +static Kernel::SharedPtr<Kernel::Event> handle_event; +static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory; + +/** + * IR::GetHandles service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Translate header, used by the ARM11-kernel + * 3 : Shared memory handle + * 4 : Event handle + */ +static void GetHandles(Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 0x4000000; + cmd_buff[3] = Kernel::g_handle_table.Create(Service::IR::shared_memory).MoveFrom(); + cmd_buff[4] = Kernel::g_handle_table.Create(Service::IR::handle_event).MoveFrom(); +} + const Interface::FunctionInfo FunctionTable[] = { {0x00010000, GetHandles, "GetHandles"}, {0x00020080, nullptr, "Initialize"}, @@ -19,5 +41,20 @@ IR_RST_Interface::IR_RST_Interface() { Register(FunctionTable); } +void InitRST() { + using namespace Kernel; + + shared_memory = + SharedMemory::Create(nullptr, 0x1000, MemoryPermission::ReadWrite, + MemoryPermission::ReadWrite, 0, MemoryRegion::BASE, "IR:SharedMemory"); + + handle_event = Event::Create(ResetType::OneShot, "IR:HandleEvent"); +} + +void ShutdownRST() { + shared_memory = nullptr; + handle_event = nullptr; +} + } // namespace IR } // namespace Service diff --git a/src/core/hle/service/ir/ir_rst.h b/src/core/hle/service/ir/ir_rst.h index a492e15c9..75b732627 100644 --- a/src/core/hle/service/ir/ir_rst.h +++ b/src/core/hle/service/ir/ir_rst.h @@ -18,5 +18,8 @@ public: } }; +void InitRST(); +void ShutdownRST(); + } // namespace IR } // namespace Service diff --git a/src/core/hle/service/ir/ir_u.cpp b/src/core/hle/service/ir/ir_u.cpp index 429615f31..ce00d5732 100644 --- a/src/core/hle/service/ir/ir_u.cpp +++ b/src/core/hle/service/ir/ir_u.cpp @@ -27,7 +27,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00100000, nullptr, "GetErrorStatus"}, {0x00110040, nullptr, "SetSleepModeActive"}, {0x00120040, nullptr, "SetSleepModeState"}, - // clang-format off + // clang-format on }; IR_U_Interface::IR_U_Interface() { diff --git a/src/core/hle/service/ir/ir_user.cpp b/src/core/hle/service/ir/ir_user.cpp index 6cff1d544..b326d7fc7 100644 --- a/src/core/hle/service/ir/ir_user.cpp +++ b/src/core/hle/service/ir/ir_user.cpp @@ -2,12 +2,112 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/hle/kernel/event.h" +#include "core/hle/kernel/shared_memory.h" #include "core/hle/service/ir/ir.h" #include "core/hle/service/ir/ir_user.h" namespace Service { namespace IR { +static Kernel::SharedPtr<Kernel::Event> conn_status_event; +static Kernel::SharedPtr<Kernel::SharedMemory> transfer_shared_memory; + +/** + * IR::InitializeIrNopShared service function + * Inputs: + * 1 : Size of transfer buffer + * 2 : Recv buffer size + * 3 : unknown + * 4 : Send buffer size + * 5 : unknown + * 6 : BaudRate (u8) + * 7 : 0 + * 8 : Handle of transfer shared memory + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void InitializeIrNopShared(Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 transfer_buff_size = cmd_buff[1]; + u32 recv_buff_size = cmd_buff[2]; + u32 unk1 = cmd_buff[3]; + u32 send_buff_size = cmd_buff[4]; + u32 unk2 = cmd_buff[5]; + u8 baud_rate = cmd_buff[6] & 0xFF; + Kernel::Handle handle = cmd_buff[8]; + + if (Kernel::g_handle_table.IsValid(handle)) { + transfer_shared_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(handle); + transfer_shared_memory->name = "IR:TransferSharedMemory"; + } + + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_IR, "(STUBBED) called, transfer_buff_size=%d, recv_buff_size=%d, " + "unk1=%d, send_buff_size=%d, unk2=%d, baud_rate=%u, handle=0x%08X", + transfer_buff_size, recv_buff_size, unk1, send_buff_size, unk2, baud_rate, handle); +} + +/** + * IR::RequireConnection service function + * Inputs: + * 1 : unknown (u8), looks like always 1 + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void RequireConnection(Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + conn_status_event->Signal(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_IR, "(STUBBED) called"); +} + +/** + * IR::Disconnect service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void Disconnect(Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_IR, "(STUBBED) called"); +} + +/** + * IR::GetConnectionStatusEvent service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Connection Status Event handle + */ +static void GetConnectionStatusEvent(Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[3] = Kernel::g_handle_table.Create(Service::IR::conn_status_event).MoveFrom(); + + LOG_WARNING(Service_IR, "(STUBBED) called"); +} + +/** + * IR::FinalizeIrNop service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void FinalizeIrNop(Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_IR, "(STUBBED) called"); +} + const Interface::FunctionInfo FunctionTable[] = { {0x00010182, nullptr, "InitializeIrNop"}, {0x00020000, FinalizeIrNop, "FinalizeIrNop"}, @@ -41,5 +141,17 @@ IR_User_Interface::IR_User_Interface() { Register(FunctionTable); } +void InitUser() { + using namespace Kernel; + + transfer_shared_memory = nullptr; + conn_status_event = Event::Create(ResetType::OneShot, "IR:ConnectionStatusEvent"); +} + +void ShutdownUser() { + transfer_shared_memory = nullptr; + conn_status_event = nullptr; +} + } // namespace IR } // namespace Service diff --git a/src/core/hle/service/ir/ir_user.h b/src/core/hle/service/ir/ir_user.h index 71c932ffa..3849bd923 100644 --- a/src/core/hle/service/ir/ir_user.h +++ b/src/core/hle/service/ir/ir_user.h @@ -18,5 +18,8 @@ public: } }; +void InitUser(); +void ShutdownUser(); + } // namespace IR } // namespace Service diff --git a/src/core/hle/service/ldr_ro/cro_helper.h b/src/core/hle/service/ldr_ro/cro_helper.h index 060d5a55f..3bc10dbdc 100644 --- a/src/core/hle/service/ldr_ro/cro_helper.h +++ b/src/core/hle/service/ldr_ro/cro_helper.h @@ -57,7 +57,7 @@ public: * @param is_crs true if the module itself is the static module * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. */ - ResultCode Rebase(VAddr crs_address, u32 cro_size, VAddr data_segment_addresss, + ResultCode Rebase(VAddr crs_address, u32 cro_size, VAddr data_segment_address, u32 data_segment_size, VAddr bss_segment_address, u32 bss_segment_size, bool is_crs); @@ -102,7 +102,7 @@ public: /** * Registers this module and adds it to the module list. * @param crs_address the virtual address of the static module - * @auto_link whether to register as an auto link module + * @param auto_link whether to register as an auto link module */ void Register(VAddr crs_address, bool auto_link); diff --git a/src/core/hle/service/ldr_ro/ldr_ro.cpp b/src/core/hle/service/ldr_ro/ldr_ro.cpp index 8d00a7577..7af76676b 100644 --- a/src/core/hle/service/ldr_ro/ldr_ro.cpp +++ b/src/core/hle/service/ldr_ro/ldr_ro.cpp @@ -6,6 +6,7 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "core/arm/arm_interface.h" +#include "core/core.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/vm_manager.h" #include "core/hle/service/ldr_ro/cro_helper.h" diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index 0be94322c..63c334cb2 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp @@ -19,7 +19,7 @@ void CheckSysUpdateAvailable(Service::Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = 0; // No update available - LOG_WARNING(Service_NWM, "(STUBBED) called"); + LOG_WARNING(Service_NIM, "(STUBBED) called"); } void Init() { diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index a7ba7688f..e6a5f1417 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -10,6 +10,7 @@ #include <boost/container/flat_map.hpp> #include "common/common_types.h" #include "core/hle/ipc.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/thread.h" #include "core/hle/result.h" diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index 31bb466fc..907d9c8fa 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp @@ -281,37 +281,39 @@ static void GetTransferEndEvent(Interface* self) { } static void SetSendingY(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - conversion.src_Y.address = cmd_buff[1]; - conversion.src_Y.image_size = cmd_buff[2]; - conversion.src_Y.transfer_unit = cmd_buff[3]; - conversion.src_Y.gap = cmd_buff[4]; + // The helper should be passed by argument to the function + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x00100102); + conversion.src_Y.address = rp.Pop<u32>(); + conversion.src_Y.image_size = rp.Pop<u32>(); + conversion.src_Y.transfer_unit = rp.Pop<u32>(); + conversion.src_Y.gap = rp.Pop<u32>(); + Kernel::Handle src_process_handle = rp.PopHandle(); - cmd_buff[0] = IPC::MakeHeader(0x10, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " "src_process_handle=0x%08X", conversion.src_Y.image_size, conversion.src_Y.transfer_unit, conversion.src_Y.gap, - cmd_buff[6]); + src_process_handle); } static void SetSendingU(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + // The helper should be passed by argument to the function + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x00110102); + conversion.src_U.address = rp.Pop<u32>(); + conversion.src_U.image_size = rp.Pop<u32>(); + conversion.src_U.transfer_unit = rp.Pop<u32>(); + conversion.src_U.gap = rp.Pop<u32>(); + Kernel::Handle src_process_handle = rp.PopHandle(); - conversion.src_U.address = cmd_buff[1]; - conversion.src_U.image_size = cmd_buff[2]; - conversion.src_U.transfer_unit = cmd_buff[3]; - conversion.src_U.gap = cmd_buff[4]; - - cmd_buff[0] = IPC::MakeHeader(0x11, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " "src_process_handle=0x%08X", conversion.src_U.image_size, conversion.src_U.transfer_unit, conversion.src_U.gap, - cmd_buff[6]); + src_process_handle); } static void SetSendingV(Interface* self) { @@ -561,11 +563,10 @@ static void GetAlpha(Interface* self) { } static void SetDitheringWeightParams(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - std::memcpy(&dithering_weight_params, &cmd_buff[1], sizeof(DitheringWeightParams)); - - cmd_buff[0] = IPC::MakeHeader(0x24, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x24, 8, 0); // 0x240200 + rp.PopRaw(dithering_weight_params); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); LOG_DEBUG(Service_Y2R, "called"); } diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 96db39ad9..4e0c3fb8b 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -556,11 +556,21 @@ static ResultCode CreateThread(Kernel::Handle* out_handle, s32 priority, u32 ent break; } - if (processor_id == THREADPROCESSORID_1 || processor_id == THREADPROCESSORID_ALL || - (processor_id == THREADPROCESSORID_DEFAULT && - Kernel::g_current_process->ideal_processor == THREADPROCESSORID_1)) { - LOG_WARNING(Kernel_SVC, - "Newly created thread is allowed to be run in the SysCore, unimplemented."); + if (processor_id == THREADPROCESSORID_ALL) { + LOG_INFO(Kernel_SVC, + "Newly created thread is allowed to be run in any Core, unimplemented."); + } + + if (processor_id == THREADPROCESSORID_DEFAULT && + Kernel::g_current_process->ideal_processor == THREADPROCESSORID_1) { + LOG_WARNING( + Kernel_SVC, + "Newly created thread is allowed to be run in the SysCore (Core1), unimplemented."); + } + + if (processor_id == THREADPROCESSORID_1) { + LOG_ERROR(Kernel_SVC, + "Newly created thread must run in the SysCore (Core1), unimplemented."); } CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create(name, entry_point, priority, @@ -837,6 +847,11 @@ static ResultCode SetTimer(Kernel::Handle handle, s64 initial, s64 interval) { LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle); + if (initial < 0 || interval < 0) { + return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Kernel, + ErrorSummary::InvalidArgument, ErrorLevel::Permanent); + } + SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle); if (timer == nullptr) return ERR_INVALID_HANDLE; |