summaryrefslogtreecommitdiffstats
path: root/src/core/hle/service
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/service')
-rw-r--r--src/core/hle/service/apt/apt.cpp103
-rw-r--r--src/core/hle/service/apt/apt.h40
-rw-r--r--src/core/hle/service/apt/apt_a.cpp4
-rw-r--r--src/core/hle/service/apt/apt_s.cpp4
-rw-r--r--src/core/hle/service/apt/apt_u.cpp4
-rw-r--r--src/core/hle/service/cam/cam.cpp1024
-rw-r--r--src/core/hle/service/cam/cam.h358
-rw-r--r--src/core/hle/service/cam/cam_u.cpp32
-rw-r--r--src/core/hle/service/cfg/cfg.cpp105
-rw-r--r--src/core/hle/service/err_f.cpp2
-rw-r--r--src/core/hle/service/fs/archive.h2
-rw-r--r--src/core/hle/service/fs/fs_user.cpp27
-rw-r--r--src/core/hle/service/gsp_gpu.cpp37
-rw-r--r--src/core/hle/service/hid/hid.cpp63
-rw-r--r--src/core/hle/service/hid/hid.h41
-rw-r--r--src/core/hle/service/ir/ir.cpp94
-rw-r--r--src/core/hle/service/ir/ir.h57
-rw-r--r--src/core/hle/service/ir/ir_rst.cpp37
-rw-r--r--src/core/hle/service/ir/ir_rst.h3
-rw-r--r--src/core/hle/service/ir/ir_u.cpp2
-rw-r--r--src/core/hle/service/ir/ir_user.cpp112
-rw-r--r--src/core/hle/service/ir/ir_user.h3
-rw-r--r--src/core/hle/service/ldr_ro/cro_helper.h4
-rw-r--r--src/core/hle/service/ldr_ro/ldr_ro.cpp1
-rw-r--r--src/core/hle/service/nim/nim.cpp2
-rw-r--r--src/core/hle/service/service.h1
-rw-r--r--src/core/hle/service/y2r_u.cpp47
27 files changed, 1754 insertions, 455 deletions
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");
}